Update IMS resolver/binding code to support multiuser
Instead of assuming that we will bind on the system user, improve
the ImsResolver code so that it can take into account the current
user and bind based on the active user + system user to support
HSUM. The code now also supports ImsService packages limited to
a subset of the full users on the device.
This fix also improves logging for validation purposes.
Flag: com.android.internal.telephony.flags.ims_resolver_user_aware
Bug: b/371272669
Test: atest FrameworksTelephonyTests CtsTeleponyTestCases
Test: manual testing using ImsTestApp and switching across users
Change-Id: Ica5166382e8ddc2860306b57efd2727dda133ca6
diff --git a/flags/ims.aconfig b/flags/ims.aconfig
index 4426933..bb8abd0 100644
--- a/flags/ims.aconfig
+++ b/flags/ims.aconfig
@@ -138,6 +138,17 @@
}
}
+# OWNER=breadley TARGET=25Q1
+flag {
+ name: "ims_resolver_user_aware"
+ namespace: "telephony"
+ description: "When enabled, it makes ImsResolver mult-user aware for configurations like HSUM."
+ bug:"371272669"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
# OWNER=meghapatil TARGET=25Q2
flag {
name: "support_sms_over_ims_apis"
diff --git a/src/java/com/android/internal/telephony/ims/ImsResolver.java b/src/java/com/android/internal/telephony/ims/ImsResolver.java
index eb389b7..d8b0fd4 100644
--- a/src/java/com/android/internal/telephony/ims/ImsResolver.java
+++ b/src/java/com/android/internal/telephony/ims/ImsResolver.java
@@ -19,6 +19,7 @@
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -51,6 +52,7 @@
import android.util.ArraySet;
import android.util.LocalLog;
import android.util.Log;
+import android.util.Pair;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -129,6 +131,10 @@
private static final int HANDLER_MSIM_CONFIGURATION_CHANGE = 7;
// clear any carrier ImsService test overrides.
private static final int HANDLER_CLEAR_CARRIER_IMS_SERVICE_CONFIG = 8;
+ // the user has switched
+ private static final int HANDLER_USER_SWITCHED = 9;
+ // A dynamic query has failed permanently, remove the package from being tracked
+ private static final int HANDLER_REMOVE_PACKAGE_PERM_ERROR = 10;
// Delay between dynamic ImsService queries.
private static final int DELAY_DYNAMIC_QUERY_MS = 5000;
@@ -175,7 +181,8 @@
*/
@VisibleForTesting
public static class ImsServiceInfo {
- public ComponentName name;
+ public final ComponentName name;
+ public final Set<UserHandle> users = new HashSet<>();
// Determines if features were created from metadata in the manifest or through dynamic
// query.
public boolean featureFromMetadata = true;
@@ -184,7 +191,8 @@
// Map slotId->Feature
private final HashSet<ImsFeatureConfiguration.FeatureSlotPair> mSupportedFeatures;
- public ImsServiceInfo() {
+ public ImsServiceInfo(ComponentName componentName) {
+ name = componentName;
mSupportedFeatures = new HashSet<>();
}
@@ -208,37 +216,41 @@
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
-
ImsServiceInfo that = (ImsServiceInfo) o;
-
- if (name != null ? !name.equals(that.name) : that.name != null) return false;
- if (!mSupportedFeatures.equals(that.mSupportedFeatures)) {
- return false;
- }
- return controllerFactory != null ? controllerFactory.equals(that.controllerFactory)
- : that.controllerFactory == null;
+ return Objects.equals(name, that.name) && Objects.equals(users, that.users);
}
@Override
public int hashCode() {
- // We do not include mSupportedFeatures in hashcode because the internal structure
- // changes after adding.
- int result = name != null ? name.hashCode() : 0;
- result = 31 * result + (controllerFactory != null ? controllerFactory.hashCode() : 0);
- return result;
+ return Objects.hash(name, users);
}
@Override
public String toString() {
return "[ImsServiceInfo] name="
+ name
- + ", featureFromMetadata="
+ + ", user="
+ + users
+ + ",featureFromMetadata="
+ featureFromMetadata
+ ","
+ printFeatures(mSupportedFeatures);
}
}
+ private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ final UserHandle handle = intent.getParcelableExtra(Intent.EXTRA_USER,
+ UserHandle.class);
+ switch (action) {
+ case Intent.ACTION_USER_SWITCHED ->
+ mHandler.obtainMessage(HANDLER_USER_SWITCHED, handle).sendToTarget();
+ }
+ }
+ };
+
// Receives broadcasts from the system involving changes to the installed applications. If
// an ImsService that we are configured to use is installed, we must bind to it.
private final BroadcastReceiver mAppChangedReceiver = new BroadcastReceiver() {
@@ -311,6 +323,17 @@
};
/**
+ * Testing interface used to mock ActivityManager in testing
+ */
+ @VisibleForTesting
+ public interface ActivityManagerProxy {
+ /**
+ * @return The current user
+ */
+ UserHandle getCurrentUser();
+ }
+
+ /**
* Testing interface used to mock SubscriptionManager in testing
*/
@VisibleForTesting
@@ -360,6 +383,13 @@
}
};
+ private ActivityManagerProxy mActivityManagerProxy = new ActivityManagerProxy() {
+ @Override
+ public UserHandle getCurrentUser() {
+ return UserHandle.of(ActivityManager.getCurrentUser());
+ }
+ };
+
/**
* Testing interface for injecting mock ImsServiceControllers.
*/
@@ -470,6 +500,11 @@
maybeRemovedImsService(packageName);
break;
}
+ case HANDLER_REMOVE_PACKAGE_PERM_ERROR: {
+ Pair<String, UserHandle> packageInfo = (Pair<String, UserHandle>) msg.obj;
+ maybeRemovedImsServiceForUser(packageInfo.first, packageInfo.second);
+ break;
+ }
case HANDLER_BOOT_COMPLETE: {
if (!mBootCompletedHandlerRan) {
mBootCompletedHandlerRan = true;
@@ -534,6 +569,11 @@
clearCarrierServiceOverrides(msg.arg1);
break;
}
+ case HANDLER_USER_SWITCHED: {
+ UserHandle handle = (UserHandle) msg.obj;
+ Log.i(TAG, "onUserSwitched=" + handle);
+ maybeAddedImsService(null);
+ }
default:
break;
}
@@ -561,11 +601,16 @@
}
@Override
- public void onPermanentError(ComponentName name) {
+ public void onPermanentError(ComponentName name, UserHandle user) {
Log.w(TAG, "onPermanentError: component=" + name);
mEventLog.log("onPermanentError - error for " + name);
- mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE,
- name.getPackageName()).sendToTarget();
+ if (!mFeatureFlags.imsResolverUserAware()) {
+ mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE,
+ name.getPackageName()).sendToTarget();
+ } else {
+ mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE_PERM_ERROR,
+ new Pair<>(name.getPackageName(), user)).sendToTarget();
+ }
}
};
@@ -628,6 +673,11 @@
}
@VisibleForTesting
+ public void setActivityManagerProxy(ActivityManagerProxy proxy) {
+ mActivityManagerProxy = proxy;
+ }
+
+ @VisibleForTesting
public Handler getHandler() {
return mHandler;
}
@@ -660,6 +710,10 @@
appChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
appChangedFilter.addDataScheme("package");
mReceiverContext.registerReceiver(mAppChangedReceiver, appChangedFilter);
+ if (mFeatureFlags.imsResolverUserAware()) {
+ mReceiverContext.registerReceiver(mUserReceiver, new IntentFilter(
+ Intent.ACTION_USER_SWITCHED));
+ }
mReceiverContext.registerReceiver(mConfigChangedReceiver, new IntentFilter(
CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
@@ -706,19 +760,24 @@
// carrier config changed.
private void bindCarrierServicesIfAvailable() {
boolean hasConfigChanged = false;
+ boolean pendingDynamicQuery = false;
for (int slotId = 0; slotId < mNumSlots; slotId++) {
int subId = mSubscriptionManagerProxy.getSubId(slotId);
Map<Integer, String> featureMap = getImsPackageOverrideConfig(subId);
for (int f = ImsFeature.FEATURE_EMERGENCY_MMTEL; f < ImsFeature.FEATURE_MAX; f++) {
String newPackageName = featureMap.getOrDefault(f, "");
if (!TextUtils.isEmpty(newPackageName)) {
+ Log.d(TAG, "bindCarrierServicesIfAvailable - carrier package found: "
+ + newPackageName + ", feature "
+ + ImsFeature.FEATURE_LOG_MAP.getOrDefault(f, "invalid")
+ + " on slot " + slotId);
mEventLog.log("bindCarrierServicesIfAvailable - carrier package found: "
+ newPackageName + " on slot " + slotId);
// Carrier configs are already available, so mark received.
mCarrierConfigReceived = true;
setSubId(slotId, subId);
setCarrierConfiguredPackageName(newPackageName, slotId, f);
- ImsServiceInfo info = getImsServiceInfoFromCache(newPackageName);
+ ImsServiceInfo info = getVisibleImsServiceInfoFromCache(newPackageName);
// We do not want to trigger feature configuration changes unless there is
// already a valid carrier config change.
if (info != null && info.featureFromMetadata) {
@@ -726,11 +785,18 @@
} else {
// Config will change when this query completes
scheduleQueryForFeatures(info);
+ if (info != null) pendingDynamicQuery = true;
}
}
}
}
- if (hasConfigChanged) calculateFeatureConfigurationChange();
+ // we want to make sure that we are either pending to bind to a carrier configured service
+ // or bind to the device config if we potentially missed the carrier config changed
+ // indication.
+ if (hasConfigChanged || (mFeatureFlags.imsResolverUserAware()
+ && mCarrierConfigReceived && !pendingDynamicQuery)) {
+ calculateFeatureConfigurationChange();
+ }
}
/**
@@ -927,13 +993,14 @@
/**
* Check the cached ImsServices that exist on this device to determine if there is a ImsService
- * with the same package name that matches the provided configuration.
+ * with the same package name that matches the provided configuration and is configured to run
+ * in one of the active users.
*/
// not synchronized, access in handler ONLY.
private boolean doesCachedImsServiceExist(String packageName, int slotId,
@ImsFeature.FeatureType int featureType) {
// Config exists, but the carrier ImsService also needs to support this feature
- ImsServiceInfo info = getImsServiceInfoFromCache(packageName);
+ ImsServiceInfo info = getVisibleImsServiceInfoFromCache(packageName);
return info != null && info.getSupportedFeatures().stream().anyMatch(
feature -> feature.slotId == slotId && feature.featureType == featureType);
}
@@ -1073,7 +1140,8 @@
return null;
}
- private void putImsController(int slotId, int feature, ImsServiceController controller) {
+ private void putImsController(int slotId, int subId, int feature,
+ ImsServiceController controller) {
if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.FEATURE_INVALID
|| feature >= ImsFeature.FEATURE_MAX) {
Log.w(TAG, "putImsController received invalid parameters - slot: " + slotId
@@ -1088,9 +1156,9 @@
}
mEventLog.log("putImsController - [" + slotId + ", "
+ ImsFeature.FEATURE_LOG_MAP.get(feature) + "] -> " + controller);
- Log.i(TAG, "ImsServiceController added on slot: " + slotId + " with feature: "
- + ImsFeature.FEATURE_LOG_MAP.get(feature) + " using package: "
- + controller.getComponentName());
+ Log.i(TAG, "ImsServiceController added on slot: " + slotId + ", subId: " + subId
+ + " with feature: " + ImsFeature.FEATURE_LOG_MAP.get(feature)
+ + " using package: " + controller.getComponentName());
services.put(feature, controller);
}
}
@@ -1134,6 +1202,10 @@
// features. Will only be one (if it exists), since it is a set.
ImsServiceInfo match = getInfoByComponentName(mInstalledServicesCache, info.name);
if (match != null) {
+ if (mFeatureFlags.imsResolverUserAware()) {
+ match.users.clear();
+ match.users.addAll(info.users);
+ }
// for dynamic query the new "info" will have no supported features yet. Don't wipe
// out the cache for the existing features or update yet. Instead start a query
// for features dynamically.
@@ -1141,9 +1213,8 @@
mEventLog.log("maybeAddedImsService - updating features for " + info.name
+ ": " + printFeatures(match.getSupportedFeatures()) + " -> "
+ printFeatures(info.getSupportedFeatures()));
- Log.i(TAG, "Updating features in cached ImsService: " + info.name);
- Log.d(TAG, "Updating features - Old features: " + match + " new features: "
- + info);
+ Log.d(TAG, "Updating features in cached ImsService: " + info.name
+ + ", old features: " + match + " new features: " + info);
// update features in the cache
match.replaceFeatures(info.getSupportedFeatures());
requiresCalculation = true;
@@ -1168,10 +1239,9 @@
if (requiresCalculation) calculateFeatureConfigurationChange();
}
- // Remove the ImsService from the cache. This may have been due to the ImsService being removed
- // from the device or was returning permanent errors when bound.
+ // Remove the ImsService from the cache due to the ImsService package being removed.
// Called from the handler ONLY
- private boolean maybeRemovedImsService(String packageName) {
+ private boolean maybeRemovedImsServiceOld(String packageName) {
ImsServiceInfo match = getInfoByPackageName(mInstalledServicesCache, packageName);
if (match != null) {
mInstalledServicesCache.remove(match.name);
@@ -1184,6 +1254,69 @@
return false;
}
+ // Remove the ImsService from the cache due to the ImsService package being removed.
+ // Called from the handler ONLY
+ private boolean maybeRemovedImsService(String packageName) {
+ if (!mFeatureFlags.imsResolverUserAware()) {
+ return maybeRemovedImsServiceOld(packageName);
+ }
+ ImsServiceInfo match = getInfoByPackageName(mInstalledServicesCache, packageName);
+ if (match != null) {
+ List<ImsServiceInfo> imsServices = searchForImsServices(packageName,
+ match.controllerFactory);
+ ImsServiceInfo newMatch = imsServices.isEmpty() ? null : imsServices.getFirst();
+ if (newMatch == null) {
+ // The package doesn't exist anymore on any user, so remove
+ mInstalledServicesCache.remove(match.name);
+ mEventLog.log("maybeRemovedImsService - removing ImsService: " + match);
+ Log.i(TAG, "maybeRemovedImsService Removing ImsService for all users: "
+ + match.name);
+ unbindImsService(match);
+ } else {
+ // The Package exists on some users still, so modify the users
+ match.users.clear();
+ match.users.addAll(newMatch.users);
+ mEventLog.log("maybeRemovedImsService - modifying ImsService users: " + match);
+ Log.i(TAG, "maybeRemovedImsService - Modifying ImsService users " + match);
+ // If this package still remains on some users, then it is possible we are unbinding
+ // an active ImsService, but the assumption here is that the package is being
+ // removed on an active user. Be safe and unbind now - we will rebind below if
+ // needed.
+ unbindImsService(match);
+ }
+ calculateFeatureConfigurationChange();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Remove the cached ImsService for a specific user. If there are no more users available after
+ * removing the specified user, remove the ImsService cache entry entirely.
+ */
+ // Called from the handler ONLY
+ private boolean maybeRemovedImsServiceForUser(String packageName, UserHandle user) {
+ ImsServiceInfo match = getInfoByPackageName(mInstalledServicesCache, packageName);
+ if (match != null) {
+ mEventLog.log("maybeRemovedImsServiceForUser - removing ImsService " + match
+ + "for user " + user);
+ Log.i(TAG, "maybeRemovedImsServiceForUser: Removing ImsService "
+ + match + "for user " + user);
+ unbindImsService(match);
+ match.users.remove(user);
+ if (match.users.isEmpty()) {
+ mEventLog.log("maybeRemovedImsServiceForUser - no more users, removing "
+ + "ImsService " + match);
+ Log.i(TAG, "maybeRemovedImsServiceForUser - no more users, removing "
+ + "ImsService " + match);
+ mInstalledServicesCache.remove(match.name);
+ }
+ calculateFeatureConfigurationChange();
+ return true;
+ }
+ return false;
+ }
+
private boolean isDeviceService(ImsServiceInfo info) {
if (info == null) return false;
synchronized (mDeviceServices) {
@@ -1193,6 +1326,15 @@
private List<Integer> getSlotsForActiveCarrierService(ImsServiceInfo info) {
if (info == null) return Collections.emptyList();
+ if (mFeatureFlags.imsResolverUserAware()) {
+ Set<UserHandle> activeUsers = getActiveUsers();
+ activeUsers.retainAll(info.users);
+ if (activeUsers.isEmpty()) {
+ Log.d(TAG, "getSlotsForActiveCarrierService: ImsService " + info.name + "is not "
+ + "configured to run for users " + activeUsers + ", skipping...");
+ return Collections.emptyList();
+ }
+ }
List<Integer> slots = new ArrayList<>(mNumSlots);
for (int i = 0; i < mNumSlots; i++) {
if (!TextUtils.isEmpty(getCarrierConfiguredPackageNames(i).values().stream()
@@ -1222,7 +1364,7 @@
return searchMap.get(matchValue);
}
- private void bindImsServiceWithFeatures(ImsServiceInfo info,
+ private void bindImsServiceWithFeatures(ImsServiceInfo info, UserHandle user,
Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
// Only bind if there are features that will be created by the service.
if (shouldFeaturesCauseBind(features)) {
@@ -1230,10 +1372,21 @@
ImsServiceController controller = getControllerByServiceInfo(mActiveControllers, info);
SparseIntArray slotIdToSubIdMap = mSlotIdToSubIdMap.clone();
if (controller != null) {
- Log.i(TAG, "ImsService connection exists for " + info.name + ", updating features "
- + features);
try {
- controller.changeImsServiceFeatures(features, slotIdToSubIdMap);
+ if (!mFeatureFlags.imsResolverUserAware()
+ || Objects.equals(user, controller.getBoundUser())) {
+ Log.i(TAG, "ImsService connection exists for " + info.name
+ + ", updating features " + features);
+ controller.changeImsServiceFeatures(features, slotIdToSubIdMap);
+ } else {
+ // Changing a user is a pretty rare event, we need to unbind and rebind
+ // on the correct new user.
+ Log.i(TAG, "ImsService user changed for " + info.name
+ + ", rebinding on user " + user + ", features " + features);
+ controller.unbind();
+ controller.bind(user, features, slotIdToSubIdMap);
+ }
+
// Features have been set, there was an error adding/removing. When the
// controller recovers, it will add/remove again.
} catch (RemoteException e) {
@@ -1243,8 +1396,9 @@
controller = info.controllerFactory.create(mContext, info.name, this, mRepo,
mFeatureFlags);
Log.i(TAG, "Binding ImsService: " + controller.getComponentName()
- + " with features: " + features);
- controller.bind(features, slotIdToSubIdMap);
+ + "on user " + user + " with features: " + features + ", subIdMap: "
+ + slotIdToSubIdMap);
+ controller.bind(user, features, slotIdToSubIdMap);
mEventLog.log("bindImsServiceWithFeatures - create new controller: "
+ controller);
}
@@ -1285,7 +1439,7 @@
imsFeaturesBySlot.addAll(info.getSupportedFeatures().stream()
.filter(feature -> info.name.getPackageName().equals(
getCarrierConfiguredPackageName(feature.slotId, feature.featureType)))
- .collect(Collectors.toList()));
+ .toList());
return imsFeaturesBySlot;
}
if (isDeviceService(info)) {
@@ -1298,7 +1452,7 @@
// by the carrier ImsService.
.filter(feature -> !doesCarrierConfigurationExist(feature.slotId,
feature.featureType))
- .collect(Collectors.toList()));
+ .toList());
}
return imsFeaturesBySlot;
}
@@ -1309,8 +1463,9 @@
* adds the ImsServiceController from the mBoundImsServicesByFeature structure.
*/
@Override
- public void imsServiceFeatureCreated(int slotId, int feature, ImsServiceController controller) {
- putImsController(slotId, feature, controller);
+ public void imsServiceFeatureCreated(int slotId, int subId, int feature,
+ ImsServiceController controller) {
+ putImsController(slotId, subId, feature, controller);
}
/**
@@ -1341,13 +1496,19 @@
}
@Override
- public void imsServiceBindPermanentError(ComponentName name) {
+ public void imsServiceBindPermanentError(ComponentName name, UserHandle user) {
if (name == null) {
return;
}
- Log.w(TAG, "imsServiceBindPermanentError: component=" + name);
- mEventLog.log("imsServiceBindPermanentError - for " + name);
- mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE, name.getPackageName()).sendToTarget();
+ Log.w(TAG, "imsServiceBindPermanentError: component=" + name + ", user=" + user);
+ mEventLog.log("imsServiceBindPermanentError - for " + name + ", user " + user);
+ if (!mFeatureFlags.imsResolverUserAware()) {
+ mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE,
+ name.getPackageName()).sendToTarget();
+ } else {
+ mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE_PERM_ERROR,
+ new Pair<>(name.getPackageName(), user)).sendToTarget();
+ }
}
/**
@@ -1400,7 +1561,7 @@
mEventLog.log("overrideDeviceService - device package changed (override): "
+ oldPackageName + " -> " + overridePackageName);
setDeviceConfiguration(overridePackageName, featureType);
- ImsServiceInfo info = getImsServiceInfoFromCache(overridePackageName);
+ ImsServiceInfo info = getVisibleImsServiceInfoFromCache(overridePackageName);
if (info == null || info.featureFromMetadata) {
requiresRecalc = true;
} else {
@@ -1430,7 +1591,7 @@
ArrayMap<String, ImsServiceInfo> featureDynamicImsPackages = new ArrayMap<>();
for (int f = ImsFeature.FEATURE_EMERGENCY_MMTEL; f < ImsFeature.FEATURE_MAX; f++) {
String packageName = getDeviceConfiguration(f);
- ImsServiceInfo serviceInfo = getImsServiceInfoFromCache(packageName);
+ ImsServiceInfo serviceInfo = getVisibleImsServiceInfoFromCache(packageName);
if (serviceInfo != null && !serviceInfo.featureFromMetadata
&& !featureDynamicImsPackages.containsKey(packageName)) {
featureDynamicImsPackages.put(packageName, serviceInfo);
@@ -1465,13 +1626,7 @@
setCarrierConfiguredPackageName(newPackageName, slotId, f);
// Carrier config may have not changed, but we still want to kick off a recalculation
// in case there has been a change to the supported device features.
- ImsServiceInfo info = getImsServiceInfoFromCache(newPackageName);
- Log.i(TAG, "updateBoundServices - carrier package changed: "
- + oldPackageName + " -> " + newPackageName + " on slot " + slotId
- + ", hasConfigChanged=" + hasConfigChanged);
- mEventLog.log("updateBoundServices - carrier package changed: "
- + oldPackageName + " -> " + newPackageName + " on slot " + slotId
- + ", hasConfigChanged=" + hasConfigChanged);
+ ImsServiceInfo info = getVisibleImsServiceInfoFromCache(newPackageName);
if (info == null || info.featureFromMetadata) {
hasConfigChanged = true;
} else {
@@ -1479,6 +1634,12 @@
scheduleQueryForFeatures(info);
didQuerySchedule = true;
}
+ Log.i(TAG, "updateBoundServices - carrier package changed: "
+ + oldPackageName + " -> " + newPackageName + " on slot " + slotId
+ + ", hasConfigChanged=" + hasConfigChanged);
+ mEventLog.log("updateBoundServices - carrier package changed: "
+ + oldPackageName + " -> " + newPackageName + " on slot " + slotId
+ + ", hasConfigChanged=" + hasConfigChanged);
}
if (hasConfigChanged) calculateFeatureConfigurationChange();
@@ -1530,7 +1691,7 @@
}
private void scheduleQueryForFeatures(ComponentName name, int delayMs) {
- ImsServiceInfo service = getImsServiceInfoFromCache(name.getPackageName());
+ ImsServiceInfo service = getVisibleImsServiceInfoFromCache(name.getPackageName());
if (service == null) {
Log.w(TAG, "scheduleQueryForFeatures: Couldn't find cached info for name: " + name);
return;
@@ -1614,6 +1775,12 @@
// Starts a dynamic query. Called from handler ONLY.
private void startDynamicQuery(ImsServiceInfo service) {
+ UserHandle user = getUserForBind(service);
+ if (user == null) {
+ Log.i(TAG, "scheduleQueryForFeatures: skipping query for ImsService that is not"
+ + " running: " + service);
+ return;
+ }
// if not current device/carrier service, don't perform query. If this changes, this method
// will be called again.
if (!isDeviceService(service) && getSlotsForActiveCarrierService(service).isEmpty()) {
@@ -1622,7 +1789,7 @@
return;
}
mEventLog.log("startDynamicQuery - starting query for " + service);
- boolean queryStarted = mFeatureQueryManager.startQuery(service.name,
+ boolean queryStarted = mFeatureQueryManager.startQuery(service.name, user,
service.controllerFactory.getServiceInterface());
if (!queryStarted) {
Log.w(TAG, "startDynamicQuery: service could not connect. Retrying after delay.");
@@ -1637,7 +1804,7 @@
// process complete dynamic query. Called from handler ONLY.
private void dynamicQueryComplete(ComponentName name,
Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
- ImsServiceInfo service = getImsServiceInfoFromCache(name.getPackageName());
+ ImsServiceInfo service = getVisibleImsServiceInfoFromCache(name.getPackageName());
if (service == null) {
Log.w(TAG, "dynamicQueryComplete: Couldn't find cached info for name: "
+ name);
@@ -1683,17 +1850,85 @@
// Calculate the new configuration for the bound ImsServices.
// Should ONLY be called from the handler.
- private void calculateFeatureConfigurationChange() {
+ private void calculateFeatureConfigurationChangeOld() {
for (ImsServiceInfo info : mInstalledServicesCache.values()) {
Set<ImsFeatureConfiguration.FeatureSlotPair> features = calculateFeaturesToCreate(info);
if (shouldFeaturesCauseBind(features)) {
- bindImsServiceWithFeatures(info, features);
+ bindImsServiceWithFeatures(info, mContext.getUser(), features);
} else {
unbindImsService(info);
}
}
}
+ // Should ONLY be called from the handler.
+ private void calculateFeatureConfigurationChange() {
+ if (!mFeatureFlags.imsResolverUserAware()) {
+ calculateFeatureConfigurationChangeOld();
+ return;
+ }
+ // There is an implicit assumption here that the ImsServiceController will remove itself
+ // from caches BEFORE adding a new one. If this assumption is broken, we will remove a valid
+ // ImsServiceController from the cache accidentally. To keep this assumption valid, we will
+ // iterate through the cache twice - first to unbind, then to bind and change features of
+ // existing ImsServiceControllers. This is a little inefficient, but there should be on the
+ // order of 10 installed ImsServices at most, so running through this list twice is
+ // reasonable vs the memory cost of caching binding vs unbinding services.
+
+ // Unbind first if needed
+ for (ImsServiceInfo info : mInstalledServicesCache.values()) {
+ Set<ImsFeatureConfiguration.FeatureSlotPair> features = calculateFeaturesToCreate(info);
+ UserHandle user = getUserForBind(info);
+ if (shouldFeaturesCauseBind(features) && user != null) continue;
+ unbindImsService(info);
+ }
+ // Bind/alter features second
+ for (ImsServiceInfo info : mInstalledServicesCache.values()) {
+ Set<ImsFeatureConfiguration.FeatureSlotPair> features = calculateFeaturesToCreate(info);
+ UserHandle user = getUserForBind(info);
+ if (shouldFeaturesCauseBind(features) && user != null) {
+ bindImsServiceWithFeatures(info, user, features);
+ }
+ }
+ }
+
+ /**
+ * Returns the UserHandle that should be used to bind the ImsService.
+ *
+ * @return The UserHandle of the user that telephony is running in if the
+ * ImsService is configured to run in that user, or the current active user
+ * if not. Returns null if the ImsService is not configured to run in any
+ * active user.
+ */
+ private UserHandle getUserForBind(ImsServiceInfo info) {
+ if (!mFeatureFlags.imsResolverUserAware()) {
+ return mContext.getUser();
+ }
+ UserHandle currentUser = mActivityManagerProxy.getCurrentUser();
+ List<UserHandle> activeUsers = getActiveUsers().stream()
+ .filter(info.users::contains).toList();
+ if (activeUsers.isEmpty()) return null;
+ // Prioritize the User that Telephony is in, since it is always running
+ if (activeUsers.stream()
+ .anyMatch(u -> Objects.equals(u, mContext.getUser()))) {
+ return mContext.getUser();
+ }
+ if (activeUsers.stream().anyMatch(u -> Objects.equals(u, currentUser))) {
+ return currentUser;
+ }
+ return null;
+ }
+
+ /**
+ * Returns the set of full users that are currently active.
+ */
+ private Set<UserHandle> getActiveUsers() {
+ Set<UserHandle> profiles = new HashSet<>();
+ profiles.add(mContext.getUser());
+ profiles.add(mActivityManagerProxy.getCurrentUser());
+ return profiles;
+ }
+
private static String printFeatures(Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
StringBuilder featureString = new StringBuilder();
featureString.append(" features: [");
@@ -1711,8 +1946,25 @@
}
/**
- * Returns the ImsServiceInfo that matches the provided packageName. Visible for testing
- * the ImsService caching functionality.
+ * Returns the ImsServiceInfo that matches the provided packageName if it belongs to a
+ * package that is visible as part of the set of active users.
+ */
+ public ImsServiceInfo getVisibleImsServiceInfoFromCache(String packageName) {
+ ImsServiceInfo match = getImsServiceInfoFromCache(packageName);
+ if (!mFeatureFlags.imsResolverUserAware()) {
+ return match;
+ }
+ if (match == null) return null;
+ Set<UserHandle> activeUsers = getActiveUsers();
+ activeUsers.retainAll(match.users);
+ Log.d(TAG, "getVisibleImsServiceInfoFromCache: " + packageName + ", match=" + match
+ + ", activeUsers=" + activeUsers);
+ if (!activeUsers.isEmpty()) return match; else return null;
+ }
+
+ /**
+ * Returns the ImsServiceInfo that matches the provided packageName. This includes
+ * ImsServiceInfos that are not currently visible for the active users.
*/
@VisibleForTesting
public ImsServiceInfo getImsServiceInfoFromCache(String packageName) {
@@ -1738,6 +1990,12 @@
return infos;
}
+ private ImsServiceInfo getInfoFromCache(List<ImsServiceInfo> infos,
+ ComponentName componentName) {
+ return infos.stream().filter(info -> Objects.equals(info.name, componentName)).findFirst()
+ .orElse(null);
+ }
+
private List<ImsServiceInfo> searchForImsServices(String packageName,
ImsServiceControllerFactory controllerFactory) {
List<ImsServiceInfo> infos = new ArrayList<>();
@@ -1745,62 +2003,84 @@
Intent serviceIntent = new Intent(controllerFactory.getServiceInterface());
serviceIntent.setPackage(packageName);
+ Set<UserHandle> profiles;
+ if (mFeatureFlags.imsResolverUserAware()) {
+ profiles = getActiveUsers();
+ } else {
+ profiles = Collections.singleton(mContext.getUser());
+ }
+ Log.v(TAG, "searchForImsServices: package=" + packageName + ", users=" + profiles);
+
PackageManager packageManager = mContext.getPackageManager();
- for (ResolveInfo entry : packageManager.queryIntentServicesAsUser(
- serviceIntent,
- PackageManager.GET_META_DATA,
- UserHandle.of(UserHandle.myUserId()))) {
- ServiceInfo serviceInfo = entry.serviceInfo;
+ for (UserHandle handle : profiles) {
+ for (ResolveInfo entry : packageManager.queryIntentServicesAsUser(serviceIntent,
+ PackageManager.GET_META_DATA, handle)) {
+ ServiceInfo serviceInfo = entry.serviceInfo;
- if (serviceInfo != null) {
- ImsServiceInfo info = new ImsServiceInfo();
- info.name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
- info.controllerFactory = controllerFactory;
+ if (serviceInfo != null) {
+ ComponentName name = new ComponentName(serviceInfo.packageName,
+ serviceInfo.name);
+ ImsServiceInfo info = getInfoFromCache(infos, name);
+ if (info != null) {
+ info.users.add(handle);
+ Log.d(TAG, "service modify users:" + info);
+ continue;
+ } else {
+ info = new ImsServiceInfo(name);
+ info.users.add(handle);
+ }
+ info.controllerFactory = controllerFactory;
- // we will allow the manifest method of declaring manifest features in two cases:
- // 1) it is the device overlay "default" ImsService, where the features do not
- // change (the new method can still be used if the default does not define manifest
- // entries).
- // 2) using the "compat" ImsService, which only supports manifest query.
- if (isDeviceService(info)
- || mImsServiceControllerFactoryCompat == controllerFactory) {
- if (serviceInfo.metaData != null) {
- if (serviceInfo.metaData.getBoolean(METADATA_MMTEL_FEATURE, false)) {
- info.addFeatureForAllSlots(mNumSlots, ImsFeature.FEATURE_MMTEL);
- // only allow FEATURE_EMERGENCY_MMTEL if FEATURE_MMTEL is defined.
- if (serviceInfo.metaData.getBoolean(METADATA_EMERGENCY_MMTEL_FEATURE,
- false)) {
- info.addFeatureForAllSlots(mNumSlots,
- ImsFeature.FEATURE_EMERGENCY_MMTEL);
+ // we will allow the manifest method of declaring manifest features in two
+ // cases:
+
+ // 1) it is the device overlay "default" ImsService, where the features do not
+ // change (the new method can still be used if the default does not define
+ // manifest entries).
+ // 2) using the "compat" ImsService, which only supports manifest query.
+ if (isDeviceService(info)
+ || mImsServiceControllerFactoryCompat == controllerFactory) {
+ if (serviceInfo.metaData != null) {
+ if (serviceInfo.metaData.getBoolean(METADATA_MMTEL_FEATURE, false)) {
+ info.addFeatureForAllSlots(mNumSlots, ImsFeature.FEATURE_MMTEL);
+ // only allow FEATURE_EMERGENCY_MMTEL if FEATURE_MMTEL is defined.
+ if (serviceInfo.metaData.getBoolean(
+ METADATA_EMERGENCY_MMTEL_FEATURE,
+ false)) {
+ info.addFeatureForAllSlots(mNumSlots,
+ ImsFeature.FEATURE_EMERGENCY_MMTEL);
+ }
+ }
+ if (serviceInfo.metaData.getBoolean(METADATA_RCS_FEATURE, false)) {
+ info.addFeatureForAllSlots(mNumSlots, ImsFeature.FEATURE_RCS);
}
}
- if (serviceInfo.metaData.getBoolean(METADATA_RCS_FEATURE, false)) {
- info.addFeatureForAllSlots(mNumSlots, ImsFeature.FEATURE_RCS);
+ // Only dynamic query if we are not a compat version of ImsService and the
+ // default service.
+ if (mImsServiceControllerFactoryCompat != controllerFactory
+ && info.getSupportedFeatures().isEmpty()) {
+ // metadata empty, try dynamic query instead
+ info.featureFromMetadata = false;
}
- }
- // Only dynamic query if we are not a compat version of ImsService and the
- // default service.
- if (mImsServiceControllerFactoryCompat != controllerFactory
- && info.getSupportedFeatures().isEmpty()) {
- // metadata empty, try dynamic query instead
+ } else {
+ // We are a carrier service and not using the compat version of ImsService.
info.featureFromMetadata = false;
}
- } else {
- // We are a carrier service and not using the compat version of ImsService.
- info.featureFromMetadata = false;
- }
- Log.i(TAG, "service name: " + info.name + ", manifest query: "
- + info.featureFromMetadata);
- // Check manifest permission to be sure that the service declares the correct
- // permissions. Overridden if the METADATA_OVERRIDE_PERM_CHECK metadata is set to
- // true.
- // NOTE: METADATA_OVERRIDE_PERM_CHECK should only be set for testing.
- if (TextUtils.equals(serviceInfo.permission, Manifest.permission.BIND_IMS_SERVICE)
- || serviceInfo.metaData.getBoolean(METADATA_OVERRIDE_PERM_CHECK, false)) {
- infos.add(info);
- } else {
- Log.w(TAG, "ImsService is not protected with BIND_IMS_SERVICE permission: "
- + info.name);
+ Log.d(TAG, "service name: " + info.name + ", manifest query: "
+ + info.featureFromMetadata + ", users: " + info.users);
+ // Check manifest permission to be sure that the service declares the correct
+ // permissions. Overridden if the METADATA_OVERRIDE_PERM_CHECK metadata is set
+ // to true.
+ // NOTE: METADATA_OVERRIDE_PERM_CHECK should only be set for testing.
+ if (TextUtils.equals(serviceInfo.permission,
+ Manifest.permission.BIND_IMS_SERVICE)
+ || serviceInfo.metaData.getBoolean(METADATA_OVERRIDE_PERM_CHECK,
+ false)) {
+ infos.add(info);
+ } else {
+ Log.w(TAG, "ImsService is not protected with BIND_IMS_SERVICE permission: "
+ + info.name);
+ }
}
}
}
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceController.java b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
index ea8399f..37c10eb 100644
--- a/src/java/com/android/internal/telephony/ims/ImsServiceController.java
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
@@ -196,7 +196,7 @@
}
if (mCallbacks != null) {
// Will trigger an unbind.
- mCallbacks.imsServiceBindPermanentError(getComponentName());
+ mCallbacks.imsServiceBindPermanentError(getComponentName(), mBoundUser);
}
}
@@ -217,7 +217,8 @@
/**
* Called by ImsServiceController when a new MMTEL or RCS feature has been created.
*/
- void imsServiceFeatureCreated(int slotId, int feature, ImsServiceController controller);
+ void imsServiceFeatureCreated(int slotId, int subId, int feature,
+ ImsServiceController controller);
/**
* Called by ImsServiceController when a new MMTEL or RCS feature has been removed.
*/
@@ -234,7 +235,7 @@
* Called by the ImsServiceController when there has been an error binding that is
* not recoverable, such as the ImsService returning a null binder.
*/
- void imsServiceBindPermanentError(ComponentName name);
+ void imsServiceBindPermanentError(ComponentName name, UserHandle user);
}
/**
@@ -273,6 +274,7 @@
private boolean mIsBound = false;
private boolean mIsBinding = false;
+ private UserHandle mBoundUser = null;
// Set of a pair of slotId->feature
private Set<ImsFeatureConfiguration.FeatureSlotPair> mImsFeatures;
private SparseIntArray mSlotIdToSubIdMap;
@@ -337,7 +339,7 @@
if (mIsBound) {
return;
}
- bind(mImsFeatures, mSlotIdToSubIdMap);
+ bind(mBoundUser, mImsFeatures, mSlotIdToSubIdMap);
}
}
};
@@ -413,17 +415,18 @@
* @return {@link true} if the service is in the process of being bound, {@link false} if it
* has failed.
*/
- public boolean bind(Set<ImsFeatureConfiguration.FeatureSlotPair> imsFeatureSet,
- SparseIntArray slotIdToSubIdMap) {
+ public boolean bind(UserHandle user, Set<ImsFeatureConfiguration.FeatureSlotPair> imsFeatureSet,
+ SparseIntArray slotIdToSubIdMap) {
synchronized (mLock) {
if (!mIsBound && !mIsBinding) {
mIsBinding = true;
+ mBoundUser = user;
sanitizeFeatureConfig(imsFeatureSet);
mImsFeatures = imsFeatureSet;
mSlotIdToSubIdMap = slotIdToSubIdMap;
// Set the number of slots that support the feature
mImsEnablementTracker.setNumOfSlots(mSlotIdToSubIdMap.size());
- grantPermissionsToService();
+ grantPermissionsToService(user);
Intent imsServiceIntent = new Intent(getServiceInterface()).setComponent(
mComponentName);
mImsServiceConnection = new ImsServiceConnection();
@@ -432,8 +435,8 @@
mLocalLog.log("binding " + imsFeatureSet);
Log.i(LOG_TAG, "Binding ImsService:" + mComponentName);
try {
- boolean bindSucceeded = mContext.bindService(imsServiceIntent,
- mImsServiceConnection, serviceFlags);
+ boolean bindSucceeded = mContext.bindServiceAsUser(imsServiceIntent,
+ mImsServiceConnection, serviceFlags, user);
if (!bindSucceeded) {
mLocalLog.log(" binding failed, retrying in "
+ mBackoff.getCurrentDelay() + " mS");
@@ -482,6 +485,7 @@
changeImsServiceFeatures(new HashSet<>(), mSlotIdToSubIdMap);
mIsBound = false;
mIsBinding = false;
+ mBoundUser = null;
setServiceController(null);
unbindService();
}
@@ -608,6 +612,13 @@
}
/**
+ * @return The UserHandle that this controller is bound to or null if bound to no service.
+ */
+ public UserHandle getBoundUser() {
+ return mBoundUser;
+ }
+
+ /**
* Notify ImsService to enable IMS for the framework. This will trigger IMS registration and
* trigger ImsFeature status updates.
*/
@@ -766,7 +777,7 @@
// Grant runtime permissions to ImsService. PermissionManager ensures that the ImsService is
// system/signed before granting permissions.
- private void grantPermissionsToService() {
+ private void grantPermissionsToService(UserHandle user) {
mLocalLog.log("grant permissions to " + getComponentName());
Log.i(LOG_TAG, "Granting Runtime permissions to:" + getComponentName());
String[] pkgToGrant = {mComponentName.getPackageName()};
@@ -774,8 +785,7 @@
if (mPermissionManager != null) {
CountDownLatch latch = new CountDownLatch(1);
mPermissionManager.grantDefaultPermissionsToEnabledImsServices(
- pkgToGrant, UserHandle.of(UserHandle.myUserId()), Runnable::run,
- isSuccess -> {
+ pkgToGrant, user, Runnable::run, isSuccess -> {
if (isSuccess) {
latch.countDown();
} else {
@@ -807,7 +817,8 @@
Log.i(LOG_TAG, "supports emergency calling on slot " + featurePair.slotId);
}
// Signal ImsResolver to change supported ImsFeatures for this ImsServiceController
- mCallbacks.imsServiceFeatureCreated(featurePair.slotId, featurePair.featureType, this);
+ mCallbacks.imsServiceFeatureCreated(featurePair.slotId, subId, featurePair.featureType,
+ this);
}
// This method should only be called when synchronized on mLock
@@ -978,10 +989,11 @@
@Override
public String toString() {
synchronized (mLock) {
- return "[ImsServiceController: componentName=" + getComponentName() + ", features="
- + mImsFeatures + ", isBinding=" + mIsBinding + ", isBound=" + mIsBound
- + ", serviceController=" + getImsServiceController() + ", rebindDelay="
- + getRebindDelay() + "]";
+ return "[ImsServiceController: componentName=" + getComponentName() + ", boundUser="
+ + mBoundUser + ", features=" + mImsFeatures + ", isBinding=" + mIsBinding
+ + ", isBound=" + mIsBound + ", serviceController=" + getImsServiceController()
+ + ", rebindDelay=" + getRebindDelay() + ", slotToSubIdMap=" + mSlotIdToSubIdMap
+ + "]";
}
}
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceFeatureQueryManager.java b/src/java/com/android/internal/telephony/ims/ImsServiceFeatureQueryManager.java
index 564cdcc..a4b4f46 100644
--- a/src/java/com/android/internal/telephony/ims/ImsServiceFeatureQueryManager.java
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceFeatureQueryManager.java
@@ -21,6 +21,7 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
+import android.os.UserHandle;
import android.telephony.ims.aidl.IImsServiceController;
import android.telephony.ims.stub.ImsFeatureConfiguration;
import android.util.Log;
@@ -42,14 +43,16 @@
private static final String LOG_TAG = "ImsServiceFeatureQuery";
private final ComponentName mName;
+ private final UserHandle mUser;
private final String mIntentFilter;
// Track the status of whether or not the Service has died in case we need to permanently
// unbind (see onNullBinding below).
private boolean mIsServiceConnectionDead = false;
- ImsServiceFeatureQuery(ComponentName name, String intentFilter) {
+ ImsServiceFeatureQuery(ComponentName name, UserHandle user, String intentFilter) {
mName = name;
+ mUser = user;
mIntentFilter = intentFilter;
}
@@ -62,7 +65,8 @@
Intent imsServiceIntent = new Intent(mIntentFilter).setComponent(mName);
int serviceFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
| Context.BIND_IMPORTANT;
- boolean bindStarted = mContext.bindService(imsServiceIntent, this, serviceFlags);
+ boolean bindStarted = mContext.bindServiceAsUser(imsServiceIntent, this,
+ serviceFlags, mUser);
if (!bindStarted) {
// Docs say to unbind if this fails.
cleanup();
@@ -78,7 +82,7 @@
} else {
Log.w(LOG_TAG, "onServiceConnected: " + name + " binder null.");
cleanup();
- mListener.onPermanentError(name);
+ mListener.onPermanentError(name, mUser);
}
}
@@ -103,7 +107,7 @@
// permanently unbind and instead let the automatic rebind occur.
if (mIsServiceConnectionDead) return;
cleanup();
- mListener.onPermanentError(name);
+ mListener.onPermanentError(name, mUser);
}
private void queryImsFeatures(IImsServiceController controller) {
@@ -154,7 +158,7 @@
/**
* Called when a query has failed due to a permanent error and should not be retried.
*/
- void onPermanentError(ComponentName name);
+ void onPermanentError(ComponentName name, UserHandle user);
}
// Maps an active ImsService query (by Package Name String) its query.
@@ -171,16 +175,17 @@
/**
* Starts an ImsService feature query for the ComponentName and Intent specified.
* @param name The ComponentName of the ImsService being queried.
+ * @param user The User associated with the request.
* @param intentFilter The Intent filter that the ImsService specified.
* @return true if the query started, false if it was unable to start.
*/
- public boolean startQuery(ComponentName name, String intentFilter) {
+ public boolean startQuery(ComponentName name, UserHandle user, String intentFilter) {
synchronized (mLock) {
if (mActiveQueries.containsKey(name)) {
// We already have an active query, wait for it to return.
return true;
}
- ImsServiceFeatureQuery query = new ImsServiceFeatureQuery(name, intentFilter);
+ ImsServiceFeatureQuery query = new ImsServiceFeatureQuery(name, user, intentFilter);
mActiveQueries.put(name, query);
return query.start();
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
index 4abf33f..130fba8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
@@ -27,6 +27,7 @@
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -46,6 +47,7 @@
import android.os.Looper;
import android.os.PersistableBundle;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.os.UserManager;
import android.telephony.CarrierConfigManager;
import android.telephony.TelephonyManager;
@@ -69,8 +71,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.stubbing.Answer;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -95,13 +99,14 @@
"TestCarrier2Pkg", "Carrier2ImsService");
private static final int NUM_MAX_SLOTS = 2;
- private static final String TAG = ImsResolverTest.class.getSimpleName();
+ private static final UserHandle TEST_USER_HANDLE = UserHandle.of(Integer.MAX_VALUE);
// Mocked classes
Context mMockContext;
PackageManager mMockPM;
ImsResolver.SubscriptionManagerProxy mTestSubscriptionManagerProxy;
ImsResolver.TelephonyManagerProxy mTestTelephonyManagerProxy;
+ ImsResolver.ActivityManagerProxy mTestActivityManagerProxy;
CarrierConfigManager mMockCarrierConfigManager;
UserManager mMockUserManager;
ImsResolver.ImsDynamicQueryManagerFactory mMockQueryManagerFactory;
@@ -112,6 +117,7 @@
private BroadcastReceiver mTestPackageBroadcastReceiver;
private BroadcastReceiver mTestCarrierConfigReceiver;
private BroadcastReceiver mTestBootCompleteReceiver;
+ private BroadcastReceiver mTestUserChangedReceiver;
private ImsServiceFeatureQueryManager.Listener mDynamicQueryListener;
private PersistableBundle[] mCarrierConfigs;
private FeatureFlags mFeatureFlags;
@@ -124,12 +130,14 @@
mMockPM = mock(PackageManager.class);
mTestSubscriptionManagerProxy = mock(ImsResolver.SubscriptionManagerProxy.class);
mTestTelephonyManagerProxy = mock(ImsResolver.TelephonyManagerProxy.class);
+ mTestActivityManagerProxy = mock(ImsResolver.ActivityManagerProxy.class);
mMockCarrierConfigManager = mock(CarrierConfigManager.class);
mMockUserManager = mock(UserManager.class);
mMockQueryManagerFactory = mock(ImsResolver.ImsDynamicQueryManagerFactory.class);
mMockQueryManager = mock(ImsServiceFeatureQueryManager.class);
mMockRepo = mock(ImsFeatureBinderRepository.class);
mFeatureFlags = mock(FeatureFlags.class);
+ when(mFeatureFlags.imsResolverUserAware()).thenReturn(true);
}
@After
@@ -411,7 +419,7 @@
ArgumentCaptor<SparseIntArray> arrayCaptor =
ArgumentCaptor.forClass(SparseIntArray.class);
- verify(controller).bind(eq(features), arrayCaptor.capture());
+ verify(controller).bind(eq(mContext.getUser()), eq(features), arrayCaptor.capture());
SparseIntArray slotIdToSubIdMap = arrayCaptor.getValue();
SparseIntArray compareMap = new SparseIntArray();
compareMap.put(0, 0);
@@ -469,11 +477,14 @@
when(mMockQueryManager.isQueryInProgress()).thenReturn(false);
setupDynamicQueryFeatures(TEST_CARRIER_2_DEFAULT_NAME, featuresAll, 1);
- verify(deviceController).bind(eq(featuresDevice), any(SparseIntArray.class));
+ verify(deviceController).bind(eq(mContext.getUser()), eq(featuresDevice),
+ any(SparseIntArray.class));
verify(deviceController, never()).unbind();
- verify(carrierController1).bind(eq(featuresMmTel), any(SparseIntArray.class));
+ verify(carrierController1).bind(eq(mContext.getUser()), eq(featuresMmTel),
+ any(SparseIntArray.class));
verify(carrierController1, never()).unbind();
- verify(carrierController2).bind(eq(featuresRcs), any(SparseIntArray.class));
+ verify(carrierController2).bind(eq(mContext.getUser()), eq(featuresRcs),
+ any(SparseIntArray.class));
verify(carrierController2, never()).unbind();
assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController1.getComponentName());
assertEquals(TEST_CARRIER_2_DEFAULT_NAME, carrierController2.getComponentName());
@@ -521,11 +532,14 @@
when(mMockQueryManager.isQueryInProgress()).thenReturn(false);
setupDynamicQueryFeatures(TEST_CARRIER_2_DEFAULT_NAME, allFeatures, 1);
- verify(deviceController, never()).bind(any(), any(SparseIntArray.class));
+ verify(deviceController, never()).bind(eq(mContext.getUser()), any(),
+ any(SparseIntArray.class));
verify(deviceController, never()).unbind();
- verify(carrierController1).bind(eq(featuresMmTel), any(SparseIntArray.class));
+ verify(carrierController1).bind(eq(mContext.getUser()), eq(featuresMmTel),
+ any(SparseIntArray.class));
verify(carrierController1, never()).unbind();
- verify(carrierController2).bind(eq(featuresRcs), any(SparseIntArray.class));
+ verify(carrierController2).bind(eq(mContext.getUser()), eq(featuresRcs),
+ any(SparseIntArray.class));
verify(carrierController2, never()).unbind();
assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController1.getComponentName());
assertEquals(TEST_CARRIER_2_DEFAULT_NAME, carrierController2.getComponentName());
@@ -553,7 +567,7 @@
startBindCarrierConfigAlreadySet();
setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, features, 1);
- verify(controller).bind(eq(features), any(SparseIntArray.class));
+ verify(controller).bind(eq(mContext.getUser()), eq(features), any(SparseIntArray.class));
verify(controller, never()).unbind();
assertEquals(TEST_CARRIER_DEFAULT_NAME, controller.getComponentName());
}
@@ -582,7 +596,7 @@
// We will not bind with FEATURE_EMERGENCY_MMTEL
features.remove(new ImsFeatureConfiguration.FeatureSlotPair(0,
ImsFeature.FEATURE_EMERGENCY_MMTEL));
- verify(controller).bind(eq(features), any(SparseIntArray.class));
+ verify(controller).bind(eq(mContext.getUser()), eq(features), any(SparseIntArray.class));
verify(controller, never()).unbind();
assertEquals(TEST_CARRIER_DEFAULT_NAME, controller.getComponentName());
}
@@ -606,20 +620,23 @@
setupPackageQuery(info);
ImsServiceController deviceController1 = mock(ImsServiceController.class);
ImsServiceController deviceController2 = mock(ImsServiceController.class);
- setImsServiceControllerFactory(deviceController1, deviceController2, null, null);
+ setImsServiceControllerDDCFactory(deviceController1, deviceController2, null);
// Bind using default features
startBindNoCarrierConfig(1);
HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet1 =
convertToHashSet(featuresController1, 0);
HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet2 =
convertToHashSet(featuresController2, 0);
- verify(deviceController1).bind(eq(featureSet1), any(SparseIntArray.class));
- verify(deviceController2).bind(eq(featureSet2), any(SparseIntArray.class));
+ verify(deviceController1).bind(eq(mContext.getUser()), eq(featureSet1),
+ any(SparseIntArray.class));
+ verify(deviceController2).bind(eq(mContext.getUser()), eq(featureSet2),
+ any(SparseIntArray.class));
// simulate ImsServiceController binding and setup
- mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.FEATURE_EMERGENCY_MMTEL,
+ mTestImsResolver.imsServiceFeatureCreated(0, 0, ImsFeature.FEATURE_EMERGENCY_MMTEL,
deviceController1);
- mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.FEATURE_MMTEL, deviceController1);
- mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.FEATURE_RCS, deviceController2);
+ mTestImsResolver.imsServiceFeatureCreated(0, 0, ImsFeature.FEATURE_MMTEL,
+ deviceController1);
+ mTestImsResolver.imsServiceFeatureCreated(0, 0, ImsFeature.FEATURE_RCS, deviceController2);
mTestImsResolver.enableIms(0 /*slotId*/);
// Verify enableIms is only called once per controller.
@@ -651,7 +668,7 @@
// Bind without emergency calling
startBindCarrierConfigAlreadySet();
setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, features, 1);
- verify(controller).bind(eq(features), any(SparseIntArray.class));
+ verify(controller).bind(eq(mContext.getUser()), eq(features), any(SparseIntArray.class));
verify(controller, never()).unbind();
assertEquals(TEST_CARRIER_DEFAULT_NAME, controller.getComponentName());
@@ -686,8 +703,8 @@
startBindCarrierConfigAlreadySet();
processAllMessages();
- verify(mMockQueryManager, never()).startQuery(any(), any());
- verify(controller, never()).bind(any(), any(SparseIntArray.class));
+ verify(mMockQueryManager, never()).startQuery(any(), any(), any());
+ verify(controller, never()).bind(any(), any(), any(SparseIntArray.class));
verify(controller, never()).unbind();
}
@@ -719,7 +736,7 @@
HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet = convertToHashSet(features, 0);
ArgumentCaptor<SparseIntArray> arrayCaptor =
ArgumentCaptor.forClass(SparseIntArray.class);
- verify(controller).bind(eq(featureSet), arrayCaptor.capture());
+ verify(controller).bind(eq(mContext.getUser()), eq(featureSet), arrayCaptor.capture());
SparseIntArray slotIdToSubIdMap = arrayCaptor.getValue();
SparseIntArray compareMap = new SparseIntArray();
compareMap.put(0, 0);
@@ -729,7 +746,7 @@
}
verify(controller, never()).unbind();
- verify(mMockQueryManager, never()).startQuery(any(), any());
+ verify(mMockQueryManager, never()).startQuery(any(), any(), any());
assertEquals(TEST_DEVICE_DEFAULT_NAME, controller.getComponentName());
}
@@ -763,7 +780,7 @@
featureSet.addAll(convertToHashSet(features, 1));
ArgumentCaptor<SparseIntArray> arrayCaptor =
ArgumentCaptor.forClass(SparseIntArray.class);
- verify(controller).bind(eq(featureSet), arrayCaptor.capture());
+ verify(controller).bind(eq(mContext.getUser()), eq(featureSet), arrayCaptor.capture());
SparseIntArray slotIdToSubIdMap = arrayCaptor.getValue();
assertEquals(slotIdToSubIdMap.size(), 2);
SparseIntArray compareMap = new SparseIntArray();
@@ -774,7 +791,7 @@
assertEquals(slotIdToSubIdMap.get(i), compareMap.get(i));
}
verify(controller, never()).unbind();
- verify(mMockQueryManager, never()).startQuery(any(), any());
+ verify(mMockQueryManager, never()).startQuery(any(), any(), any());
assertEquals(TEST_DEVICE_DEFAULT_NAME, controller.getComponentName());
// Change number of SIMs and verify the features in the ImsServiceController are changed
@@ -823,7 +840,7 @@
ArgumentCaptor<SparseIntArray> arrayCaptor =
ArgumentCaptor.forClass(SparseIntArray.class);
- verify(controller).bind(eq(featureSet), arrayCaptor.capture());
+ verify(controller).bind(eq(mContext.getUser()), eq(featureSet), arrayCaptor.capture());
SparseIntArray slotIdToSubIdMap = arrayCaptor.getValue();
assertEquals(slotIdToSubIdMap.size(), 1);
SparseIntArray compareMap = new SparseIntArray();
@@ -833,7 +850,7 @@
assertEquals(slotIdToSubIdMap.get(i), compareMap.get(i));
}
verify(controller, never()).unbind();
- verify(mMockQueryManager, never()).startQuery(any(), any());
+ verify(mMockQueryManager, never()).startQuery(any(), any(), any());
assertEquals(TEST_DEVICE_DEFAULT_NAME, controller.getComponentName());
// Change number of SIMs and verify the features in the ImsServiceController are changed
@@ -895,17 +912,19 @@
processAllMessages();
// ensure that startQuery was called
verify(mMockQueryManager, times(1)).startQuery(eq(TEST_DEVICE_DEFAULT_NAME),
- any(String.class));
+ any(UserHandle.class), any(String.class));
verify(mMockQueryManager, times(1)).startQuery(eq(TEST_DEVICE2_DEFAULT_NAME),
- any(String.class));
+ any(UserHandle.class), any(String.class));
mDynamicQueryListener.onComplete(TEST_DEVICE_DEFAULT_NAME, deviceFeatures1);
mDynamicQueryListener.onComplete(TEST_DEVICE2_DEFAULT_NAME, deviceFeatures2);
processAllMessages();
- verify(deviceController, times(2)).bind(eq(deviceFeatures1), any(SparseIntArray.class));
- verify(deviceController2, times(1)).bind(eq(deviceFeatures2), any(SparseIntArray.class));
+ verify(deviceController, times(2)).bind(eq(mContext.getUser()), eq(deviceFeatures1),
+ any(SparseIntArray.class));
+ verify(deviceController2, times(1)).bind(eq(mContext.getUser()), eq(deviceFeatures2),
+ any(SparseIntArray.class));
}
/**
@@ -942,7 +961,8 @@
setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
// Verify that all features that have been defined for the carrier override are bound
- verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class));
+ verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures),
+ any(SparseIntArray.class));
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
@@ -950,7 +970,8 @@
HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
convertToHashSet(deviceFeatures, 0);
deviceFeatureSet.removeAll(carrierFeatures);
- verify(deviceController).bind(eq(deviceFeatureSet), any(SparseIntArray.class));
+ verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet),
+ any(SparseIntArray.class));
verify(deviceController, never()).unbind();
assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
@@ -1006,7 +1027,8 @@
setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
// Verify that all features that have been defined for the carrier override are bound
- verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class));
+ verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures),
+ any(SparseIntArray.class));
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
@@ -1015,7 +1037,8 @@
convertToHashSet(deviceFeatures, 0);
deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 1));
deviceFeatureSet.removeAll(carrierFeatures);
- verify(deviceController).bind(eq(deviceFeatureSet), any(SparseIntArray.class));
+ verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet),
+ any(SparseIntArray.class));
verify(deviceController, never()).unbind();
assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
@@ -1062,9 +1085,9 @@
HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet = convertToHashSet(features, 0);
// There is no carrier override set, so make sure that the ImsServiceController binds
// to all SIMs.
- verify(controller).bind(eq(featureSet), any(SparseIntArray.class));
+ verify(controller).bind(eq(mContext.getUser()), eq(featureSet), any(SparseIntArray.class));
verify(controller, never()).unbind();
- verify(mMockQueryManager, never()).startQuery(any(), any());
+ verify(mMockQueryManager, never()).startQuery(any(), any(), any());
assertEquals(TEST_DEVICE_DEFAULT_NAME, controller.getComponentName());
}
@@ -1100,7 +1123,8 @@
setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
// Verify that all features that have been defined for the carrier override are bound
- verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class));
+ verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures),
+ any(SparseIntArray.class));
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
@@ -1108,7 +1132,8 @@
HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
convertToHashSet(deviceFeatures, 0);
deviceFeatureSet.removeAll(carrierFeatures);
- verify(deviceController).bind(eq(deviceFeatureSet), any(SparseIntArray.class));
+ verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet),
+ any(SparseIntArray.class));
verify(deviceController, never()).unbind();
assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
}
@@ -1127,11 +1152,11 @@
// Callback from mock ImsServiceControllers
// All features on slot 1 should be the device default
- mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.FEATURE_MMTEL, deviceController);
- mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.FEATURE_RCS, deviceController);
- mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.FEATURE_MMTEL, deviceController);
+ mTestImsResolver.imsServiceFeatureCreated(1, 1, ImsFeature.FEATURE_MMTEL, deviceController);
+ mTestImsResolver.imsServiceFeatureCreated(1, 1, ImsFeature.FEATURE_RCS, deviceController);
+ mTestImsResolver.imsServiceFeatureCreated(0, 0, ImsFeature.FEATURE_MMTEL, deviceController);
// The carrier override contains this feature
- mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.FEATURE_RCS, carrierController);
+ mTestImsResolver.imsServiceFeatureCreated(0, 0, ImsFeature.FEATURE_RCS, carrierController);
}
/**
@@ -1155,7 +1180,7 @@
HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet =
convertToHashSet(features, 0);
featureSet.addAll(convertToHashSet(features, 1));
- verify(controller).bind(eq(featureSet), any(SparseIntArray.class));
+ verify(controller).bind(eq(mContext.getUser()), eq(featureSet), any(SparseIntArray.class));
// add RCS to features list
Set<String> newFeatures = new HashSet<>(features);
@@ -1191,7 +1216,7 @@
setupPackageQuery(info);
ImsServiceController deviceController1 = mock(ImsServiceController.class);
ImsServiceController deviceController2 = mock(ImsServiceController.class);
- setImsServiceControllerFactory(deviceController1, deviceController2, null, null);
+ setImsServiceControllerDDCFactory(deviceController1, deviceController2, null);
// Bind using default features
startBindNoCarrierConfig(2);
HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet1 =
@@ -1200,8 +1225,10 @@
HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet2 =
convertToHashSet(featuresController2, 0);
featureSet2.addAll(convertToHashSet(featuresController2, 1));
- verify(deviceController1).bind(eq(featureSet1), any(SparseIntArray.class));
- verify(deviceController2).bind(eq(featureSet2), any(SparseIntArray.class));
+ verify(deviceController1).bind(eq(mContext.getUser()), eq(featureSet1),
+ any(SparseIntArray.class));
+ verify(deviceController2).bind(eq(mContext.getUser()), eq(featureSet2),
+ any(SparseIntArray.class));
// add RCS to features list for device 1
Set<String> newFeatures1 = new HashSet<>(featuresController1);
@@ -1239,7 +1266,7 @@
HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet =
convertToHashSet(features, 0);
featureSet.addAll(convertToHashSet(features, 1));
- verify(controller).bind(eq(featureSet), any(SparseIntArray.class));
+ verify(controller).bind(eq(mContext.getUser()), eq(featureSet), any(SparseIntArray.class));
// add RCS to features list
Set<String> newFeatures = new HashSet<>(features);
@@ -1288,7 +1315,8 @@
setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
// Verify that all features that have been defined for the carrier override are bound
- verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class));
+ verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures),
+ any(SparseIntArray.class));
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
@@ -1297,7 +1325,8 @@
convertToHashSet(deviceFeatures, 1);
deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
deviceFeatureSet.removeAll(carrierFeatures);
- verify(deviceController).bind(eq(deviceFeatureSet), any(SparseIntArray.class));
+ verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet),
+ any(SparseIntArray.class));
verify(deviceController, never()).unbind();
assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
@@ -1356,13 +1385,13 @@
ImsServiceController deviceController1 = mock(ImsServiceController.class);
ImsServiceController deviceController2 = mock(ImsServiceController.class);
ImsServiceController carrierController = mock(ImsServiceController.class);
- setImsServiceControllerFactory(deviceController1, deviceController2, carrierController,
- null);
+ setImsServiceControllerDDCFactory(deviceController1, deviceController2, carrierController);
startBindCarrierConfigAlreadySet();
setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
// Verify that all features that have been defined for the carrier override are bound
- verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class));
+ verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures),
+ any(SparseIntArray.class));
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
@@ -1370,13 +1399,16 @@
HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet1 =
convertToHashSet(deviceFeatures1, 1);
deviceFeatureSet1.removeAll(carrierFeatures);
- verify(deviceController1).bind(eq(deviceFeatureSet1), any(SparseIntArray.class));
+ verify(deviceController1).bind(eq(mContext.getUser()), eq(deviceFeatureSet1),
+ any(SparseIntArray.class));
verify(deviceController1, never()).unbind();
HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet2 =
convertToHashSet(deviceFeatures2, 0);
deviceFeatureSet2.addAll(convertToHashSet(deviceFeatures2, 1));
+ deviceFeatureSet2.addAll(convertToHashSet(deviceFeatures2, 1));
deviceFeatureSet2.removeAll(carrierFeatures);
- verify(deviceController2).bind(eq(deviceFeatureSet2), any(SparseIntArray.class));
+ verify(deviceController2).bind(eq(mContext.getUser()), eq(deviceFeatureSet2),
+ any(SparseIntArray.class));
verify(deviceController2, never()).unbind();
assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController1.getComponentName());
assertEquals(TEST_DEVICE2_DEFAULT_NAME, deviceController2.getComponentName());
@@ -1392,7 +1424,7 @@
verify(carrierController).changeImsServiceFeatures(eq(carrierFeatures),
any(SparseIntArray.class));
deviceFeatureSet1.removeAll(carrierFeatures);
- verify(deviceController1, times(2)).changeImsServiceFeatures(eq(deviceFeatureSet1),
+ verify(deviceController1).changeImsServiceFeatures(eq(deviceFeatureSet1),
any(SparseIntArray.class));
deviceFeatureSet2.removeAll(carrierFeatures);
verify(deviceController2).changeImsServiceFeatures(eq(deviceFeatureSet2),
@@ -1429,7 +1461,8 @@
startBindCarrierConfigAlreadySet();
setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
// Verify that all features that have been defined for the carrier override are bound
- verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class));
+ verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures),
+ any(SparseIntArray.class));
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
@@ -1438,7 +1471,8 @@
convertToHashSet(deviceFeatures, 1);
deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
deviceFeatureSet.removeAll(carrierFeatures);
- verify(deviceController).bind(eq(deviceFeatureSet), any(SparseIntArray.class));
+ verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet),
+ any(SparseIntArray.class));
verify(deviceController, never()).unbind();
assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
@@ -1497,14 +1531,20 @@
setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
// Verify that all features that have been defined for the carrier override are bound
- verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class));
+ verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures),
+ any(SparseIntArray.class));
// device features change
HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
convertToHashSet(deviceFeatures, 1);
deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
deviceFeatureSet.removeAll(carrierFeatures);
- verify(deviceController).changeImsServiceFeatures(eq(deviceFeatureSet),
- any(SparseIntArray.class));
+ if (mFeatureFlags.imsResolverUserAware()) {
+ verify(deviceController).changeImsServiceFeatures(eq(deviceFeatureSet),
+ any(SparseIntArray.class));
+ } else {
+ verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet),
+ any(SparseIntArray.class));
+ }
}
/**
@@ -1644,7 +1684,8 @@
assertNotNull(mTestImsResolver.getImsServiceInfoFromCache(
TEST_CARRIER_DEFAULT_NAME.getPackageName()));
// Verify that carrier 2 is bound
- verify(carrierController2).bind(eq(carrierFeatures2), any(SparseIntArray.class));
+ verify(carrierController2).bind(eq(mContext.getUser()), eq(carrierFeatures2),
+ any(SparseIntArray.class));
assertNotNull(mTestImsResolver.getImsServiceInfoFromCache(
TEST_CARRIER_2_DEFAULT_NAME.getPackageName()));
// device features change to accommodate for the features carrier 2 lacks
@@ -1692,7 +1733,8 @@
setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
// Verify that all features that have been defined for the carrier override are bound
- verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class));
+ verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures),
+ any(SparseIntArray.class));
// device features change
HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
convertToHashSet(deviceFeatures, 1);
@@ -1733,13 +1775,15 @@
setupDynamicQueryFeaturesFailure(TEST_CARRIER_DEFAULT_NAME, 1);
// Verify that a bind never occurs for the carrier controller.
- verify(carrierController, never()).bind(any(), any(SparseIntArray.class));
+ verify(carrierController, never()).bind(eq(mContext.getUser()), any(),
+ any(SparseIntArray.class));
verify(carrierController, never()).unbind();
// Verify that all features are used to bind to the device ImsService since the carrier
// ImsService failed to bind properly.
HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
convertToHashSet(deviceFeatures, 0);
- verify(deviceController).bind(eq(deviceFeatureSet), any(SparseIntArray.class));
+ verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet),
+ any(SparseIntArray.class));
verify(deviceController, never()).unbind();
assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
}
@@ -1774,18 +1818,21 @@
setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
// Verify that a bind never occurs for the carrier controller.
- verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class));
+ verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures),
+ any(SparseIntArray.class));
verify(carrierController, never()).unbind();
// 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<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
convertToHashSet(deviceFeatures, 0);
deviceFeatureSet.removeAll(carrierFeatures);
- verify(deviceController).bind(eq(deviceFeatureSet), any(SparseIntArray.class));
+ verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet),
+ any(SparseIntArray.class));
verify(deviceController, never()).unbind();
assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
- mTestImsResolver.imsServiceBindPermanentError(TEST_CARRIER_DEFAULT_NAME);
+ mTestImsResolver.imsServiceBindPermanentError(TEST_CARRIER_DEFAULT_NAME,
+ mContext.getUser());
processAllMessages();
verify(carrierController).unbind();
// Verify that the device ImsService features are changed to include the ones previously
@@ -1815,7 +1862,7 @@
setupPackageQuery(info);
ImsServiceController deviceController1 = mock(ImsServiceController.class);
ImsServiceController deviceController2 = mock(ImsServiceController.class);
- setImsServiceControllerFactory(deviceController1, deviceController2, null, null);
+ setImsServiceControllerDDCFactory(deviceController1, deviceController2, null);
startBindNoCarrierConfig(1);
processAllMessages();
@@ -1825,11 +1872,12 @@
HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureResultSet =
convertToHashSet(featureResult, 0);
featureResultSet.addAll(convertToHashSet(featureResult, 1));
- verify(deviceController1).bind(eq(featureResultSet), any(SparseIntArray.class));
+ verify(deviceController1).bind(eq(mContext.getUser()), eq(featureResultSet),
+ any(SparseIntArray.class));
verify(deviceController1, never()).unbind();
- verify(deviceController2, never()).bind(any(), any(SparseIntArray.class));
+ verify(deviceController2, never()).bind(any(), any(), any(SparseIntArray.class));
verify(deviceController2, never()).unbind();
- verify(mMockQueryManager, never()).startQuery(any(), any());
+ verify(mMockQueryManager, never()).startQuery(any(), any(), any());
assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController1.getComponentName());
}
@@ -1852,7 +1900,7 @@
setupPackageQuery(info);
ImsServiceController deviceController1 = mock(ImsServiceController.class);
ImsServiceController deviceController2 = mock(ImsServiceController.class);
- setImsServiceControllerFactory(deviceController1, deviceController2, null, null);
+ setImsServiceControllerDDCFactory(deviceController1, deviceController2, null);
startBindNoCarrierConfig(1);
processAllMessages();
@@ -1862,11 +1910,12 @@
HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureResultSet =
convertToHashSet(featureResult, 0);
featureResultSet.addAll(convertToHashSet(featureResult, 1));
- verify(deviceController1).bind(eq(featureResultSet), any(SparseIntArray.class));
+ verify(deviceController1).bind(eq(mContext.getUser()), eq(featureResultSet),
+ any(SparseIntArray.class));
verify(deviceController1, never()).unbind();
- verify(deviceController2, never()).bind(any(), any(SparseIntArray.class));
+ verify(deviceController2, never()).bind(any(), any(), any(SparseIntArray.class));
verify(deviceController2, never()).unbind();
- verify(mMockQueryManager, never()).startQuery(any(), any());
+ verify(mMockQueryManager, never()).startQuery(any(), any(), any());
assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController1.getComponentName());
}
@@ -1893,7 +1942,7 @@
setupPackageQuery(info);
ImsServiceController deviceController1 = mock(ImsServiceController.class);
ImsServiceController deviceController2 = mock(ImsServiceController.class);
- setImsServiceControllerFactory(deviceController1, deviceController2, null, null);
+ setImsServiceControllerDDCFactory(deviceController1, deviceController2, null);
startBindNoCarrierConfig(1);
processAllMessages();
@@ -1904,11 +1953,13 @@
HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet2 =
convertToHashSet(features2, 0);
featureSet2.addAll(convertToHashSet(features2, 1));
- verify(deviceController1).bind(eq(featureSet1), any(SparseIntArray.class));
+ verify(deviceController1).bind(eq(mContext.getUser()), eq(featureSet1),
+ any(SparseIntArray.class));
verify(deviceController1, never()).unbind();
- verify(deviceController2).bind(eq(featureSet2), any(SparseIntArray.class));
+ verify(deviceController2).bind(eq(mContext.getUser()), eq(featureSet2),
+ any(SparseIntArray.class));
verify(deviceController2, never()).unbind();
- verify(mMockQueryManager, never()).startQuery(any(), any());
+ verify(mMockQueryManager, never()).startQuery(any(), any(), any());
assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController1.getComponentName());
assertEquals(TEST_DEVICE2_DEFAULT_NAME, deviceController2.getComponentName());
}
@@ -1934,16 +1985,134 @@
setupPackageQuery(info);
ImsServiceController deviceController1 = mock(ImsServiceController.class);
ImsServiceController deviceController2 = mock(ImsServiceController.class);
- setImsServiceControllerFactory(deviceController1, deviceController2, null, null);
+ setImsServiceControllerDDCFactory(deviceController1, deviceController2, null);
startBindNoCarrierConfig(1);
processAllMessages();
- verify(deviceController1, never()).bind(any(), any(SparseIntArray.class));
+ verify(deviceController1, never()).bind(any(), any(), any(SparseIntArray.class));
verify(deviceController1, never()).unbind();
- verify(deviceController2, never()).bind(any(), any(SparseIntArray.class));
+ verify(deviceController2, never()).bind(any(), any(), any(SparseIntArray.class));
verify(deviceController2, never()).unbind();
- verify(mMockQueryManager, never()).startQuery(any(), any());
+ verify(mMockQueryManager, never()).startQuery(any(), any(), any());
+ }
+
+ /**
+ * Change the current active user while having ImsServices in system user. The ImsService config
+ * should not change.
+ */
+ @Test
+ @SmallTest
+ public void testChangeCurrentUserServicesInSystem() throws RemoteException {
+ if (!mFeatureFlags.imsResolverUserAware()) {
+ return;
+ }
+ setupResolver(1 /*numSlots*/, TEST_DEVICE_DEFAULT_NAME.getPackageName(),
+ TEST_DEVICE_DEFAULT_NAME.getPackageName());
+ List<ResolveInfo> info = new ArrayList<>();
+ Set<String> deviceFeatures = new HashSet<>();
+ deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+ deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+ // Set the carrier override package for slot 0
+ setConfigCarrierStringMmTelRcs(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures = new HashSet<>();
+ // Carrier service doesn't support the voice feature.
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
+ info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
+ info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
+ // Use device default package, which will load the ImsService that the device provides
+ setupPackageQuery(info);
+
+ ImsServiceController deviceController = mock(ImsServiceController.class);
+ ImsServiceController carrierController = mock(ImsServiceController.class);
+ setImsServiceControllerFactory(deviceController, carrierController);
+
+ startBindCarrierConfigAlreadySet();
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
+
+ // Perform a user switch
+ userChanged(TEST_USER_HANDLE);
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 2);
+
+
+ // Verify that all features that have been defined for the device/carrier override are bound
+ // and are not changed when the user changes.
+ verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures),
+ any(SparseIntArray.class));
+ verify(carrierController, atLeastOnce()).changeImsServiceFeatures(eq(carrierFeatures),
+ any(SparseIntArray.class));
+ verify(carrierController, never()).unbind();
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
+ convertToHashSet(deviceFeatures, 0);
+ deviceFeatureSet.removeAll(carrierFeatures);
+ verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet),
+ any(SparseIntArray.class));
+ verify(deviceController, atLeastOnce()).changeImsServiceFeatures(eq(deviceFeatureSet),
+ any(SparseIntArray.class));
+ verify(deviceController, never()).unbind();
+ }
+
+ /**
+ * Change the current active user while having a carrier ImsService installed for second user.
+ * The features should change when the current user changes to the second user and back.
+ */
+ @Test
+ @SmallTest
+ public void testChangeCurrentUserCarrierInSecondUser() throws RemoteException {
+ if (!mFeatureFlags.imsResolverUserAware()) {
+ return;
+ }
+ setupResolver(1 /*numSlots*/, TEST_DEVICE_DEFAULT_NAME.getPackageName(),
+ TEST_DEVICE_DEFAULT_NAME.getPackageName());
+ Set<String> deviceFeatures = new HashSet<>();
+ deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+ deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+ // Set the carrier override package for slot 0
+ setConfigCarrierStringMmTelRcs(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures = new HashSet<>();
+ // Carrier service doesn't support the voice feature.
+ carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
+ // Use device default package, which will load the ImsService that the device provides
+ setupPackageQuery(Collections.singletonList(
+ getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true)));
+ setupPackageQueryForUser(Collections.singletonList(
+ getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true)),
+ TEST_USER_HANDLE);
+
+ ImsServiceController deviceController = mock(ImsServiceController.class);
+ ImsServiceController carrierController = mock(ImsServiceController.class);
+ setImsServiceControllerFactory(deviceController, carrierController);
+
+ startBindCarrierConfigAlreadySet();
+
+ verify(carrierController, never()).bind(eq(mContext.getUser()), any(),
+ any(SparseIntArray.class));
+ verify(deviceController).bind(eq(mContext.getUser()),
+ eq(convertToHashSet(deviceFeatures, 0)), any(SparseIntArray.class));
+
+ // Perform a user switch
+ setBoundImsServiceControllerUser(carrierController, TEST_USER_HANDLE);
+ userChanged(TEST_USER_HANDLE);
+ setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
+
+
+ // Verify the carrier controller was bound only when the user changed
+ verify(carrierController).bind(eq(TEST_USER_HANDLE), eq(carrierFeatures),
+ any(SparseIntArray.class));
+ verify(carrierController, never()).changeImsServiceFeatures(eq(carrierFeatures),
+ any(SparseIntArray.class));
+ verify(carrierController, never()).unbind();
+
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
+ convertToHashSet(deviceFeatures, 0);
+ deviceFeatureSet.removeAll(carrierFeatures);
+ verify(deviceController).changeImsServiceFeatures(eq(deviceFeatureSet),
+ any(SparseIntArray.class));
+ verify(deviceController, never()).unbind();
+ }
+
+ private void setCurrentUser(UserHandle handle) {
+ when(mTestActivityManagerProxy.getCurrentUser()).thenReturn(handle);
}
private void setupResolver(int numSlots, String deviceMmTelPkgName,
@@ -1970,12 +2139,15 @@
when(mTestTelephonyManagerProxy.getSimState(any(Context.class), eq(i))).thenReturn(
TelephonyManager.SIM_STATE_READY);
}
+ when(mMockContext.getUser()).thenReturn(mContext.getUser());
+ when(mTestActivityManagerProxy.getCurrentUser()).thenReturn(mContext.getUser());
mTestImsResolver = new ImsResolver(mMockContext, deviceMmTelPkgName, deviceRcsPkgName,
numSlots, mMockRepo, Looper.myLooper(), mFeatureFlags);
mTestImsResolver.setSubscriptionManagerProxy(mTestSubscriptionManagerProxy);
mTestImsResolver.setTelephonyManagerProxy(mTestTelephonyManagerProxy);
+ mTestImsResolver.setActivityManagerProxy(mTestActivityManagerProxy);
when(mMockQueryManagerFactory.create(any(Context.class),
any(ImsServiceFeatureQueryManager.Listener.class))).thenReturn(mMockQueryManager);
mTestImsResolver.setImsDynamicQueryManagerFactory(mMockQueryManagerFactory);
@@ -1983,24 +2155,55 @@
}
private void setupPackageQuery(List<ResolveInfo> infos) {
- // Only return info if not using the compat argument
- when(mMockPM.queryIntentServicesAsUser(
+ doAnswer((Answer<List<ResolveInfo>>) invocation -> {
+ Intent intent = (Intent) invocation.getArguments()[0];
+ String pkg = intent.getPackage();
+ if (pkg == null) {
+ return infos;
+ } else {
+ for (ResolveInfo info : infos) {
+ if (pkg.equals(info.serviceInfo.packageName)) {
+ return Collections.singletonList(info);
+ }
+ }
+ }
+ return Collections.emptyList();
+ }).when(mMockPM).queryIntentServicesAsUser(
+ // Only return info if not using the compat argument
argThat(argument -> ImsService.SERVICE_INTERFACE.equals(argument.getAction())),
- anyInt(), any())).thenReturn(infos);
+ anyInt(), any());
+ }
+
+ private void setupPackageQueryForUser(List<ResolveInfo> infos, UserHandle user) {
+ doAnswer((Answer<List<ResolveInfo>>) invocation -> {
+ Intent intent = (Intent) invocation.getArguments()[0];
+ String pkg = intent.getPackage();
+ if (pkg == null) {
+ return infos;
+ } else {
+ for (ResolveInfo info : infos) {
+ if (pkg.equals(info.serviceInfo.packageName)) {
+ return Collections.singletonList(info);
+ }
+ }
+ }
+ return Collections.emptyList();
+ }).when(mMockPM).queryIntentServicesAsUser(
+ // Only return info if not using the compat argument
+ argThat(argument -> ImsService.SERVICE_INTERFACE.equals(argument.getAction())),
+ anyInt(), eq(user));
}
private void setupPackageQuery(ComponentName name, Set<String> features,
boolean isPermissionGranted) {
List<ResolveInfo> info = new ArrayList<>();
info.add(getResolveInfo(name, features, isPermissionGranted));
- // Only return info if not using the compat argument
- when(mMockPM.queryIntentServicesAsUser(
- argThat(argument -> ImsService.SERVICE_INTERFACE.equals(argument.getAction())),
- anyInt(), any())).thenReturn(info);
+ setupPackageQuery(info);
}
private ImsServiceController setupController() {
ImsServiceController controller = mock(ImsServiceController.class);
+ when(controller.getBoundUser()).thenReturn(mContext.getUser());
mTestImsResolver.setImsServiceControllerFactory(
new ImsResolver.ImsServiceControllerFactory() {
@Override
@@ -2028,15 +2231,25 @@
processAllMessages();
ArgumentCaptor<BroadcastReceiver> receiversCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
- verify(mMockContext, times(3)).registerReceiver(receiversCaptor.capture(), any());
- mTestPackageBroadcastReceiver = receiversCaptor.getAllValues().get(0);
- mTestCarrierConfigReceiver = receiversCaptor.getAllValues().get(1);
- mTestBootCompleteReceiver = receiversCaptor.getAllValues().get(2);
+ if (!mFeatureFlags.imsResolverUserAware()) {
+ verify(mMockContext, times(3)).registerReceiver(receiversCaptor.capture(), any());
+ mTestPackageBroadcastReceiver = receiversCaptor.getAllValues().get(0);
+ mTestCarrierConfigReceiver = receiversCaptor.getAllValues().get(1);
+ mTestBootCompleteReceiver = receiversCaptor.getAllValues().get(2);
+ } else {
+ verify(mMockContext, times(4)).registerReceiver(receiversCaptor.capture(), any());
+ mTestPackageBroadcastReceiver = receiversCaptor.getAllValues().get(0);
+ mTestUserChangedReceiver = receiversCaptor.getAllValues().get(1);
+ mTestCarrierConfigReceiver = receiversCaptor.getAllValues().get(2);
+ mTestBootCompleteReceiver = receiversCaptor.getAllValues().get(3);
+
+ }
ArgumentCaptor<ImsServiceFeatureQueryManager.Listener> queryManagerCaptor =
ArgumentCaptor.forClass(ImsServiceFeatureQueryManager.Listener.class);
verify(mMockQueryManagerFactory).create(any(Context.class), queryManagerCaptor.capture());
mDynamicQueryListener = queryManagerCaptor.getValue();
- when(mMockQueryManager.startQuery(any(ComponentName.class), any(String.class)))
+ when(mMockQueryManager.startQuery(any(ComponentName.class), any(UserHandle.class),
+ any(String.class)))
.thenReturn(true);
processAllMessages();
}
@@ -2050,10 +2263,18 @@
processAllMessages();
ArgumentCaptor<BroadcastReceiver> receiversCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
- verify(mMockContext, times(3)).registerReceiver(receiversCaptor.capture(), any());
- mTestPackageBroadcastReceiver = receiversCaptor.getAllValues().get(0);
- mTestCarrierConfigReceiver = receiversCaptor.getAllValues().get(1);
- mTestBootCompleteReceiver = receiversCaptor.getAllValues().get(2);
+ if (!mFeatureFlags.imsResolverUserAware()) {
+ verify(mMockContext, times(3)).registerReceiver(receiversCaptor.capture(), any());
+ mTestPackageBroadcastReceiver = receiversCaptor.getAllValues().get(0);
+ mTestCarrierConfigReceiver = receiversCaptor.getAllValues().get(1);
+ mTestBootCompleteReceiver = receiversCaptor.getAllValues().get(2);
+ } else {
+ verify(mMockContext, times(4)).registerReceiver(receiversCaptor.capture(), any());
+ mTestPackageBroadcastReceiver = receiversCaptor.getAllValues().get(0);
+ mTestUserChangedReceiver = receiversCaptor.getAllValues().get(1);
+ mTestCarrierConfigReceiver = receiversCaptor.getAllValues().get(2);
+ mTestBootCompleteReceiver = receiversCaptor.getAllValues().get(3);
+ }
ArgumentCaptor<ImsServiceFeatureQueryManager.Listener> queryManagerCaptor =
ArgumentCaptor.forClass(ImsServiceFeatureQueryManager.Listener.class);
verify(mMockQueryManagerFactory).create(any(Context.class), queryManagerCaptor.capture());
@@ -2069,7 +2290,8 @@
HashSet<ImsFeatureConfiguration.FeatureSlotPair> features, int times) {
processAllMessages();
// ensure that startQuery was called
- verify(mMockQueryManager, times(times)).startQuery(eq(name), any(String.class));
+ verify(mMockQueryManager, times(times)).startQuery(eq(name), any(UserHandle.class),
+ any(String.class));
mDynamicQueryListener.onComplete(name, features);
processAllMessages();
}
@@ -2078,7 +2300,8 @@
HashSet<ImsFeatureConfiguration.FeatureSlotPair> features, int times) {
processAllFutureMessages();
// ensure that startQuery was called
- verify(mMockQueryManager, times(times)).startQuery(eq(name), any(String.class));
+ verify(mMockQueryManager, times(times)).startQuery(eq(name), any(UserHandle.class),
+ any(String.class));
mDynamicQueryListener.onComplete(name, features);
processAllMessages();
}
@@ -2086,8 +2309,19 @@
private void setupDynamicQueryFeaturesFailure(ComponentName name, int times) {
processAllMessages();
// ensure that startQuery was called
- verify(mMockQueryManager, times(times)).startQuery(eq(name), any(String.class));
- mDynamicQueryListener.onPermanentError(name);
+ verify(mMockQueryManager, times(times)).startQuery(eq(name), any(UserHandle.class),
+ any(String.class));
+ mDynamicQueryListener.onPermanentError(name, mContext.getUser());
+ processAllMessages();
+ }
+
+ public void userChanged(UserHandle newUser) {
+ setCurrentUser(newUser);
+ // Tell the package manager that a new device feature is installed
+ Intent userSwitchedIntent = new Intent();
+ userSwitchedIntent.setAction(Intent.ACTION_USER_SWITCHED);
+ userSwitchedIntent.putExtra(Intent.EXTRA_USER, newUser);
+ mTestUserChangedReceiver.onReceive(null, userSwitchedIntent);
processAllMessages();
}
@@ -2110,7 +2344,14 @@
processAllMessages();
}
+ private void setBoundImsServiceControllerUser(ImsServiceController controller,
+ UserHandle handle) {
+ when(controller.getBoundUser()).thenReturn(handle);
+ }
+
private void setImsServiceControllerFactory(Map<String, ImsServiceController> controllerMap) {
+ controllerMap.values()
+ .forEach(c -> setBoundImsServiceControllerUser(c, mContext.getUser()));
mTestImsResolver.setImsServiceControllerFactory(
new ImsResolver.ImsServiceControllerFactory() {
@Override
@@ -2129,6 +2370,8 @@
private void setImsServiceControllerFactory(ImsServiceController deviceController,
ImsServiceController carrierController) {
+ setBoundImsServiceControllerUser(deviceController, mContext.getUser());
+ setBoundImsServiceControllerUser(carrierController, mContext.getUser());
mTestImsResolver.setImsServiceControllerFactory(
new ImsResolver.ImsServiceControllerFactory() {
@Override
@@ -2156,6 +2399,9 @@
private void setImsServiceControllerFactory(ImsServiceController deviceController,
ImsServiceController carrierController1, ImsServiceController carrierController2) {
+ setBoundImsServiceControllerUser(deviceController, mContext.getUser());
+ setBoundImsServiceControllerUser(carrierController1, mContext.getUser());
+ setBoundImsServiceControllerUser(carrierController2, mContext.getUser());
mTestImsResolver.setImsServiceControllerFactory(
new ImsResolver.ImsServiceControllerFactory() {
@Override
@@ -2185,9 +2431,13 @@
});
}
- private void setImsServiceControllerFactory(ImsServiceController deviceController1,
- ImsServiceController deviceController2, ImsServiceController carrierController1,
- ImsServiceController carrierController2) {
+ private void setImsServiceControllerDDCFactory(ImsServiceController deviceController1,
+ ImsServiceController deviceController2, ImsServiceController carrierController1) {
+ setBoundImsServiceControllerUser(deviceController1, mContext.getUser());
+ setBoundImsServiceControllerUser(deviceController2, mContext.getUser());
+ if (carrierController1 != null) {
+ setBoundImsServiceControllerUser(carrierController1, mContext.getUser());
+ }
mTestImsResolver.setImsServiceControllerFactory(
new ImsResolver.ImsServiceControllerFactory() {
@Override
@@ -2211,10 +2461,6 @@
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;
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerCompatTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerCompatTest.java
index 2544fc1..aa6bd7f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerCompatTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerCompatTest.java
@@ -108,9 +108,9 @@
mMmTelCompatAdapterSpy = spy(new MmTelFeatureCompatAdapter(mMockContext, SLOT_0,
mMockMmTelInterfaceAdapter));
mTestImsServiceController = new ImsServiceControllerCompat(mMockContext, mTestComponentName,
- mMockCallbacks, mHandler, REBIND_RETRY, mRepo,
+ mMockCallbacks, mHandler, REBIND_RETRY, mRepo,
(a, b, c) -> mMmTelCompatAdapterSpy);
- when(mMockContext.bindService(any(), any(), anyInt())).thenReturn(true);
+ when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true);
when(mMockServiceControllerBinder.createMMTelFeature(anyInt()))
.thenReturn(mMockRemoteMMTelFeature);
when(mMockRemoteMMTelFeature.getConfigInterface()).thenReturn(mMockImsConfig);
@@ -146,8 +146,8 @@
verify(mMockServiceControllerBinder).createMMTelFeature(SLOT_0);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_1),
+ eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
validateMmTelFeatureContainerExists(SLOT_0);
// Remove the feature
conn.onBindingDied(mTestComponentName);
@@ -191,8 +191,9 @@
SparseIntArray slotIdToSubIdMap) {
ArgumentCaptor<ServiceConnection> serviceCaptor =
ArgumentCaptor.forClass(ServiceConnection.class);
- assertTrue(mTestImsServiceController.bind(testFeatures, slotIdToSubIdMap));
- verify(mMockContext).bindService(any(), serviceCaptor.capture(), anyInt());
+ assertTrue(mTestImsServiceController.bind(mContext.getUser(), testFeatures,
+ slotIdToSubIdMap));
+ verify(mMockContext).bindServiceAsUser(any(), serviceCaptor.capture(), anyInt(), any());
return serviceCaptor.getValue();
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
index 65b73fb..5f16d9b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
@@ -39,6 +39,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.telephony.SubscriptionManager;
import android.telephony.ims.ImsService;
import android.telephony.ims.aidl.IImsConfig;
@@ -135,6 +136,7 @@
private final Handler mHandler = new Handler(Looper.getMainLooper());
private ImsServiceController mTestImsServiceController;
private ImsFeatureBinderRepository mRepo;
+ private UserHandle mUser;
@Before
@Override
@@ -150,11 +152,12 @@
mMockCallbacks = mock(ImsServiceController.ImsServiceControllerCallbacks.class);
mFeatureFlags = mock(FeatureFlags.class);
mMockContext = mock(Context.class);
+ mUser = UserHandle.of(UserHandle.myUserId());
mRepo = new ImsFeatureBinderRepository();
mTestImsServiceController = new ImsServiceController(mMockContext, mTestComponentName,
mMockCallbacks, mHandler, REBIND_RETRY, mRepo, mFeatureFlags);
- when(mMockContext.bindService(any(), any(), anyInt())).thenReturn(true);
+ when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true);
when(mMockServiceControllerBinder.createMmTelFeature(anyInt(), anyInt()))
.thenReturn(mMockMmTelFeature);
when(mMockServiceControllerBinder.createRcsFeature(anyInt(), anyInt()))
@@ -198,11 +201,12 @@
SparseIntArray slotIdToSubIdMap = new SparseIntArray();
slotIdToSubIdMap.put(SLOT_0, SUB_2);
- assertTrue(mTestImsServiceController.bind(testFeatures, slotIdToSubIdMap.clone()));
+ assertTrue(mTestImsServiceController.bind(mUser, testFeatures, slotIdToSubIdMap.clone()));
int expectedFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
| Context.BIND_IMPORTANT;
- verify(mMockContext).bindService(intentCaptor.capture(), any(), eq(expectedFlags));
+ verify(mMockContext).bindServiceAsUser(intentCaptor.capture(), any(), eq(expectedFlags),
+ any());
Intent testIntent = intentCaptor.getValue();
assertEquals(ImsService.SERVICE_INTERFACE, testIntent.getAction());
assertEquals(mTestComponentName, testIntent.getComponent());
@@ -222,9 +226,9 @@
bindAndConnectService(testFeatures, slotIdToSubIdMap.clone());
// already bound, should return false
- assertFalse(mTestImsServiceController.bind(testFeatures, slotIdToSubIdMap.clone()));
+ assertFalse(mTestImsServiceController.bind(mUser, testFeatures, slotIdToSubIdMap.clone()));
- verify(mMockContext, times(1)).bindService(any(), any(), anyInt());
+ verify(mMockContext, times(1)).bindServiceAsUser(any(), any(), anyInt(), any());
}
/**
@@ -250,10 +254,10 @@
verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_2);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_RCS), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
- eq(mTestImsServiceController));
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+ eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+ eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController));
validateMmTelFeatureContainerExists(SLOT_0);
validateRcsFeatureContainerExists(SLOT_0);
}
@@ -282,10 +286,10 @@
verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_2);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_RCS), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
- eq(mTestImsServiceController));
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+ eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+ eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController));
validateMmTelFeatureContainerExists(SLOT_0);
validateRcsFeatureContainerExists(SLOT_0);
@@ -313,9 +317,9 @@
verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_3);
verify(mMockServiceControllerBinder, times(2)).addFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_RCS), any());
- verify(mMockCallbacks, times(2)).imsServiceFeatureCreated(eq(SLOT_0),
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_3),
eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
- verify(mMockCallbacks, times(2)).imsServiceFeatureCreated(eq(SLOT_0),
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_3),
eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController));
validateMmTelFeatureContainerExists(SLOT_0);
validateRcsFeatureContainerExists(SLOT_0);
@@ -341,13 +345,13 @@
verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+ eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_3);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3),
+ eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
validateMmTelFeatureContainerExists(SLOT_0);
validateMmTelFeatureContainerExists(SLOT_1);
@@ -373,12 +377,12 @@
verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_4);
verify(mMockServiceControllerBinder, times(2)).addFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks, times(2)).imsServiceFeatureCreated(eq(SLOT_0),
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_4),
eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_5);
verify(mMockServiceControllerBinder, times(2)).addFeatureStatusCallback(eq(SLOT_1),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks, times(2)).imsServiceFeatureCreated(eq(SLOT_1),
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_5),
eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
validateMmTelFeatureContainerExists(SLOT_0);
validateMmTelFeatureContainerExists(SLOT_1);
@@ -411,13 +415,14 @@
verify(mMockServiceControllerBinder).createEmergencyOnlyMmTelFeature(SLOT_0);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0),
+ eq(SubscriptionManager.INVALID_SUBSCRIPTION_ID), eq(ImsFeature.FEATURE_MMTEL),
eq(mTestImsServiceController));
verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_3);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3),
+ eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
validateMmTelFeatureContainerExistsWithEmergency(SLOT_0);
validateMmTelFeatureContainerExistsWithEmergency(SLOT_1);
@@ -437,8 +442,9 @@
verify(mMockServiceControllerBinder).createEmergencyOnlyMmTelFeature(SLOT_1);
verify(mMockServiceControllerBinder, times(2)).addFeatureStatusCallback(eq(SLOT_1),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks, times(2)).imsServiceFeatureCreated(eq(SLOT_1),
- eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1),
+ eq(SubscriptionManager.INVALID_SUBSCRIPTION_ID), eq(ImsFeature.FEATURE_MMTEL),
+ eq(mTestImsServiceController));
validateMmTelFeatureContainerExistsWithEmergency(SLOT_0);
validateMmTelFeatureContainerExistsWithEmergency(SLOT_1);
@@ -470,10 +476,10 @@
verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_2);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_RCS), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
- eq(mTestImsServiceController));
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+ eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+ eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController));
validateFeatureContainerExistsWithSipDelegate(ImsFeature.FEATURE_MMTEL, SLOT_0);
validateFeatureContainerExistsWithSipDelegate(ImsFeature.FEATURE_RCS, SLOT_0);
}
@@ -499,7 +505,8 @@
verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+ eq(ImsFeature.FEATURE_MMTEL),
eq(mTestImsServiceController));
// verify CAPABILITY_SIP_DELEGATE_CREATION is not set because MMTEL and RCS are not set.
validateFeatureContainerDoesNotHaveSipDelegate(ImsFeature.FEATURE_MMTEL, SLOT_0);
@@ -525,9 +532,9 @@
verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
- eq(mTestImsServiceController));
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0),
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+ eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
eq(ImsFeature.FEATURE_EMERGENCY_MMTEL), eq(mTestImsServiceController));
// Make sure this callback happens, which will notify the framework of emergency calling
// availability.
@@ -553,9 +560,11 @@
verify(mMockServiceControllerBinder).createEmergencyOnlyMmTelFeature(SLOT_0);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0),
+ eq(SubscriptionManager.INVALID_SUBSCRIPTION_ID), eq(ImsFeature.FEATURE_MMTEL),
eq(mTestImsServiceController));
verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0),
+ eq(SubscriptionManager.INVALID_SUBSCRIPTION_ID),
eq(ImsFeature.FEATURE_EMERGENCY_MMTEL), eq(mTestImsServiceController));
// Make sure this callback happens, which will notify the framework of emergency calling
// availability.
@@ -584,17 +593,17 @@
verify(mMockServiceControllerBinder, never()).createMmTelFeature(SLOT_0, SUB_2);
verify(mMockServiceControllerBinder, never()).addFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_0),
+ verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
- verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_0),
+ verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
eq(ImsFeature.FEATURE_EMERGENCY_MMTEL), eq(mTestImsServiceController));
validateMmTelFeatureContainerDoesntExist(SLOT_0);
// verify RCS feature is created
verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_2);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_RCS), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+ eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController));
validateRcsFeatureContainerExists(SLOT_0);
}
@@ -798,9 +807,9 @@
long delay = mTestImsServiceController.getRebindDelay();
waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
- verify(mMockCallbacks, never()).imsServiceFeatureCreated(anyInt(), anyInt(),
+ verify(mMockCallbacks, never()).imsServiceFeatureCreated(anyInt(), anyInt(), anyInt(),
eq(mTestImsServiceController));
- verify(mMockCallbacks).imsServiceBindPermanentError(eq(mTestComponentName));
+ verify(mMockCallbacks).imsServiceBindPermanentError(eq(mTestComponentName), eq(mUser));
validateMmTelFeatureContainerDoesntExist(SLOT_0);
validateRcsFeatureContainerDoesntExist(SLOT_0);
}
@@ -822,8 +831,8 @@
bindAndConnectService(testFeatures, slotIdToSubIdMap.clone());
verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_2);
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+ eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController));
validateRcsFeatureContainerDoesntExist(SLOT_0);
}
@@ -843,8 +852,8 @@
verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+ eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
// Create a new list with an additional item
HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithAddition = new HashSet<>(
testFeatures);
@@ -859,8 +868,8 @@
verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_3);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3),
+ eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
validateMmTelFeatureContainerExists(SLOT_0);
validateMmTelFeatureContainerExists(SLOT_1);
}
@@ -880,8 +889,8 @@
verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+ eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
// Create a new list with an additional item
HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithAddition = new HashSet<>(
testFeatures);
@@ -895,8 +904,8 @@
verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_3);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3),
+ eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
validateMmTelFeatureContainerExists(SLOT_0);
validateMmTelFeatureContainerExists(SLOT_1);
}
@@ -918,8 +927,8 @@
verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+ eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
validateMmTelFeatureContainerExists(SLOT_0);
assertEquals(mMockMmTelBinder, cb.container.imsFeature);
assertTrue((ImsService.CAPABILITY_EMERGENCY_OVER_MMTEL
@@ -934,9 +943,8 @@
mTestImsServiceController.changeImsServiceFeatures(testFeaturesWithAddition,
slotIdToSubIdMap.clone());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0),
- eq(ImsFeature.FEATURE_EMERGENCY_MMTEL),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+ eq(ImsFeature.FEATURE_EMERGENCY_MMTEL), eq(mTestImsServiceController));
validateMmTelFeatureContainerExistsWithEmergency(SLOT_0);
assertEquals(mMockMmTelBinder, cb.container.imsFeature);
@@ -972,8 +980,8 @@
verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_2);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_RCS), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+ eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController));
validateRcsFeatureContainerExists(SLOT_0);
// Add FEATURE_EMERGENCY_MMTEL and ensure it doesn't cause MMTEL bind
HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithAddition = new HashSet<>(
@@ -988,9 +996,8 @@
verify(mMockServiceControllerBinder, never()).createMmTelFeature(SLOT_1, SUB_3);
verify(mMockServiceControllerBinder, never()).addFeatureStatusCallback(eq(SLOT_1),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_1),
- eq(ImsFeature.FEATURE_MMTEL),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3),
+ eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
validateMmTelFeatureContainerDoesntExist(SLOT_1);
}
@@ -1010,8 +1017,8 @@
verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+ eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
validateMmTelFeatureContainerExists(SLOT_0);
// Call change with the same features and make sure it is disregarded
@@ -1027,7 +1034,7 @@
verify(mMockServiceControllerBinder, never()).removeFeatureStatusCallback(anyInt(),
anyInt(), any());
verify(mMockCallbacks, times(1)).imsServiceFeatureCreated(eq(SLOT_0),
- eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
+ eq(SUB_2), eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
verify(mMockCallbacks, never()).imsServiceFeatureRemoved(anyInt(), anyInt(), any());
validateMmTelFeatureContainerExists(SLOT_0);
}
@@ -1050,13 +1057,13 @@
verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+ eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_3);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3),
+ eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
validateMmTelFeatureContainerExists(SLOT_0);
validateMmTelFeatureContainerExists(SLOT_1);
// Create a new list with one less item
@@ -1098,13 +1105,13 @@
verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+ eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_3);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3),
+ eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
validateMmTelFeatureContainerExists(SLOT_0);
validateMmTelFeatureContainerExists(SLOT_1);
// Create a new list with one less item
@@ -1149,23 +1156,23 @@
verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+ eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_2);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_RCS), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+ eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController));
verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_3);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3),
+ eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
verify(mMockServiceControllerBinder).createRcsFeature(SLOT_1, SUB_3);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1),
eq(ImsFeature.FEATURE_RCS), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_RCS),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3),
+ eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController));
validateMmTelFeatureContainerExists(SLOT_0);
validateMmTelFeatureContainerExists(SLOT_1);
validateRcsFeatureContainerExists(SLOT_0);
@@ -1205,12 +1212,12 @@
verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_4);
verify(mMockServiceControllerBinder, times(2)).addFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks, times(2)).imsServiceFeatureCreated(eq(SLOT_0),
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_4),
eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_4);
verify(mMockServiceControllerBinder, times(2)).addFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_RCS), any());
- verify(mMockCallbacks, times(2)).imsServiceFeatureCreated(eq(SLOT_0),
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_4),
eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController));
validateMmTelFeatureContainerExists(SLOT_0);
validateRcsFeatureContainerExists(SLOT_0);
@@ -1235,13 +1242,13 @@
verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+ eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_3);
verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1),
eq(ImsFeature.FEATURE_MMTEL), any());
- verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
- eq(mTestImsServiceController));
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3),
+ eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
validateMmTelFeatureContainerExists(SLOT_0);
validateMmTelFeatureContainerExists(SLOT_1);
@@ -1291,7 +1298,7 @@
verify(mMockServiceControllerBinder, never()).createRcsFeature(SLOT_0, SUB_2);
verify(mMockServiceControllerBinder, never()).removeFeatureStatusCallback(eq(SLOT_0),
eq(ImsFeature.FEATURE_RCS), any());
- verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_0),
+ verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController));
validateMmTelFeatureContainerDoesntExist(SLOT_0);
validateRcsFeatureContainerDoesntExist(SLOT_0);
@@ -1318,7 +1325,7 @@
long delay = REBIND_RETRY.getStartDelay();
waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
// The service should autobind after rebind event occurs
- verify(mMockContext, times(2)).bindService(any(), any(), anyInt());
+ verify(mMockContext, times(2)).bindServiceAsUser(any(), any(), anyInt(), any());
}
/**
@@ -1344,7 +1351,7 @@
long delay = REBIND_RETRY.getStartDelay();
waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
// The service should autobind after rebind event occurs
- verify(mMockContext, times(2)).bindService(any(), any(), anyInt());
+ verify(mMockContext, times(2)).bindServiceAsUser(any(), any(), anyInt(), any());
}
/**
@@ -1365,7 +1372,7 @@
conn.onBindingDied(null /*null*/);
// Be sure that there are no binds before the RETRY_TIMEOUT expires
- verify(mMockContext, times(1)).bindService(any(), any(), anyInt());
+ verify(mMockContext, times(1)).bindServiceAsUser(any(), any(), anyInt(), any());
}
/**
@@ -1390,7 +1397,7 @@
waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
// Unbind should stop the autobind from occurring.
- verify(mMockContext, times(1)).bindService(any(), any(), anyInt());
+ verify(mMockContext, times(1)).bindServiceAsUser(any(), any(), anyInt(), any());
}
/**
@@ -1412,10 +1419,10 @@
long delay = REBIND_RETRY.getStartDelay();
waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
- mTestImsServiceController.bind(testFeatures, slotIdToSubIdMap.clone());
+ mTestImsServiceController.bind(mUser, testFeatures, slotIdToSubIdMap.clone());
// Should only see two binds, not three from the auto rebind that occurs.
- verify(mMockContext, times(2)).bindService(any(), any(), anyInt());
+ verify(mMockContext, times(2)).bindServiceAsUser(any(), any(), anyInt(), any());
}
private void validateMmTelFeatureContainerExists(int slotId) {
@@ -1504,8 +1511,8 @@
SparseIntArray slotIdToSubIdMap) {
ArgumentCaptor<ServiceConnection> serviceCaptor =
ArgumentCaptor.forClass(ServiceConnection.class);
- assertTrue(mTestImsServiceController.bind(testFeatures, slotIdToSubIdMap));
- verify(mMockContext).bindService(any(), serviceCaptor.capture(), anyInt());
+ assertTrue(mTestImsServiceController.bind(mUser, testFeatures, slotIdToSubIdMap));
+ verify(mMockContext).bindServiceAsUser(any(), serviceCaptor.capture(), anyInt(), any());
return serviceCaptor.getValue();
}
}