blob: 5576227f6f7f60e5e1a98b58098545bb9ba96902 [file] [log] [blame]
/*
* Copyright (C) 2017 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 com.android.internal.telephony.ims;
import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.telephony.CarrierConfigManager;
import android.telephony.ims.feature.ImsFeature;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Pair;
import com.android.ims.internal.IImsServiceController;
import com.android.internal.telephony.PhoneConstants;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* Unit tests for ImsResolver
*/
@RunWith(AndroidJUnit4.class)
public class ImsResolverTest extends ImsTestBase {
private static final int TEST_TIMEOUT = 200; //ms
private static final ComponentName TEST_DEVICE_DEFAULT_NAME = new ComponentName("TestDevicePkg",
"DeviceImsService");
private static final ComponentName TEST_CARRIER_DEFAULT_NAME = new ComponentName(
"TestCarrierPkg", "CarrierImsService");
private static final ComponentName TEST_CARRIER_2_DEFAULT_NAME = new ComponentName(
"TestCarrier2Pkg", "Carrier2ImsService");
@Mock Context mMockContext;
@Mock PackageManager mMockPM;
@Mock ImsResolver.SubscriptionManagerProxy mTestSubscriptionManagerProxy;
@Mock CarrierConfigManager mMockCarrierConfigManager;
private ImsResolver mTestImsResolver;
private BroadcastReceiver mTestPackageBroadcastReceiver;
private BroadcastReceiver mTestCarrierConfigReceiver;
private PersistableBundle[] mCarrierConfigs;
@Before
@Override
public void setUp() throws Exception {
super.setUp();
}
@After
@Override
public void tearDown() throws Exception {
mTestImsResolver = null;
super.tearDown();
}
/**
* Add a package to the package manager and make sure it is added to the cache of available
* ImsServices in the ImsResolver
*/
@Test
@SmallTest
public void testAddPackageToCache() {
setupResolver(1/*numSlots*/);
List<ResolveInfo> info = new ArrayList<>();
Set<String> features = new HashSet<>();
features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
features.add(ImsResolver.METADATA_MMTEL_FEATURE);
features.add(ImsResolver.METADATA_RCS_FEATURE);
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, features, true));
when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
mTestImsResolver.populateCacheAndStartBind();
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
ImsResolver.ImsServiceInfo testCachedService =
mTestImsResolver.getImsServiceInfoFromCache(
TEST_DEVICE_DEFAULT_NAME.getPackageName());
assertNotNull(testCachedService);
assertTrue(isImsServiceInfoEqual(TEST_DEVICE_DEFAULT_NAME, features, testCachedService));
}
/**
* Set the carrier config override value and ensure that ImsResolver calls .bind on that
* package name with the correct ImsFeatures.
*/
@Test
@SmallTest
public void testCarrierPackageBind() throws RemoteException {
setupResolver(1/*numSlots*/);
// Set CarrierConfig default package name and make it available to the package manager
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
List<ResolveInfo> info = new ArrayList<>();
Set<String> features = new HashSet<>();
features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
features.add(ImsResolver.METADATA_MMTEL_FEATURE);
features.add(ImsResolver.METADATA_RCS_FEATURE);
info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, features, true));
when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
ImsServiceController controller = mock(ImsServiceController.class);
mTestImsResolver.setImsServiceControllerFactory((context, componentName) -> {
when(controller.getComponentName()).thenReturn(componentName);
return controller;
});
mTestImsResolver.populateCacheAndStartBind();
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
verify(controller).bind(convertToHashSet(features, 0));
verify(controller, never()).unbind();
assertEquals(TEST_CARRIER_DEFAULT_NAME, controller.getComponentName());
}
/**
* Ensure that no ImsService is bound if there is no carrier or device package explictly set.
*/
@Test
@SmallTest
public void testDontBindWhenNullCarrierPackage() throws RemoteException {
setupResolver(1/*numSlots*/);
List<ResolveInfo> info = new ArrayList<>();
Set<String> features = new HashSet<>();
features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
features.add(ImsResolver.METADATA_MMTEL_FEATURE);
features.add(ImsResolver.METADATA_RCS_FEATURE);
info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, features, true));
when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
ImsServiceController controller = mock(ImsServiceController.class);
mTestImsResolver.setImsServiceControllerFactory((context, componentName) -> {
when(controller.getComponentName()).thenReturn(componentName);
return controller;
});
// Set the CarrierConfig string to null so that ImsResolver will not bind to the available
// Services
setConfigCarrierString(0, null);
mTestImsResolver.populateCacheAndStartBind();
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
verify(controller, never()).bind(any());
verify(controller, never()).unbind();
}
/**
* Test that the ImsService corresponding to the default device ImsService package name is
* bound.
*/
@Test
@SmallTest
public void testDevicePackageBind() throws RemoteException {
setupResolver(1/*numSlots*/);
List<ResolveInfo> info = new ArrayList<>();
Set<String> features = new HashSet<>();
features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
features.add(ImsResolver.METADATA_MMTEL_FEATURE);
features.add(ImsResolver.METADATA_RCS_FEATURE);
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, features, true));
info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, features, true));
when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
ImsServiceController controller = mock(ImsServiceController.class);
mTestImsResolver.setImsServiceControllerFactory((context, componentName) -> {
when(controller.getComponentName()).thenReturn(componentName);
return controller;
});
mTestImsResolver.populateCacheAndStartBind();
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
// There is no carrier override set, so make sure that the ImsServiceController binds
// to all SIMs.
HashSet<Pair<Integer, Integer>> featureSet = convertToHashSet(features, 0);
verify(controller).bind(featureSet);
verify(controller, never()).unbind();
assertEquals(TEST_DEVICE_DEFAULT_NAME, controller.getComponentName());
}
/**
* Test that when a device and carrier override package are set, both ImsServices are bound.
* Verify that the carrier ImsService features are created and the device default features
* are created for all features that are not covered by the carrier ImsService.
*/
@Test
@SmallTest
public void testDeviceAndCarrierPackageBind() throws RemoteException {
setupResolver(1/*numSlots*/);
List<ResolveInfo> info = new ArrayList<>();
Set<String> deviceFeatures = new HashSet<>();
deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
// Set the carrier override package for slot 0
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
Set<String> carrierFeatures = new HashSet<>();
// Carrier service doesn't support the emergency voice feature.
carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
ImsServiceController deviceController = mock(ImsServiceController.class);
ImsServiceController carrierController = mock(ImsServiceController.class);
setImsServiceControllerFactory(deviceController, carrierController);
mTestImsResolver.populateCacheAndStartBind();
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
// Verify that all features that have been defined for the carrier override are bound
HashSet<Pair<Integer, Integer>> carrierFeatureSet = convertToHashSet(carrierFeatures, 0);
carrierFeatureSet.addAll(convertToHashSet(carrierFeatures, 0));
verify(carrierController).bind(carrierFeatureSet);
verify(carrierController, never()).unbind();
assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName());
// Verify that all features that are not defined in the carrier override are bound in the
// device controller (including emergency voice for slot 0)
HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 0);
deviceFeatureSet.removeAll(carrierFeatureSet);
verify(deviceController).bind(deviceFeatureSet);
verify(deviceController, never()).unbind();
assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
}
/**
* Verify that the ImsServiceController is available for the feature specified
* (carrier for VOICE/RCS and device for emergency).
*/
@Test
@SmallTest
public void testGetDeviceCarrierFeatures() throws RemoteException {
setupResolver(2/*numSlots*/);
ImsServiceController deviceController = mock(ImsServiceController.class);
IImsServiceController iDeviceController = mock(IImsServiceController.class);
when(deviceController.getImsServiceController()).thenReturn(iDeviceController);
ImsServiceController carrierController = mock(ImsServiceController.class);
IImsServiceController iCarrierController = mock(IImsServiceController.class);
when(carrierController.getImsServiceController()).thenReturn(iCarrierController);
mTestImsResolver.populateCacheAndStartBind();
// Callback from mock ImsServiceControllers
// All features on slot 1 should be the device default
mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.EMERGENCY_MMTEL, deviceController);
mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.MMTEL, deviceController);
mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.RCS, deviceController);
// The carrier override does not support emergency voice
mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.EMERGENCY_MMTEL, deviceController);
// The carrier override contains these features
mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.MMTEL, carrierController);
mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.RCS, carrierController);
// Get the IImsServiceControllers for each feature on each slot and verify they are correct.
assertEquals(iDeviceController, mTestImsResolver.getImsServiceControllerAndListen(
1/*Slot id*/, ImsFeature.EMERGENCY_MMTEL, null));
assertEquals(iDeviceController, mTestImsResolver.getImsServiceControllerAndListen(
1 /*Slot id*/, ImsFeature.MMTEL, null));
assertEquals(iDeviceController, mTestImsResolver.getImsServiceControllerAndListen(
1 /*Slot id*/, ImsFeature.RCS, null));
assertEquals(iDeviceController, mTestImsResolver.getImsServiceControllerAndListen(
1 /*Slot id*/, ImsFeature.EMERGENCY_MMTEL, null));
assertEquals(iCarrierController, mTestImsResolver.getImsServiceControllerAndListen(
0 /*Slot id*/, ImsFeature.MMTEL, null));
assertEquals(iCarrierController, mTestImsResolver.getImsServiceControllerAndListen(
0 /*Slot id*/, ImsFeature.RCS, null));
}
/**
* Bind to device ImsService and change the feature set. Verify that changeImsServiceFeature
* is called with the new feature set.
*/
@Test
@SmallTest
public void testAddDeviceFeatureNoCarrier() throws RemoteException {
setupResolver(2/*numSlots*/);
List<ResolveInfo> info = new ArrayList<>();
Set<String> features = new HashSet<>();
features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
features.add(ImsResolver.METADATA_MMTEL_FEATURE);
// Doesn't include RCS feature by default
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, features, true));
when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
ImsServiceController controller = mock(ImsServiceController.class);
mTestImsResolver.setImsServiceControllerFactory((context, componentName) -> {
when(controller.getComponentName()).thenReturn(componentName);
return controller;
});
// Bind using default features
mTestImsResolver.populateCacheAndStartBind();
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
HashSet<Pair<Integer, Integer>> featureSet = convertToHashSet(features, 0);
featureSet.addAll(convertToHashSet(features, 1));
verify(controller).bind(featureSet);
// add RCS to features list
Set<String> newFeatures = new HashSet<>(features);
newFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
info.clear();
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, newFeatures, true));
// Tell the package manager that a new device feature is installed
Intent addPackageIntent = new Intent();
addPackageIntent.setAction(Intent.ACTION_PACKAGE_ADDED);
addPackageIntent.setData(new Uri.Builder().scheme("package")
.opaquePart(TEST_DEVICE_DEFAULT_NAME.getPackageName()).build());
mTestPackageBroadcastReceiver.onReceive(null, addPackageIntent);
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
//Verify new feature is added to the device default.
HashSet<Pair<Integer, Integer>> newFeatureSet = convertToHashSet(newFeatures, 0);
newFeatureSet.addAll(convertToHashSet(newFeatures, 1));
verify(controller).changeImsServiceFeatures(newFeatureSet);
}
/**
* Bind to device ImsService and change the feature set. Verify that changeImsServiceFeature
* is called with the new feature set on the sub that doesn't include the carrier override.
*/
@Test
@SmallTest
public void testAddDeviceFeatureWithCarrier() throws RemoteException {
setupResolver(2/*numSlots*/);
List<ResolveInfo> info = new ArrayList<>();
Set<String> deviceFeatures = new HashSet<>();
deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
// Set the carrier override package for slot 0
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
Set<String> carrierFeatures = new HashSet<>();
// Carrier service doesn't support the emergency voice feature.
carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
ImsServiceController deviceController = mock(ImsServiceController.class);
ImsServiceController carrierController = mock(ImsServiceController.class);
setImsServiceControllerFactory(deviceController, carrierController);
mTestImsResolver.populateCacheAndStartBind();
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
// Verify that all features that have been defined for the carrier override are bound
HashSet<Pair<Integer, Integer>> carrierFeatureSet = convertToHashSet(carrierFeatures, 0);
carrierFeatureSet.addAll(convertToHashSet(carrierFeatures, 0));
verify(carrierController).bind(carrierFeatureSet);
verify(carrierController, never()).unbind();
assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName());
// Verify that all features that are not defined in the carrier override are bound in the
// device controller (including emergency voice for slot 0)
HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
deviceFeatures.removeAll(carrierFeatures);
deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
verify(deviceController).bind(deviceFeatureSet);
verify(deviceController, never()).unbind();
assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
// add RCS to features list
Set<String> newDeviceFeatures = new HashSet<>();
newDeviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
newDeviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
newDeviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
info.clear();
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, newDeviceFeatures, true));
// Tell the package manager that a new device feature is installed
Intent addPackageIntent = new Intent();
addPackageIntent.setAction(Intent.ACTION_PACKAGE_ADDED);
addPackageIntent.setData(new Uri.Builder().scheme("package")
.opaquePart(TEST_DEVICE_DEFAULT_NAME.getPackageName()).build());
mTestPackageBroadcastReceiver.onReceive(null, addPackageIntent);
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
//Verify new feature is added to the device default.
// add all features for slot 1
HashSet<Pair<Integer, Integer>> newDeviceFeatureSet =
convertToHashSet(newDeviceFeatures, 1);
// remove carrier overrides for slot 0
newDeviceFeatures.removeAll(carrierFeatures);
newDeviceFeatureSet.addAll(convertToHashSet(newDeviceFeatures, 0));
verify(deviceController).changeImsServiceFeatures(newDeviceFeatureSet);
verify(carrierController, never()).changeImsServiceFeatures(any());
}
/**
* Bind to device ImsService and change the feature set of the carrier overridden ImsService.
* Verify that the device and carrier ImsServices are changed.
*/
@Test
@SmallTest
public void testAddCarrierFeature() throws RemoteException {
setupResolver(2/*numSlots*/);
List<ResolveInfo> info = new ArrayList<>();
Set<String> deviceFeatures = new HashSet<>();
deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
// Set the carrier override package for slot 0
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
Set<String> carrierFeatures = new HashSet<>();
// Carrier service doesn't support the emergency voice feature.
carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
ImsServiceController deviceController = mock(ImsServiceController.class);
ImsServiceController carrierController = mock(ImsServiceController.class);
setImsServiceControllerFactory(deviceController, carrierController);
mTestImsResolver.populateCacheAndStartBind();
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
// Verify that all features that have been defined for the carrier override are bound
HashSet<Pair<Integer, Integer>> carrierFeatureSet = convertToHashSet(carrierFeatures, 0);
carrierFeatureSet.addAll(convertToHashSet(carrierFeatures, 0));
verify(carrierController).bind(carrierFeatureSet);
verify(carrierController, never()).unbind();
assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName());
// Verify that all features that are not defined in the carrier override are bound in the
// device controller (including emergency voice for slot 0)
HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
deviceFeatures.removeAll(carrierFeatures);
deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
verify(deviceController).bind(deviceFeatureSet);
verify(deviceController, never()).unbind();
assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
// add RCS to carrier features list
Set<String> newCarrierFeatures = new HashSet<>();
newCarrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
newCarrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
info.clear();
info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, newCarrierFeatures, true));
// Tell the package manager that a new device feature is installed
Intent addPackageIntent = new Intent();
addPackageIntent.setAction(Intent.ACTION_PACKAGE_ADDED);
addPackageIntent.setData(new Uri.Builder().scheme("package")
.opaquePart(TEST_CARRIER_DEFAULT_NAME.getPackageName()).build());
mTestPackageBroadcastReceiver.onReceive(null, addPackageIntent);
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
//Verify new feature is added to the carrier override.
// add all features for slot 0
HashSet<Pair<Integer, Integer>> newCarrierFeatureSet =
convertToHashSet(newCarrierFeatures, 0);
verify(carrierController).changeImsServiceFeatures(newCarrierFeatureSet);
deviceFeatureSet.removeAll(newCarrierFeatureSet);
verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
}
/**
* Bind to device ImsService and change the feature set of the carrier overridden ImsService by
* removing a feature.
* Verify that the device and carrier ImsServices are changed.
*/
@Test
@SmallTest
public void testRemoveCarrierFeature() throws RemoteException {
setupResolver(2/*numSlots*/);
List<ResolveInfo> info = new ArrayList<>();
Set<String> deviceFeatures = new HashSet<>();
deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
// Set the carrier override package for slot 0
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
Set<String> carrierFeatures = new HashSet<>();
// Carrier service doesn't support the emergency voice feature.
carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
ImsServiceController deviceController = mock(ImsServiceController.class);
ImsServiceController carrierController = mock(ImsServiceController.class);
setImsServiceControllerFactory(deviceController, carrierController);
mTestImsResolver.populateCacheAndStartBind();
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
// Verify that all features that have been defined for the carrier override are bound
HashSet<Pair<Integer, Integer>> carrierFeatureSet = convertToHashSet(carrierFeatures, 0);
carrierFeatureSet.addAll(convertToHashSet(carrierFeatures, 0));
verify(carrierController).bind(carrierFeatureSet);
verify(carrierController, never()).unbind();
assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName());
// Verify that all features that are not defined in the carrier override are bound in the
// device controller (including emergency voice for slot 0)
HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
deviceFeatures.removeAll(carrierFeatures);
deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
verify(deviceController).bind(deviceFeatureSet);
verify(deviceController, never()).unbind();
assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
// remove RCS from carrier features list
Set<String> newCarrierFeatures = new HashSet<>();
newCarrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
info.clear();
info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, newCarrierFeatures, true));
// Tell the package manager that a new device feature is installed
Intent addPackageIntent = new Intent();
addPackageIntent.setAction(Intent.ACTION_PACKAGE_ADDED);
addPackageIntent.setData(new Uri.Builder().scheme("package")
.opaquePart(TEST_CARRIER_DEFAULT_NAME.getPackageName()).build());
mTestPackageBroadcastReceiver.onReceive(null, addPackageIntent);
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
//Verify new feature is added to the carrier override.
// add all features for slot 0
HashSet<Pair<Integer, Integer>> newCarrierFeatureSet =
convertToHashSet(newCarrierFeatures, 0);
verify(carrierController).changeImsServiceFeatures(newCarrierFeatureSet);
Set<String> newDeviceFeatures = new HashSet<>();
newDeviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
newDeviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
newDeviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
HashSet<Pair<Integer, Integer>> newDeviceFeatureSet = convertToHashSet(newDeviceFeatures,
1);
newDeviceFeatures.removeAll(newCarrierFeatures);
newDeviceFeatureSet.addAll(convertToHashSet(newDeviceFeatures, 0));
verify(deviceController).changeImsServiceFeatures(newDeviceFeatureSet);
}
/**
* Inform the ImsResolver that a Carrier ImsService has been installed and must be bound.
*/
@Test
@SmallTest
public void testInstallCarrierImsService() throws RemoteException {
setupResolver(2/*numSlots*/);
List<ResolveInfo> info = new ArrayList<>();
Set<String> deviceFeatures = new HashSet<>();
deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
// Set the carrier override package for slot 0
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
ImsServiceController deviceController = mock(ImsServiceController.class);
ImsServiceController carrierController = mock(ImsServiceController.class);
setImsServiceControllerFactory(deviceController, carrierController);
mTestImsResolver.populateCacheAndStartBind();
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
Set<String> carrierFeatures = new HashSet<>();
// Carrier service doesn't support the emergency voice feature.
carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
// Tell the package manager that a new carrier app is installed
Intent addPackageIntent = new Intent();
addPackageIntent.setAction(Intent.ACTION_PACKAGE_ADDED);
addPackageIntent.setData(new Uri.Builder().scheme("package")
.opaquePart(TEST_CARRIER_DEFAULT_NAME.getPackageName()).build());
mTestPackageBroadcastReceiver.onReceive(null, addPackageIntent);
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
// Verify that all features that have been defined for the carrier override are bound
HashSet<Pair<Integer, Integer>> carrierFeatureSet = convertToHashSet(carrierFeatures, 0);
carrierFeatureSet.addAll(convertToHashSet(carrierFeatures, 0));
verify(carrierController).bind(carrierFeatureSet);
// device features change
HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
deviceFeatureSet.removeAll(carrierFeatureSet);
verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
}
/**
* Inform the ImsResolver that a carrier ImsService has been uninstalled and the device default
* must now use those features.
*/
@Test
@SmallTest
public void testUninstallCarrierImsService() throws RemoteException {
setupResolver(2/*numSlots*/);
List<ResolveInfo> info = new ArrayList<>();
Set<String> deviceFeatures = new HashSet<>();
deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
// Set the carrier override package for slot 0
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
Set<String> carrierFeatures = new HashSet<>();
// Carrier service doesn't support the emergency voice feature.
carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
ImsServiceController deviceController = mock(ImsServiceController.class);
ImsServiceController carrierController = mock(ImsServiceController.class);
setImsServiceControllerFactory(deviceController, carrierController);
mTestImsResolver.populateCacheAndStartBind();
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
// Tell the package manager that carrier app is uninstalled
Intent removePackageIntent = new Intent();
removePackageIntent.setAction(Intent.ACTION_PACKAGE_REMOVED);
removePackageIntent.setData(new Uri.Builder().scheme("package")
.opaquePart(TEST_CARRIER_DEFAULT_NAME.getPackageName()).build());
info.clear();
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
mTestPackageBroadcastReceiver.onReceive(null, removePackageIntent);
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
// Verify that the carrier controller is unbound
verify(carrierController).unbind();
assertNull(mTestImsResolver.getImsServiceInfoFromCache(
TEST_CARRIER_DEFAULT_NAME.getPackageName()));
// device features change to include all supported functionality
HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
}
/**
* Inform ImsResolver that the carrier config has changed to none, requiring the device
* ImsService to be bound/set up and the previous carrier ImsService to be unbound.
*/
@Test
@SmallTest
public void testCarrierConfigChangedToNone() throws RemoteException {
setupResolver(2/*numSlots*/);
List<ResolveInfo> info = new ArrayList<>();
Set<String> deviceFeatures = new HashSet<>();
deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
// Set the carrier override package for slot 0
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
Set<String> carrierFeatures = new HashSet<>();
// Carrier service doesn't support the emergency voice feature.
carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
ImsServiceController deviceController = mock(ImsServiceController.class);
ImsServiceController carrierController = mock(ImsServiceController.class);
setImsServiceControllerFactory(deviceController, carrierController);
mTestImsResolver.populateCacheAndStartBind();
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
setConfigCarrierString(0, null);
Intent carrierConfigIntent = new Intent();
carrierConfigIntent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, 0);
mTestCarrierConfigReceiver.onReceive(null, carrierConfigIntent);
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
// Verify that the carrier controller is unbound
verify(carrierController).unbind();
assertNotNull(mTestImsResolver.getImsServiceInfoFromCache(
TEST_CARRIER_DEFAULT_NAME.getPackageName()));
// device features change
HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
}
/**
* Inform ImsResolver that the carrier config has changed to another, requiring the new carrier
* ImsService to be bound/set up and the previous carrier ImsService to be unbound.
*/
@Test
@SmallTest
public void testCarrierConfigChangedToAnotherService() throws RemoteException {
setupResolver(2/*numSlots*/);
List<ResolveInfo> info = new ArrayList<>();
Set<String> deviceFeatures = new HashSet<>();
deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
// Set the carrier override package for slot 0
setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
Set<String> carrierFeatures1 = new HashSet<>();
// Carrier service doesn't support the emergency voice feature.
carrierFeatures1.add(ImsResolver.METADATA_MMTEL_FEATURE);
carrierFeatures1.add(ImsResolver.METADATA_RCS_FEATURE);
Set<String> carrierFeatures2 = new HashSet<>();
// Carrier service doesn't support the emergency voice feature.
carrierFeatures2.add(ImsResolver.METADATA_RCS_FEATURE);
info.add(getResolveInfo(TEST_CARRIER_2_DEFAULT_NAME, carrierFeatures2, true));
info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures1, true));
// Use device default package, which will load the ImsService that the device provides
info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
ImsServiceController deviceController = mock(ImsServiceController.class);
ImsServiceController carrierController1 = mock(ImsServiceController.class);
ImsServiceController carrierController2 = mock(ImsServiceController.class);
setImsServiceControllerFactory(deviceController, carrierController1, carrierController2);
mTestImsResolver.populateCacheAndStartBind();
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
setConfigCarrierString(0, TEST_CARRIER_2_DEFAULT_NAME.getPackageName());
Intent carrierConfigIntent = new Intent();
carrierConfigIntent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, 0);
mTestCarrierConfigReceiver.onReceive(null, carrierConfigIntent);
waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
// Verify that carrier 1 is unbound
verify(carrierController1).unbind();
assertNotNull(mTestImsResolver.getImsServiceInfoFromCache(
TEST_CARRIER_DEFAULT_NAME.getPackageName()));
// Verify that carrier 2 is bound
HashSet<Pair<Integer, Integer>> carrier2FeatureSet = convertToHashSet(carrierFeatures2, 0);
verify(carrierController2).bind(carrier2FeatureSet);
assertNotNull(mTestImsResolver.getImsServiceInfoFromCache(
TEST_CARRIER_DEFAULT_NAME.getPackageName()));
// device features change to accommodate for the features carrier 2 lacks
HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
deviceFeatures.removeAll(carrierFeatures2);
deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
}
private void setupResolver(int numSlots) {
when(mMockContext.getSystemService(eq(Context.CARRIER_CONFIG_SERVICE))).thenReturn(
mMockCarrierConfigManager);
when(mMockContext.getPackageManager()).thenReturn(mMockPM);
mCarrierConfigs = new PersistableBundle[numSlots];
for (int i = 0; i < numSlots; i++) {
mCarrierConfigs[i] = new PersistableBundle();
when(mMockCarrierConfigManager.getConfigForSubId(eq(i))).thenReturn(
mCarrierConfigs[i]);
when(mTestSubscriptionManagerProxy.getSlotIndex(eq(i))).thenReturn(i);
when(mTestSubscriptionManagerProxy.getSubId(eq(i))).thenReturn(i);
}
mTestImsResolver = new ImsResolver(mMockContext, TEST_DEVICE_DEFAULT_NAME.getPackageName(),
numSlots);
ArgumentCaptor<BroadcastReceiver> packageBroadcastCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
ArgumentCaptor<BroadcastReceiver> carrierConfigCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
verify(mMockContext).registerReceiverAsUser(packageBroadcastCaptor.capture(), any(),
any(), any(), any());
verify(mMockContext).registerReceiver(carrierConfigCaptor.capture(), any());
mTestCarrierConfigReceiver = carrierConfigCaptor.getValue();
mTestPackageBroadcastReceiver = packageBroadcastCaptor.getValue();
mTestImsResolver.setSubscriptionManagerProxy(mTestSubscriptionManagerProxy);
}
private void setImsServiceControllerFactory(ImsServiceController deviceController,
ImsServiceController carrierController) {
mTestImsResolver.setImsServiceControllerFactory((context, componentName) -> {
if (TEST_DEVICE_DEFAULT_NAME.getPackageName().equals(componentName.getPackageName())) {
when(deviceController.getComponentName()).thenReturn(componentName);
return deviceController;
} else if (TEST_CARRIER_DEFAULT_NAME.getPackageName().equals(
componentName.getPackageName())) {
when(carrierController.getComponentName()).thenReturn(componentName);
return carrierController;
}
return null;
});
}
private void setImsServiceControllerFactory(ImsServiceController deviceController,
ImsServiceController carrierController1, ImsServiceController carrierController2) {
mTestImsResolver.setImsServiceControllerFactory((context, componentName) -> {
if (TEST_DEVICE_DEFAULT_NAME.getPackageName().equals(componentName.getPackageName())) {
when(deviceController.getComponentName()).thenReturn(componentName);
return deviceController;
} else if (TEST_CARRIER_DEFAULT_NAME.getPackageName().equals(
componentName.getPackageName())) {
when(carrierController1.getComponentName()).thenReturn(componentName);
return carrierController1;
} else if (TEST_CARRIER_2_DEFAULT_NAME.getPackageName().equals(
componentName.getPackageName())) {
when(carrierController2.getComponentName()).thenReturn(componentName);
return carrierController2;
}
return null;
});
}
private void setConfigCarrierString(int subId, String packageName) {
mCarrierConfigs[subId].putString(
CarrierConfigManager.KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, packageName);
}
private HashSet<Pair<Integer, Integer>> convertToHashSet(Set<String> features, int subId) {
HashSet<Pair<Integer, Integer>> featureSet = features.stream()
.map(f -> new Pair<>(subId, metadataStringToFeature(f)))
.collect(Collectors.toCollection(HashSet::new));
return featureSet;
}
private int metadataStringToFeature(String f) {
switch (f) {
case ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE:
return ImsFeature.EMERGENCY_MMTEL;
case ImsResolver.METADATA_MMTEL_FEATURE:
return ImsFeature.MMTEL;
case ImsResolver.METADATA_RCS_FEATURE:
return ImsFeature.RCS;
}
return -1;
}
private boolean isImsServiceInfoEqual(ComponentName name, Set<String> features,
ImsResolver.ImsServiceInfo sInfo) {
if (!Objects.equals(sInfo.name, name)) {
return false;
}
for (String f : features) {
switch (f) {
case ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE:
if (!sInfo.supportedFeatures.contains(ImsFeature.EMERGENCY_MMTEL)) {
return false;
}
break;
case ImsResolver.METADATA_MMTEL_FEATURE:
if (!sInfo.supportedFeatures.contains(ImsFeature.MMTEL)) {
return false;
}
break;
case ImsResolver.METADATA_RCS_FEATURE:
if (!sInfo.supportedFeatures.contains(ImsFeature.RCS)) {
return false;
}
break;
}
}
return true;
}
private ResolveInfo getResolveInfo(ComponentName name, Set<String> features,
boolean isPermissionGranted) {
ResolveInfo info = new ResolveInfo();
info.serviceInfo = new ServiceInfo();
info.serviceInfo.packageName = name.getPackageName();
info.serviceInfo.name = name.getClassName();
info.serviceInfo.metaData = new Bundle();
for (String s : features) {
info.serviceInfo.metaData.putBoolean(s, true);
}
if (isPermissionGranted) {
info.serviceInfo.permission = Manifest.permission.BIND_IMS_SERVICE;
}
return info;
}
}