blob: cddf19e954bb3ded101311817bbf1a798bcedd65 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.telephony.ims.cts;
import static junit.framework.TestCase.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.PersistableBundle;
import android.telephony.AccessNetworkConstants;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.ims.ImsException;
import android.telephony.ims.ImsManager;
import android.telephony.ims.ImsMmTelManager;
import android.telephony.ims.feature.MmTelFeature;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.ShellIdentityUtils;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
public class ImsMmTelManagerTest {
// Copied from CarrierConfigManager, since these keys is inappropriately marked as @hide
private static final String KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL =
"carrier_volte_override_wfc_provisioning_bool";
private static final String KEY_EDITABLE_WFC_MODE_BOOL = "editable_wfc_mode_bool";
private static final String KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL =
"use_wfc_home_network_mode_in_roaming_network_bool";
private static final String KEY_EDITABLE_WFC_ROAMING_MODE_BOOL =
"editable_wfc_roaming_mode_bool";
private static int sTestSub = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private static Handler sHandler;
private static CarrierConfigReceiver sReceiver;
private static class CarrierConfigReceiver extends BroadcastReceiver {
private CountDownLatch mLatch = new CountDownLatch(1);
private final int mSubId;
CarrierConfigReceiver(int subId) {
mSubId = subId;
}
@Override
public void onReceive(Context context, Intent intent) {
if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
int subId = intent.getIntExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, -1);
if (mSubId == subId) {
mLatch.countDown();
}
}
}
void clearQueue() {
mLatch = new CountDownLatch(1);
}
void waitForCarrierConfigChanged() throws Exception {
mLatch.await(5000, TimeUnit.MILLISECONDS);
}
}
@BeforeClass
public static void beforeAllTests() {
assumeTrue(ImsUtils.shouldTestImsService());
sTestSub = ImsUtils.getPreferredActiveSubId();
if (Looper.getMainLooper() == null) {
Looper.prepareMainLooper();
}
sHandler = new Handler(Looper.getMainLooper());
sReceiver = new CarrierConfigReceiver(sTestSub);
IntentFilter filter = new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
// ACTION_CARRIER_CONFIG_CHANGED is sticky, so we will get a callback right away.
getContext().registerReceiver(sReceiver, filter);
}
@AfterClass
public static void afterAllTests() {
if (sReceiver != null) {
getContext().unregisterReceiver(sReceiver);
sReceiver = null;
}
}
@Before
public void beforeTest() {
if (!SubscriptionManager.isValidSubscriptionId(sTestSub)) {
fail("This test requires that there is a SIM in the device!");
}
}
@Test
public void testGetVoWiFiSetting_noPermission() {
try {
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
ImsMmTelManager mMmTelManager = imsManager.getImsMmTelManager(sTestSub);
boolean isEnabled = mMmTelManager.isVoWiFiSettingEnabled();
fail("Expected SecurityException for missing permissions");
} catch (SecurityException ex) {
/* Expected */
}
}
/**
* Given the advanced calling setting is editable and not hidden
* (see {@link CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL}, and
* {@link CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL}), set the advanced
* calling setting and ensure the correct calling setting is returned. Also ensure the
* ContentObserver is triggered properly.
*/
@Test
public void testAdvancedCallingSetting() throws Exception {
// Ensure advanced calling setting is editable.
PersistableBundle bundle = new PersistableBundle();
bundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL, true);
bundle.putBoolean(CarrierConfigManager.KEY_HIDE_ENHANCED_4G_LTE_BOOL, false);
overrideCarrierConfig(bundle);
// Register Observer
Uri callingUri = Uri.withAppendedPath(
SubscriptionManager.ADVANCED_CALLING_ENABLED_CONTENT_URI, "" + sTestSub);
CountDownLatch contentObservedLatch = new CountDownLatch(1);
ContentObserver observer = createObserver(callingUri, contentObservedLatch);
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
ImsMmTelManager mMmTelManager = imsManager.getImsMmTelManager(sTestSub);
boolean isEnabled = ShellIdentityUtils.invokeMethodWithShellPermissions(mMmTelManager,
ImsMmTelManager::isAdvancedCallingSettingEnabled);
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
(m) -> m.setAdvancedCallingSettingEnabled(!isEnabled));
waitForLatch(contentObservedLatch, observer);
boolean isEnabledResult = ShellIdentityUtils.invokeMethodWithShellPermissions(mMmTelManager,
ImsMmTelManager::isAdvancedCallingSettingEnabled);
assertEquals("isAdvancedCallingSettingEnabled does not reflect the new value set by "
+ "setAdvancedCallingSettingEnabled", !isEnabled, isEnabledResult);
// Set back to default
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
(m) -> m.setAdvancedCallingSettingEnabled(isEnabled));
// restore original carrier config.
overrideCarrierConfig(null);
}
/**
* Set the VT setting and ensure it is queried successfully. Also ensure the ContentObserver
* is triggered properly.
*/
@Test
public void testVtSetting() throws Exception {
// Register Observer
Uri callingUri = Uri.withAppendedPath(
SubscriptionManager.VT_ENABLED_CONTENT_URI, "" + sTestSub);
CountDownLatch contentObservedLatch = new CountDownLatch(1);
ContentObserver observer = createObserver(callingUri, contentObservedLatch);
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
ImsMmTelManager mMmTelManager = imsManager.getImsMmTelManager(sTestSub);
boolean isEnabled = ShellIdentityUtils.invokeMethodWithShellPermissions(mMmTelManager,
ImsMmTelManager::isVtSettingEnabled);
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
(m) -> m.setVtSettingEnabled(!isEnabled));
waitForLatch(contentObservedLatch, observer);
boolean isEnabledResult = ShellIdentityUtils.invokeMethodWithShellPermissions(mMmTelManager,
ImsMmTelManager::isVtSettingEnabled);
assertEquals("isVtSettingEnabled does not match the value set by setVtSettingEnabled",
!isEnabled, isEnabledResult);
// Set back to default
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
(m) -> m.setVtSettingEnabled(isEnabled));
}
/**
* Set the VoWiFi setting and ensure it is queried successfully. Also ensure the ContentObserver
* is triggered properly.
*/
@Test
public void testVoWiFiSetting() throws Exception {
PersistableBundle bundle = new PersistableBundle();
// Do not worry about provisioning for this test
bundle.putBoolean(KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL, false);
bundle.putBoolean(CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false);
overrideCarrierConfig(bundle);
// Register Observer
Uri callingUri = Uri.withAppendedPath(
SubscriptionManager.WFC_ENABLED_CONTENT_URI, "" + sTestSub);
CountDownLatch contentObservedLatch = new CountDownLatch(1);
ContentObserver observer = createObserver(callingUri, contentObservedLatch);
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
ImsMmTelManager mMmTelManager = imsManager.getImsMmTelManager(sTestSub);
boolean isEnabled = ShellIdentityUtils.invokeMethodWithShellPermissions(mMmTelManager,
ImsMmTelManager::isVoWiFiSettingEnabled);
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
(m) -> m.setVoWiFiSettingEnabled(!isEnabled));
waitForLatch(contentObservedLatch, observer);
boolean isEnabledResult = ShellIdentityUtils.invokeMethodWithShellPermissions(mMmTelManager,
ImsMmTelManager::isVoWiFiSettingEnabled);
assertEquals("isVoWiFiSettingEnabled did not match value set by setVoWiFiSettingEnabled",
!isEnabled, isEnabledResult);
// Set back to default
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
(m) -> m.setVoWiFiSettingEnabled(isEnabled));
overrideCarrierConfig(null);
}
/**
* Set the VoWiFi roaming setting and ensure it is queried successfully. Also ensure the
* ContentObserver is triggered properly.
*/
@Test
public void testVoWiFiRoamingSetting() throws Exception {
Uri callingUri = Uri.withAppendedPath(
SubscriptionManager.WFC_ROAMING_ENABLED_CONTENT_URI, "" + sTestSub);
CountDownLatch contentObservedLatch = new CountDownLatch(1);
ContentObserver observer = createObserver(callingUri, contentObservedLatch);
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
ImsMmTelManager mMmTelManager = imsManager.getImsMmTelManager(sTestSub);
boolean isEnabled = ShellIdentityUtils.invokeMethodWithShellPermissions(mMmTelManager,
ImsMmTelManager::isVoWiFiRoamingSettingEnabled);
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
(m) -> m.setVoWiFiRoamingSettingEnabled(!isEnabled));
waitForLatch(contentObservedLatch, observer);
boolean isEnabledResult = ShellIdentityUtils.invokeMethodWithShellPermissions(mMmTelManager,
ImsMmTelManager::isVoWiFiRoamingSettingEnabled);
assertEquals("isVoWiFiRoamingSettingEnabled result does not match the value set by "
+ "setVoWiFiRoamingSettingEnabled", !isEnabled, isEnabledResult);
// Set back to default
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
(m) -> m.setVoWiFiRoamingSettingEnabled(isEnabled));
}
/**
* Expect to fail when Set the VoWiFi Mode setting withour proper permission
*/
@Test
public void testGetVoWiFiModeSetting_noPermission() throws Exception {
try {
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
ImsMmTelManager mMmTelManager = imsManager.getImsMmTelManager(sTestSub);
int oldMode = mMmTelManager.getVoWiFiModeSetting();
fail("Expected SecurityException for missing permissoins");
} catch (SecurityException ex) {
/* Expected */
}
}
/**
* Expect to fail when Set the VoWiFi Mode setting withour proper permission
*/
@Test
public void testGetVoWiFiRoamingModeSetting_noPermission() throws Exception {
try {
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
ImsMmTelManager mMmTelManager = imsManager.getImsMmTelManager(sTestSub);
int oldMode = mMmTelManager.getVoWiFiRoamingModeSetting();
fail("Expected SecurityException for missing permissoins");
} catch (SecurityException ex) {
/* Expected */
}
}
/**
* Set the VoWiFi Mode setting and ensure the ContentResolver is triggered as well.
*/
@Test
public void testVoWiFiModeSetting() throws Exception {
PersistableBundle bundle = new PersistableBundle();
bundle.putBoolean(KEY_EDITABLE_WFC_MODE_BOOL, true);
overrideCarrierConfig(bundle);
// Register Observer
Uri callingUri = Uri.withAppendedPath(
SubscriptionManager.WFC_MODE_CONTENT_URI, "" + sTestSub);
CountDownLatch contentObservedLatch = new CountDownLatch(1);
ContentObserver observer = createObserver(callingUri, contentObservedLatch);
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
ImsMmTelManager mMmTelManager = imsManager.getImsMmTelManager(sTestSub);
int oldMode = ShellIdentityUtils.invokeMethodWithShellPermissions(mMmTelManager,
ImsMmTelManager::getVoWiFiModeSetting);
// Keep the mode in the bounds 0-2
int newMode = (oldMode + 1) % 3;
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
(m) -> m.setVoWiFiModeSetting(newMode));
waitForLatch(contentObservedLatch, observer);
int newModeResult = ShellIdentityUtils.invokeMethodWithShellPermissions(mMmTelManager,
ImsMmTelManager::getVoWiFiModeSetting);
assertEquals(newMode, newModeResult);
// Set back to default
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
(m) -> m.setVoWiFiModeSetting(oldMode));
overrideCarrierConfig(null);
}
/**
* Set the VoWiFi Mode setting and ensure the ContentResolver is triggered as well.
*/
@Test
public void testVoWiFiRoamingModeSetting() throws Exception {
PersistableBundle bundle = new PersistableBundle();
// Ensure the WFC roaming mode will be changed properly
bundle.putBoolean(KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL, false);
bundle.putBoolean(KEY_EDITABLE_WFC_ROAMING_MODE_BOOL, true);
overrideCarrierConfig(bundle);
// Register Observer
Uri callingUri = Uri.withAppendedPath(
SubscriptionManager.WFC_ROAMING_MODE_CONTENT_URI, "" + sTestSub);
CountDownLatch contentObservedLatch = new CountDownLatch(1);
ContentObserver observer = createObserver(callingUri, contentObservedLatch);
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
ImsMmTelManager mMmTelManager = imsManager.getImsMmTelManager(sTestSub);
int oldMode = ShellIdentityUtils.invokeMethodWithShellPermissions(mMmTelManager,
ImsMmTelManager::getVoWiFiRoamingModeSetting);
// Keep the mode in the bounds 0-2
int newMode = (oldMode + 1) % 3;
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
(m) -> m.setVoWiFiRoamingModeSetting(newMode));
waitForLatch(contentObservedLatch, observer);
int newModeResult = ShellIdentityUtils.invokeMethodWithShellPermissions(mMmTelManager,
ImsMmTelManager::getVoWiFiRoamingModeSetting);
assertEquals("getVoWiFiRoamingModeSetting was not set to value set by"
+ "setVoWiFiRoamingModeSetting", newMode, newModeResult);
// Set back to default
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
(m) -> m.setVoWiFiRoamingModeSetting(oldMode));
overrideCarrierConfig(null);
}
/**
* Test Permissions on various APIs.
*/
@Test
public void testMethodPermissions() throws Exception {
ImsManager imsManager = getContext().getSystemService(ImsManager.class);
ImsMmTelManager mMmTelManager = imsManager.getImsMmTelManager(sTestSub);
// setRttCapabilitySetting
try {
mMmTelManager.setRttCapabilitySetting(false);
fail("setRttCapabilitySetting requires MODIFY_PHONE_STATE permission.");
} catch (SecurityException e) {
//expected
}
try {
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
(m) -> m.setRttCapabilitySetting(false),
"android.permission.MODIFY_PHONE_STATE");
} catch (SecurityException e) {
fail("setRttCapabilitySetting requires MODIFY_PHONE_STATE permission.");
}
// setVoWiFiNonPersistent
try {
mMmTelManager.setVoWiFiNonPersistent(true,
ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED);
fail("setVoWiFiNonPersistent requires MODIFY_PHONE_STATE permission.");
} catch (SecurityException e) {
//expected
}
try {
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
(m) -> m.setVoWiFiNonPersistent(true,
ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED),
"android.permission.MODIFY_PHONE_STATE");
} catch (SecurityException e) {
fail("setVoWiFiNonPersistent requires MODIFY_PHONE_STATE permission.");
}
try {
mMmTelManager.isVtSettingEnabled();
fail("isVtSettingEnabled requires READ_PRECISE_PHONE_STATE permission.");
} catch (SecurityException e) {
//expected
}
try {
mMmTelManager.isAdvancedCallingSettingEnabled();
fail("isAdvancedCallingSettingEnabled requires READ_PRECISE_PHONE_STATE.");
} catch (SecurityException e) {
//expected
}
try {
mMmTelManager.isVoWiFiRoamingSettingEnabled();
fail("isVoWiFiRoamingSettingEnabled requires READ_PRECISE_PHONE_STATE permission.");
} catch (SecurityException e) {
//expected
}
try {
mMmTelManager.isVoWiFiSettingEnabled();
fail("isVoWiFiSettingEnabled requires READ_PRECISE_PHONE_STATE permission.");
} catch (SecurityException e) {
//expected
}
try {
mMmTelManager.isTtyOverVolteEnabled();
fail("isTtyOverVolteEnabled requires READ_PRIVILEGED_PHONE_STATE permission.");
} catch (SecurityException e) {
//expected
}
try {
mMmTelManager.isSupported(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
AccessNetworkConstants.TRANSPORT_TYPE_WWAN, Runnable::run, (result) -> { });
fail("isSupported requires READ_PRIVILEGED_PHONE_STATE permission.");
} catch (SecurityException e) {
//expected
}
try {
mMmTelManager.getRegistrationState(Runnable::run, (result) -> { });
fail("getRegistrationState requires READ_PRECISE_PHONE_STATE permission.");
} catch (SecurityException e) {
//expected
}
try {
mMmTelManager.getRegistrationTransportType(Runnable::run, (result) -> { });
fail("getRegistrationTransportType requires READ_PRIVILEGED_PHONE_STATE permission.");
} catch (SecurityException e) {
//expected
}
try {
mMmTelManager.isSupported(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
AccessNetworkConstants.TRANSPORT_TYPE_WWAN, Runnable::run, (result) -> { });
fail("isSupported requires READ_PRIVILEGED_PHONE_STATE permission.");
} catch (SecurityException e) {
//expected
}
try {
ShellIdentityUtils.invokeMethodWithShellPermissions(mMmTelManager,
ImsMmTelManager::isTtyOverVolteEnabled,
"android.permission.READ_PRIVILEGED_PHONE_STATE");
} catch (SecurityException e) {
fail("isTtyOverVolteEnabled requires READ_PRIVILEGED_PHONE_STATE permission.");
}
try {
LinkedBlockingQueue<Boolean> resultQueue = new LinkedBlockingQueue<>(1);
ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(mMmTelManager,
(m) -> m.isSupported(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
// Run on the binder thread.
Runnable::run,
resultQueue::offer), ImsException.class,
"android.permission.READ_PRIVILEGED_PHONE_STATE");
assertNotNull(resultQueue.poll(ImsUtils.TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS));
} catch (SecurityException e) {
fail("isSupported requires READ_PRIVILEGED_PHONE_STATE permission.");
}
try {
LinkedBlockingQueue<Integer> resultQueue = new LinkedBlockingQueue<>(1);
ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(mMmTelManager,
(m) -> m.getRegistrationState(Runnable::run, resultQueue::offer),
ImsException.class, "android.permission.READ_PRIVILEGED_PHONE_STATE");
assertNotNull(resultQueue.poll(ImsUtils.TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS));
} catch (SecurityException e) {
fail("getRegistrationState requires READ_PRIVILEGED_PHONE_STATE permission.");
}
try {
LinkedBlockingQueue<Integer> resultQueue = new LinkedBlockingQueue<>(1);
ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(mMmTelManager,
(m) -> m.getRegistrationTransportType(Runnable::run, resultQueue::offer),
ImsException.class, "android.permission.READ_PRIVILEGED_PHONE_STATE");
assertNotNull(resultQueue.poll(ImsUtils.TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS));
} catch (SecurityException e) {
fail("getRegistrationTransportType requires READ_PRIVILEGED_PHONE_STATE permission.");
}
}
private void overrideCarrierConfig(PersistableBundle bundle) throws Exception {
CarrierConfigManager carrierConfigManager = getContext().getSystemService(
CarrierConfigManager.class);
sReceiver.clearQueue();
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(carrierConfigManager,
(m) -> m.overrideConfig(sTestSub, bundle));
sReceiver.waitForCarrierConfigChanged();
}
private ContentObserver createObserver(Uri observerUri, CountDownLatch latch) {
ContentObserver observer = new ContentObserver(sHandler) {
@Override
public void onChange(boolean selfChange, Uri uri) {
if (observerUri.equals(uri)) {
latch.countDown();
}
}
};
getContext().getContentResolver().registerContentObserver(observerUri, true, observer);
return observer;
}
private void waitForLatch(CountDownLatch latch, ContentObserver observer) {
try {
// Wait for the ContentObserver to fire signalling the change.
latch.await(ImsUtils.TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
fail("Interrupted Exception waiting for latch countdown:" + e.getMessage());
} finally {
getContext().getContentResolver().unregisterContentObserver(observer);
}
}
private static Context getContext() {
return InstrumentationRegistry.getInstrumentation().getContext();
}
}