• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.telephony.ims;
18 
19 import android.Manifest;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.app.ActivityManager;
23 import android.content.BroadcastReceiver;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.pm.PackageManager;
29 import android.content.pm.ResolveInfo;
30 import android.content.pm.ServiceInfo;
31 import android.os.AsyncResult;
32 import android.os.Handler;
33 import android.os.HandlerExecutor;
34 import android.os.HandlerThread;
35 import android.os.Looper;
36 import android.os.Message;
37 import android.os.PersistableBundle;
38 import android.os.RemoteException;
39 import android.os.UserHandle;
40 import android.os.UserManager;
41 import android.telephony.CarrierConfigManager;
42 import android.telephony.SubscriptionManager;
43 import android.telephony.TelephonyManager;
44 import android.telephony.ims.ImsService;
45 import android.telephony.ims.aidl.IImsConfig;
46 import android.telephony.ims.aidl.IImsRegistration;
47 import android.telephony.ims.feature.ImsFeature;
48 import android.telephony.ims.feature.MmTelFeature;
49 import android.telephony.ims.stub.ImsFeatureConfiguration;
50 import android.text.TextUtils;
51 import android.util.ArrayMap;
52 import android.util.ArraySet;
53 import android.util.LocalLog;
54 import android.util.Log;
55 import android.util.Pair;
56 import android.util.SparseArray;
57 import android.util.SparseIntArray;
58 
59 import com.android.ims.ImsFeatureBinderRepository;
60 import com.android.ims.ImsFeatureContainer;
61 import com.android.ims.internal.IImsServiceFeatureCallback;
62 import com.android.internal.annotations.VisibleForTesting;
63 import com.android.internal.os.SomeArgs;
64 import com.android.internal.telephony.PhoneConfigurationManager;
65 import com.android.internal.telephony.flags.FeatureFlags;
66 import com.android.internal.telephony.util.WorkerThread;
67 import com.android.internal.util.IndentingPrintWriter;
68 
69 import java.io.FileDescriptor;
70 import java.io.PrintWriter;
71 import java.util.ArrayList;
72 import java.util.Collections;
73 import java.util.HashMap;
74 import java.util.HashSet;
75 import java.util.List;
76 import java.util.Map;
77 import java.util.Objects;
78 import java.util.Set;
79 import java.util.concurrent.CompletableFuture;
80 import java.util.concurrent.LinkedBlockingQueue;
81 import java.util.concurrent.TimeUnit;
82 import java.util.stream.Collectors;
83 
84 /**
85  * Creates a list of ImsServices that are available to bind to based on the Device configuration
86  * overlay values "config_ims_rcs_package" and "config_ims_mmtel_package" as well as Carrier
87  * Configuration value "config_ims_rcs_package_override_string" and
88  * "config_ims_mmtel_package_override_string".
89  * These ImsServices are then bound to in the following order for each mmtel and rcs feature:
90  *
91  * 1. Carrier Config defined override value per SIM.
92  * 2. Device overlay default value (including no SIM case).
93  *
94  * ImsManager can then retrieve the binding to the correct ImsService using
95  * {@link #listenForFeature} on a per-slot and per feature basis.
96  */
97 
98 public class ImsResolver implements ImsServiceController.ImsServiceControllerCallbacks {
99 
100     private static final String TAG = "ImsResolver";
101     private static final int GET_IMS_SERVICE_TIMEOUT_MS = 5000;
102 
103     @VisibleForTesting
104     public static final String METADATA_EMERGENCY_MMTEL_FEATURE =
105             "android.telephony.ims.EMERGENCY_MMTEL_FEATURE";
106     @VisibleForTesting
107     public static final String METADATA_MMTEL_FEATURE = "android.telephony.ims.MMTEL_FEATURE";
108     @VisibleForTesting
109     public static final String METADATA_RCS_FEATURE = "android.telephony.ims.RCS_FEATURE";
110     // Overrides the correctness permission check of android.permission.BIND_IMS_SERVICE for any
111     // ImsService that is connecting to the platform.
112     // This should ONLY be used for testing and should not be used in production ImsServices.
113     private static final String METADATA_OVERRIDE_PERM_CHECK = "override_bind_check";
114 
115     // Based on updates from PackageManager
116     private static final int HANDLER_ADD_PACKAGE = 0;
117     // Based on updates from PackageManager
118     private static final int HANDLER_REMOVE_PACKAGE = 1;
119     // Based on updates from CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED
120     private static final int HANDLER_CONFIG_CHANGED = 2;
121     // A query has been started for an ImsService to relay the features they support.
122     private static final int HANDLER_START_DYNAMIC_FEATURE_QUERY = 3;
123     // A dynamic query to request ImsService features has completed.
124     private static final int HANDLER_DYNAMIC_FEATURE_CHANGE = 4;
125     // Testing: Overrides the current configuration for ImsService binding
126     private static final int HANDLER_OVERRIDE_IMS_SERVICE_CONFIG = 5;
127     // Based on boot complete indication. When this happens, there may be ImsServices that are not
128     // direct boot aware that need to be started.
129     private static final int HANDLER_BOOT_COMPLETE = 6;
130     // Sent when the number of slots has dynamically changed on the device. We will need to
131     // resize available ImsServiceController slots and perform dynamic queries again.
132     private static final int HANDLER_MSIM_CONFIGURATION_CHANGE = 7;
133     // clear any carrier ImsService test overrides.
134     private static final int HANDLER_CLEAR_CARRIER_IMS_SERVICE_CONFIG = 8;
135     // the user has switched
136     private static final int HANDLER_USER_SWITCHED = 9;
137     // A dynamic query has failed permanently, remove the package from being tracked
138     private static final int HANDLER_REMOVE_PACKAGE_PERM_ERROR = 10;
139 
140     // Delay between dynamic ImsService queries.
141     private static final int DELAY_DYNAMIC_QUERY_MS = 5000;
142 
143     private static HandlerThread sHandlerThread;
144 
145     private static ImsResolver sInstance;
146 
147     /**
148      * Create the ImsResolver Service singleton instance.
149      */
make(Context context, String defaultMmTelPackageName, String defaultRcsPackageName, int numSlots, ImsFeatureBinderRepository repo, FeatureFlags featureFlags)150     public static void make(Context context, String defaultMmTelPackageName,
151             String defaultRcsPackageName, int numSlots, ImsFeatureBinderRepository repo,
152             FeatureFlags featureFlags) {
153         if (sInstance == null) {
154             if (featureFlags.threadShred()) {
155                 sInstance = new ImsResolver(context, defaultMmTelPackageName, defaultRcsPackageName,
156                         numSlots, repo, WorkerThread.get().getLooper(), featureFlags);
157             } else {
158                 sHandlerThread = new HandlerThread(TAG);
159                 sHandlerThread.start();
160                 sInstance = new ImsResolver(context, defaultMmTelPackageName, defaultRcsPackageName,
161                         numSlots, repo, sHandlerThread.getLooper(), featureFlags);
162             }
163         }
164     }
165 
166     /**
167      * @return The ImsResolver Service instance. May be {@code null} if no ImsResolver was created
168      * due to IMS not being supported.
169      */
getInstance()170     public static @Nullable ImsResolver getInstance() {
171         return sInstance;
172     }
173 
174     private static class OverrideConfig {
175         public final String packageName;
176         public final int slotId;
177         public final int userId;
178         public final boolean isCarrierService;
179         public final int[] featureTypes;
180 
OverrideConfig(String pkgName, int slotIndex, int userIndex, boolean isCarrier, int[] features)181         OverrideConfig(String pkgName, int slotIndex, int userIndex, boolean isCarrier,
182                 int[] features) {
183             packageName = pkgName;
184             slotId = slotIndex;
185             userId = userIndex;
186             isCarrierService = isCarrier;
187             featureTypes = features;
188         }
189     }
190 
191     /**
192      * Stores information about an ImsService, including the package name, class name, and features
193      * that the service supports.
194      */
195     @VisibleForTesting
196     public static class ImsServiceInfo {
197         public final ComponentName name;
198         public final Set<UserHandle> users = new HashSet<>();
199         // Determines if features were created from metadata in the manifest or through dynamic
200         // query.
201         public boolean featureFromMetadata = true;
202         public ImsServiceControllerFactory controllerFactory;
203 
204         // Map slotId->Feature
205         private final HashSet<ImsFeatureConfiguration.FeatureSlotPair> mSupportedFeatures;
206 
ImsServiceInfo(ComponentName componentName)207         public ImsServiceInfo(ComponentName componentName) {
208             name = componentName;
209             mSupportedFeatures = new HashSet<>();
210         }
211 
addFeatureForAllSlots(int numSlots, int feature)212         void addFeatureForAllSlots(int numSlots, int feature) {
213             for (int i = 0; i < numSlots; i++) {
214                 mSupportedFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(i, feature));
215             }
216         }
217 
replaceFeatures(Set<ImsFeatureConfiguration.FeatureSlotPair> newFeatures)218         void replaceFeatures(Set<ImsFeatureConfiguration.FeatureSlotPair> newFeatures) {
219             mSupportedFeatures.clear();
220             mSupportedFeatures.addAll(newFeatures);
221         }
222 
223         @VisibleForTesting
getSupportedFeatures()224         public Set<ImsFeatureConfiguration.FeatureSlotPair> getSupportedFeatures() {
225             return mSupportedFeatures;
226         }
227 
228         @Override
equals(Object o)229         public boolean equals(Object o) {
230             if (this == o) return true;
231             if (o == null || getClass() != o.getClass()) return false;
232             ImsServiceInfo that = (ImsServiceInfo) o;
233             return Objects.equals(name, that.name) && Objects.equals(users, that.users);
234         }
235 
236         @Override
hashCode()237         public int hashCode() {
238             return Objects.hash(name, users);
239         }
240 
241         @Override
toString()242         public String toString() {
243             return "[ImsServiceInfo] name="
244                     + name
245                     + ", user="
246                     + users
247                     + ",featureFromMetadata="
248                     + featureFromMetadata
249                     + ","
250                     + printFeatures(mSupportedFeatures);
251         }
252     }
253 
254     private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
255         @Override
256         public void onReceive(Context context, Intent intent) {
257             final String action = intent.getAction();
258             final UserHandle handle = intent.getParcelableExtra(Intent.EXTRA_USER,
259                     UserHandle.class);
260             switch (action) {
261                 case Intent.ACTION_USER_SWITCHED ->
262                         mHandler.obtainMessage(HANDLER_USER_SWITCHED, handle).sendToTarget();
263             }
264         }
265     };
266 
267     // Receives broadcasts from the system involving changes to the installed applications. If
268     // an ImsService that we are configured to use is installed, we must bind to it.
269     private final BroadcastReceiver mAppChangedReceiver = new BroadcastReceiver() {
270         @Override
271         public void onReceive(Context context, Intent intent) {
272             final String action = intent.getAction();
273             final String packageName = intent.getData().getSchemeSpecificPart();
274             switch (action) {
275                 case Intent.ACTION_PACKAGE_ADDED:
276                     // intentional fall-through
277                 case Intent.ACTION_PACKAGE_REPLACED:
278                     // intentional fall-through
279                 case Intent.ACTION_PACKAGE_CHANGED:
280                     mHandler.obtainMessage(HANDLER_ADD_PACKAGE, packageName).sendToTarget();
281                     break;
282                 case Intent.ACTION_PACKAGE_REMOVED:
283                     mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE, packageName).sendToTarget();
284                     break;
285                 default:
286                     return;
287             }
288         }
289     };
290 
291     // Receives the broadcast that a new Carrier Config has been loaded in order to possibly
292     // unbind from one service and bind to another.
293     private final BroadcastReceiver mConfigChangedReceiver = new BroadcastReceiver() {
294         @Override
295         public void onReceive(Context context, Intent intent) {
296 
297             int slotId = intent.getIntExtra(CarrierConfigManager.EXTRA_SLOT_INDEX,
298                     SubscriptionManager.INVALID_SIM_SLOT_INDEX);
299 
300             if (slotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
301                 Log.i(TAG, "Received CCC for invalid slot id.");
302                 return;
303             }
304 
305             int subId = intent.getIntExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
306                     SubscriptionManager.INVALID_SUBSCRIPTION_ID);
307             int slotSimState = mTelephonyManagerProxy.getSimState(mContext, slotId);
308             if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
309                     && (slotSimState != TelephonyManager.SIM_STATE_ABSENT
310                     && slotSimState != TelephonyManager.SIM_STATE_NOT_READY)) {
311                 // We only care about carrier config updates that happen when a slot is known to be
312                 // absent, the subscription is disabled (not ready), or populated and the carrier
313                 // config has been loaded.
314                 Log.i(TAG, "Received CCC for slot " + slotId + " and sim state "
315                         + slotSimState + ", ignoring.");
316                 return;
317             }
318 
319             Log.i(TAG, "Received Carrier Config Changed for SlotId: " + slotId + ", SubId: "
320                     + subId + ", sim state: " + slotSimState);
321 
322             mHandler.obtainMessage(HANDLER_CONFIG_CHANGED, slotId, subId).sendToTarget();
323         }
324     };
325 
326     // Receives the broadcast that the device has finished booting (and the device is no longer
327     // encrypted).
328     private final BroadcastReceiver mBootCompleted = new BroadcastReceiver() {
329         @Override
330         public void onReceive(Context context, Intent intent) {
331             Log.i(TAG, "Received BOOT_COMPLETED");
332             // Recalculate all cached services to pick up ones that have just been enabled since
333             // boot complete.
334             mHandler.obtainMessage(HANDLER_BOOT_COMPLETE, null).sendToTarget();
335         }
336     };
337 
338     /**
339      * Testing interface used to mock ActivityManager in testing
340      */
341     @VisibleForTesting
342     public interface ActivityManagerProxy {
343         /**
344          * @return The current user
345          */
getCurrentUser()346         UserHandle getCurrentUser();
347     }
348 
349     /**
350      * Testing interface used to mock SubscriptionManager in testing
351      */
352     @VisibleForTesting
353     public interface SubscriptionManagerProxy {
354         /**
355          * Mock-able interface for {@link SubscriptionManager#getSubscriptionId(int)} used for
356          * testing.
357          */
getSubId(int slotId)358         int getSubId(int slotId);
359         /**
360          * Mock-able interface for {@link SubscriptionManager#getSlotIndex(int)} used for testing.
361          */
getSlotIndex(int subId)362         int getSlotIndex(int subId);
363     }
364 
365     /**
366      * Testing interface used to stub out TelephonyManager dependencies.
367      */
368     @VisibleForTesting
369     public interface TelephonyManagerProxy {
370         /**
371          * @return the SIM state for the slot ID specified.
372          */
getSimState(Context context, int slotId)373         int getSimState(Context context, int slotId);
374     }
375 
376     private TelephonyManagerProxy mTelephonyManagerProxy = new TelephonyManagerProxy() {
377         @Override
378         public int getSimState(Context context, int slotId) {
379             TelephonyManager tm = context.getSystemService(TelephonyManager.class);
380             if (tm == null) {
381                 return TelephonyManager.SIM_STATE_UNKNOWN;
382             }
383             return tm.getSimState(slotId);
384         }
385     };
386 
387     private SubscriptionManagerProxy mSubscriptionManagerProxy = new SubscriptionManagerProxy() {
388         @Override
389         public int getSubId(int slotId) {
390             return SubscriptionManager.getSubscriptionId(slotId);
391         }
392 
393         @Override
394         public int getSlotIndex(int subId) {
395             return SubscriptionManager.getSlotIndex(subId);
396         }
397     };
398 
399     private ActivityManagerProxy mActivityManagerProxy = new ActivityManagerProxy() {
400         @Override
401         public UserHandle getCurrentUser() {
402             return UserHandle.of(ActivityManager.getCurrentUser());
403         }
404     };
405 
406     /**
407      * Testing interface for injecting mock ImsServiceControllers.
408      */
409     @VisibleForTesting
410     public interface ImsServiceControllerFactory {
411         /**
412          * @return the Service Interface String used for binding the ImsService.
413          */
getServiceInterface()414         String getServiceInterface();
415         /**
416          * @return the ImsServiceController created using the context and componentName supplied.
417          */
create(Context context, ComponentName componentName, ImsServiceController.ImsServiceControllerCallbacks callbacks, ImsFeatureBinderRepository repo, FeatureFlags featureFlags)418         ImsServiceController create(Context context, ComponentName componentName,
419                 ImsServiceController.ImsServiceControllerCallbacks callbacks,
420                 ImsFeatureBinderRepository repo, FeatureFlags featureFlags);
421     }
422 
423     private ImsServiceControllerFactory mImsServiceControllerFactory =
424             new ImsServiceControllerFactory() {
425 
426         @Override
427         public String getServiceInterface() {
428             return ImsService.SERVICE_INTERFACE;
429         }
430 
431         @Override
432         public ImsServiceController create(Context context, ComponentName componentName,
433                 ImsServiceController.ImsServiceControllerCallbacks callbacks,
434                 ImsFeatureBinderRepository repo, FeatureFlags featureFlags) {
435                     return new ImsServiceController(context, componentName, callbacks, repo,
436                             featureFlags);
437         }
438     };
439 
440     /**
441      * Used for testing.
442      */
443     @VisibleForTesting
444     public interface ImsDynamicQueryManagerFactory {
create(Context context, ImsServiceFeatureQueryManager.Listener listener)445         ImsServiceFeatureQueryManager create(Context context,
446                 ImsServiceFeatureQueryManager.Listener listener);
447     }
448 
449     private final ImsServiceControllerFactory mImsServiceControllerFactoryCompat =
450             new ImsServiceControllerFactory() {
451                 @Override
452                 public String getServiceInterface() {
453                     return android.telephony.ims.compat.ImsService.SERVICE_INTERFACE;
454                 }
455 
456                 @Override
457                 public ImsServiceController create(Context context, ComponentName componentName,
458                         ImsServiceController.ImsServiceControllerCallbacks callbacks,
459                         ImsFeatureBinderRepository repo, FeatureFlags featureFlags) {
460                     return new ImsServiceControllerCompat(context, componentName, callbacks, repo);
461                 }
462             };
463 
464     private ImsDynamicQueryManagerFactory mDynamicQueryManagerFactory =
465             ImsServiceFeatureQueryManager::new;
466 
467     private final CarrierConfigManager mCarrierConfigManager;
468     private final Context mContext;
469     // Special context created only for registering receivers for all users using UserHandle.ALL.
470     // The lifetime of a registered receiver is bounded by the lifetime of the context it's
471     // registered through, so we must retain the Context as long as we need the receiver to be
472     // active.
473     private final Context mReceiverContext;
474     private final ImsFeatureBinderRepository mRepo;
475     // Locks mBoundImsServicesByFeature only. Be careful to avoid deadlocks from
476     // ImsServiceController callbacks.
477     private final Object mBoundServicesLock = new Object();
478     private int mNumSlots;
479     // Array index corresponds to slot, per slot there is a feature->package name mapping.
480     // should only be accessed from handler
481     private final SparseArray<Map<Integer, String>> mCarrierServices;
482     // Package name of the default device services, Maps ImsFeature -> packageName.
483     // Must synchronize on this object to access.
484     private final Map<Integer, String> mDeviceServices = new ArrayMap<>();
485     // Persistent Logging
486     private final LocalLog mEventLog = new LocalLog(32);
487 
488     private boolean mBootCompletedHandlerRan = false;
489     private boolean mCarrierConfigReceived = false;
490 
491     // Synchronize all events on a handler to ensure that the cache includes the most recent
492     // version of the installed ImsServices.
493     private final Handler mHandler;
494 
495     private final FeatureFlags mFeatureFlags;
496 
497     private class ResolverHandler extends Handler {
498 
ResolverHandler(Looper looper)499         ResolverHandler(Looper looper) {
500             super(looper);
501         }
502 
503         @Override
handleMessage(Message msg)504         public void handleMessage(Message msg) {
505             switch (msg.what) {
506                 case HANDLER_ADD_PACKAGE: {
507                     String packageName = (String) msg.obj;
508                     maybeAddedImsService(packageName);
509                     break;
510                 }
511                 case HANDLER_REMOVE_PACKAGE: {
512                     String packageName = (String) msg.obj;
513                     maybeRemovedImsService(packageName);
514                     break;
515                 }
516                 case HANDLER_REMOVE_PACKAGE_PERM_ERROR: {
517                     Pair<String, UserHandle> packageInfo = (Pair<String, UserHandle>) msg.obj;
518                     maybeRemovedImsServiceForUser(packageInfo.first, packageInfo.second);
519                     break;
520                 }
521                 case HANDLER_BOOT_COMPLETE: {
522                     if (!mBootCompletedHandlerRan) {
523                         mBootCompletedHandlerRan = true;
524                         mEventLog.log("handling BOOT_COMPLETE");
525                         if (mCarrierConfigReceived) {
526                             mEventLog.log("boot complete - reeval");
527                             // Re-evaluate bound services for all slots after requerying
528                             //packagemanager
529                             maybeAddedImsService(null /*packageName*/);
530                         } else {
531                             mEventLog.log("boot complete - update cache");
532                             // Do not bind any ImsServices yet, just update the cache to include new
533                             // services. All will be re-evaluated after first carrier config changed
534                             updateInstalledServicesCache();
535                         }
536                     }
537                     break;
538                 }
539                 case HANDLER_CONFIG_CHANGED: {
540                     int slotId = msg.arg1;
541                     int subId = msg.arg2;
542                     // If the msim config has changed and there is a residual event for an invalid
543                     // slot,ignore.
544                     if (slotId >= mNumSlots) {
545                         Log.w(TAG, "HANDLER_CONFIG_CHANGED for invalid slotid=" + slotId);
546                         break;
547                     }
548                     mCarrierConfigReceived = true;
549                     carrierConfigChanged(slotId, subId);
550                     break;
551                 }
552                 case HANDLER_START_DYNAMIC_FEATURE_QUERY: {
553                     ImsServiceInfo info = (ImsServiceInfo) msg.obj;
554                     startDynamicQuery(info);
555                     break;
556                 }
557                 case HANDLER_DYNAMIC_FEATURE_CHANGE: {
558                     SomeArgs args = (SomeArgs) msg.obj;
559                     ComponentName name = (ComponentName) args.arg1;
560                     Set<ImsFeatureConfiguration.FeatureSlotPair> features =
561                             (Set<ImsFeatureConfiguration.FeatureSlotPair>) args.arg2;
562                     args.recycle();
563                     dynamicQueryComplete(name, features);
564                     break;
565                 }
566                 case HANDLER_OVERRIDE_IMS_SERVICE_CONFIG: {
567                     OverrideConfig config = (OverrideConfig) msg.obj;
568                     setPackageNameUserOverride(config.packageName, config.userId);
569                     Map<Integer, String> featureConfig = new HashMap<>();
570                     for (int featureType : config.featureTypes) {
571                         featureConfig.put(featureType, config.packageName);
572                     }
573                     if (config.isCarrierService) {
574                         overrideCarrierService(config.slotId, featureConfig);
575                     } else {
576                         overrideDeviceService(featureConfig);
577                     }
578                     break;
579                 }
580                 case HANDLER_MSIM_CONFIGURATION_CHANGE: {
581                     AsyncResult result = (AsyncResult) msg.obj;
582                     handleMsimConfigChange((Integer) result.result);
583                     break;
584                 }
585                 case HANDLER_CLEAR_CARRIER_IMS_SERVICE_CONFIG: {
586                     clearCarrierServiceOverrides(msg.arg1);
587                     break;
588                 }
589                 case HANDLER_USER_SWITCHED: {
590                     UserHandle handle = (UserHandle) msg.obj;
591                     Log.i(TAG, "onUserSwitched=" + handle);
592                     maybeAddedImsService(null);
593                 }
594                 default:
595                     break;
596             }
597         }
598     }
599 
600     private final HandlerExecutor mRunnableExecutor;
601 
602     // Results from dynamic queries to ImsService regarding the features they support.
603     private final ImsServiceFeatureQueryManager.Listener mDynamicQueryListener =
604             new ImsServiceFeatureQueryManager.Listener() {
605 
606                 @Override
607                 public void onComplete(ComponentName name,
608                         Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
609                     Log.d(TAG, "onComplete called for name: " + name + printFeatures(features));
610                     handleFeaturesChanged(name, features);
611                 }
612 
613                 @Override
614                 public void onError(ComponentName name) {
615                     Log.w(TAG, "onError: " + name + "returned with an error result");
616                     mEventLog.log("onError - dynamic query error for " + name);
617                     scheduleQueryForFeatures(name, DELAY_DYNAMIC_QUERY_MS);
618                 }
619 
620                 @Override
621                 public void onPermanentError(ComponentName name, UserHandle user) {
622                     Log.w(TAG, "onPermanentError: component=" + name);
623                     mEventLog.log("onPermanentError - error for " + name);
624                     if (!mFeatureFlags.imsResolverUserAware()) {
625                         mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE,
626                                 name.getPackageName()).sendToTarget();
627                     } else {
628                         mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE_PERM_ERROR,
629                                 new Pair<>(name.getPackageName(), user)).sendToTarget();
630                     }
631                 }
632             };
633 
634     // Used during testing, overrides the carrier services while non-empty.
635     // Array index corresponds to slot, per slot there is a feature->package name mapping.
636     // should only be accessed from handler
637     private final SparseArray<SparseArray<String>> mOverrideServices;
638     //Used during testing, restricts the ImsService to be bound on a specific user.
639     private final Map<String, UserHandle> mImsServiceTestUserRestrictions = new HashMap<>();
640     // Outer array index corresponds to Slot Id, Maps ImsFeature.FEATURE->bound ImsServiceController
641     // Locked on mBoundServicesLock
642     private final SparseArray<SparseArray<ImsServiceController>> mBoundImsServicesByFeature;
643     // not locked, only accessed on a handler thread.
644     // Tracks list of all installed ImsServices
645     private final Map<ComponentName, ImsServiceInfo> mInstalledServicesCache = new HashMap<>();
646     // not locked, only accessed on a handler thread.
647     // Active ImsServiceControllers, which are bound to ImsServices.
648     private final Map<ComponentName, ImsServiceController> mActiveControllers = new HashMap<>();
649     private ImsServiceFeatureQueryManager mFeatureQueryManager;
650     private final SparseIntArray mSlotIdToSubIdMap;
651 
ImsResolver(Context context, String defaultMmTelPackageName, String defaultRcsPackageName, int numSlots, ImsFeatureBinderRepository repo, Looper looper, FeatureFlags featureFlags)652     public ImsResolver(Context context, String defaultMmTelPackageName,
653             String defaultRcsPackageName, int numSlots, ImsFeatureBinderRepository repo,
654             Looper looper, FeatureFlags featureFlags) {
655         Log.i(TAG, "device MMTEL package: " + defaultMmTelPackageName + ", device RCS package:"
656                 + defaultRcsPackageName);
657         mContext = context;
658         mNumSlots = numSlots;
659         mRepo = repo;
660         mReceiverContext = context.createContextAsUser(UserHandle.ALL, 0 /*flags*/);
661 
662         mHandler = new ResolverHandler(looper);
663         mRunnableExecutor = new HandlerExecutor(mHandler);
664         mFeatureFlags = featureFlags;
665         mCarrierServices = new SparseArray<>(mNumSlots);
666         setDeviceConfiguration(defaultMmTelPackageName, ImsFeature.FEATURE_EMERGENCY_MMTEL);
667         setDeviceConfiguration(defaultMmTelPackageName, ImsFeature.FEATURE_MMTEL);
668         setDeviceConfiguration(defaultRcsPackageName, ImsFeature.FEATURE_RCS);
669         mCarrierConfigManager = (CarrierConfigManager) mContext.getSystemService(
670                 Context.CARRIER_CONFIG_SERVICE);
671         mOverrideServices = new SparseArray<>(0 /*initial size*/);
672         mBoundImsServicesByFeature = new SparseArray<>(mNumSlots);
673         mSlotIdToSubIdMap = new SparseIntArray(mNumSlots);
674         for (int i = 0; i < mNumSlots; i++) {
675             mSlotIdToSubIdMap.put(i, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
676         }
677     }
678 
679     @VisibleForTesting
setTelephonyManagerProxy(TelephonyManagerProxy proxy)680     public void setTelephonyManagerProxy(TelephonyManagerProxy proxy) {
681         mTelephonyManagerProxy = proxy;
682     }
683 
684     @VisibleForTesting
setSubscriptionManagerProxy(SubscriptionManagerProxy proxy)685     public void setSubscriptionManagerProxy(SubscriptionManagerProxy proxy) {
686         mSubscriptionManagerProxy = proxy;
687     }
688 
689     @VisibleForTesting
setImsServiceControllerFactory(ImsServiceControllerFactory factory)690     public void setImsServiceControllerFactory(ImsServiceControllerFactory factory) {
691         mImsServiceControllerFactory = factory;
692     }
693 
694     @VisibleForTesting
setActivityManagerProxy(ActivityManagerProxy proxy)695     public void setActivityManagerProxy(ActivityManagerProxy proxy) {
696         mActivityManagerProxy = proxy;
697     }
698 
699     @VisibleForTesting
getHandler()700     public Handler getHandler() {
701         return mHandler;
702     }
703 
704     @VisibleForTesting
setImsDynamicQueryManagerFactory(ImsDynamicQueryManagerFactory m)705     public void setImsDynamicQueryManagerFactory(ImsDynamicQueryManagerFactory m) {
706         mDynamicQueryManagerFactory = m;
707     }
708 
709     /**
710      * Needs to be called after the constructor to kick off the process of binding to ImsServices.
711      * Should be run on the handler thread of ImsResolver
712      */
initialize()713     public void initialize() {
714         mHandler.post(()-> initializeInternal());
715     }
716 
initializeInternal()717     private void initializeInternal() {
718         mEventLog.log("Initializing");
719         Log.i(TAG, "Initializing cache.");
720         PhoneConfigurationManager.registerForMultiSimConfigChange(mHandler,
721                 HANDLER_MSIM_CONFIGURATION_CHANGE, null);
722         mFeatureQueryManager = mDynamicQueryManagerFactory.create(mContext, mDynamicQueryListener);
723 
724         updateInstalledServicesCache();
725 
726         IntentFilter appChangedFilter = new IntentFilter();
727         appChangedFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
728         appChangedFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
729         appChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
730         appChangedFilter.addDataScheme("package");
731         mReceiverContext.registerReceiver(mAppChangedReceiver, appChangedFilter);
732         if (mFeatureFlags.imsResolverUserAware()) {
733             mReceiverContext.registerReceiver(mUserReceiver, new IntentFilter(
734                     Intent.ACTION_USER_SWITCHED));
735         }
736         mReceiverContext.registerReceiver(mConfigChangedReceiver, new IntentFilter(
737                 CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
738 
739         UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
740         if (userManager.isUserUnlocked()) {
741             mHandler.obtainMessage(HANDLER_BOOT_COMPLETE, null).sendToTarget();
742         } else {
743             mReceiverContext.registerReceiver(mBootCompleted, new IntentFilter(
744                     Intent.ACTION_BOOT_COMPLETED));
745             if (userManager.isUserUnlocked()) {
746                 mHandler.obtainMessage(HANDLER_BOOT_COMPLETE, null).sendToTarget();
747             }
748         }
749 
750         // Update the package names of the carrier ImsServices if they do not exist already and
751         // possibly bind if carrier configs exist. Otherwise wait for CarrierConfigChanged
752         // indication.
753         bindCarrierServicesIfAvailable();
754     }
755 
756     /**
757      * Query the system for all registered ImsServices and add them to the cache if there are any
758      * new ones that are not tracked.
759      */
updateInstalledServicesCache()760     private void updateInstalledServicesCache() {
761         // This will get all services with the correct intent filter from PackageManager
762         for (ImsServiceInfo info : getImsServiceInfo(null)) {
763             if (!mInstalledServicesCache.containsKey(info.name)) {
764                 mInstalledServicesCache.put(info.name, info);
765             }
766         }
767     }
768 
769     /**
770      * Destroys this ImsResolver. Used for tearing down static resources during testing.
771      */
772     @VisibleForTesting
destroy()773     public void destroy() {
774         PhoneConfigurationManager.unregisterForMultiSimConfigChange(mHandler);
775         mHandler.removeCallbacksAndMessages(null);
776     }
777 
778     // Only start the bind if there is an existing Carrier Configuration. Otherwise, wait for
779     // carrier config changed.
bindCarrierServicesIfAvailable()780     private void bindCarrierServicesIfAvailable() {
781         boolean hasConfigChanged = false;
782         boolean pendingDynamicQuery = false;
783         for (int slotId = 0; slotId < mNumSlots; slotId++) {
784             int subId = mSubscriptionManagerProxy.getSubId(slotId);
785             Map<Integer, String> featureMap = getImsPackageOverrideConfig(subId);
786             for (int f = ImsFeature.FEATURE_EMERGENCY_MMTEL; f < ImsFeature.FEATURE_MAX; f++) {
787                 String newPackageName = featureMap.getOrDefault(f, "");
788                 if (!TextUtils.isEmpty(newPackageName)) {
789                     Log.d(TAG, "bindCarrierServicesIfAvailable - carrier package found: "
790                             + newPackageName + ", feature "
791                             + ImsFeature.FEATURE_LOG_MAP.getOrDefault(f, "invalid")
792                             + " on slot " + slotId);
793                     mEventLog.log("bindCarrierServicesIfAvailable - carrier package found: "
794                             + newPackageName + " on slot " + slotId);
795                     // Carrier configs are already available, so mark received.
796                     mCarrierConfigReceived = true;
797                     setSubId(slotId, subId);
798                     setCarrierConfiguredPackageName(newPackageName, slotId, f);
799                     ImsServiceInfo info = getVisibleImsServiceInfoFromCache(newPackageName);
800                     // We do not want to trigger feature configuration changes unless there is
801                     // already a valid carrier config change.
802                     if (info != null && info.featureFromMetadata) {
803                         hasConfigChanged = true;
804                     } else {
805                         // Config will change when this query completes
806                         scheduleQueryForFeatures(info);
807                         if (info != null) pendingDynamicQuery = true;
808                     }
809                 }
810             }
811         }
812         // we want to make sure that we are either pending to bind to a carrier configured service
813         // or bind to the device config if we potentially missed the carrier config changed
814         // indication.
815         if (hasConfigChanged || (mFeatureFlags.imsResolverUserAware()
816                 && mCarrierConfigReceived && !pendingDynamicQuery)) {
817             calculateFeatureConfigurationChange();
818         }
819     }
820 
821     /**
822      * Notify ImsService to enable IMS for the framework. This will trigger IMS registration and
823      * trigger ImsFeature status updates.
824      */
enableIms(int slotId)825     public void enableIms(int slotId) {
826         getImsServiceControllers(slotId).forEach(
827                 (controller) -> controller.enableIms(slotId, getSubId(slotId)));
828     }
829 
830     /**
831      * Notify ImsService to disable IMS for the framework. This will trigger IMS de-registration and
832      * trigger ImsFeature capability status to become false.
833      */
disableIms(int slotId)834     public void disableIms(int slotId) {
835         getImsServiceControllers(slotId).forEach(
836                 (controller) -> controller.disableIms(slotId, getSubId(slotId)));
837     }
838 
839     /**
840      * Notify ImsService to disable IMS for the framework.
841      * And notify ImsService back to enable IMS for the framework.
842      */
resetIms(int slotId)843     public void resetIms(int slotId) {
844         getImsServiceControllers(slotId).forEach(
845                 (controller) -> controller.resetIms(slotId, getSubId(slotId)));
846     }
847 
848     /**
849      * Returns the ImsRegistration structure associated with the slotId and feature specified.
850      */
getImsRegistration(int slotId, int feature)851     public @Nullable IImsRegistration getImsRegistration(int slotId, int feature) {
852         ImsFeatureContainer fc = mRepo.getIfExists(slotId, feature).orElse(null);
853         return  (fc != null) ? fc.imsRegistration : null;
854     }
855 
856     /**
857      * Returns the ImsConfig structure associated with the slotId and feature specified.
858      */
getImsConfig(int slotId, int feature)859     public @Nullable IImsConfig getImsConfig(int slotId, int feature) {
860         ImsFeatureContainer fc = mRepo.getIfExists(slotId, feature).orElse(null);
861         return  (fc != null) ? fc.imsConfig : null;
862     }
863 
864     /**
865      * @return A Set containing all the bound ImsServiceControllers for the slotId specified.
866      */
getImsServiceControllers(int slotId)867     private Set<ImsServiceController> getImsServiceControllers(int slotId) {
868         if (slotId < 0 || slotId >= mNumSlots) {
869             return Collections.emptySet();
870         }
871         SparseArray<ImsServiceController> featureToControllerMap;
872         synchronized (mBoundServicesLock) {
873             featureToControllerMap =  mBoundImsServicesByFeature.get(slotId);
874         }
875         if (featureToControllerMap == null) {
876             Log.w(TAG, "getImsServiceControllers: couldn't find any active "
877                     + "ImsServiceControllers");
878             return Collections.emptySet();
879         }
880         // Create a temporary set to dedupe when multiple features map to the same
881         // ImsServiceController
882         Set<ImsServiceController> controllers = new ArraySet<>(2);
883         for (int i = 0; i < featureToControllerMap.size(); i++) {
884             int key = featureToControllerMap.keyAt(i);
885             ImsServiceController c = featureToControllerMap.get(key);
886             if (c != null) controllers.add(c);
887         }
888         return controllers;
889     }
890 
891     /**
892      * Register a new listener for the feature type and slot specified. ImsServiceController will
893      * update the connections as they become available.
894      */
listenForFeature(int slotId, int feature, IImsServiceFeatureCallback callback)895     public void listenForFeature(int slotId, int feature, IImsServiceFeatureCallback callback) {
896         mRepo.registerForConnectionUpdates(slotId, feature, callback, mRunnableExecutor);
897     }
898 
899     /**
900      * Unregister a previously registered IImsServiceFeatureCallback through
901      * {@link #listenForFeature(int, int, IImsServiceFeatureCallback)}.
902      * @param callback The callback to be unregistered.
903      */
unregisterImsFeatureCallback(IImsServiceFeatureCallback callback)904     public void unregisterImsFeatureCallback(IImsServiceFeatureCallback callback) {
905         mRepo.unregisterForConnectionUpdates(callback);
906     }
907 
908     // Used for testing only.
clearCarrierImsServiceConfiguration(int slotId)909     public boolean clearCarrierImsServiceConfiguration(int slotId) {
910         if (slotId < 0 || slotId >= mNumSlots) {
911             Log.w(TAG, "clearCarrierImsServiceConfiguration: invalid slotId!");
912             return false;
913         }
914 
915         Message.obtain(mHandler, HANDLER_CLEAR_CARRIER_IMS_SERVICE_CONFIG, slotId, 0 /*arg2*/)
916                 .sendToTarget();
917         return true;
918     }
919 
920     // Used for testing only.
overrideImsServiceConfiguration(String packageName, int slotId, int userId, boolean isCarrierService, int[] overrideFeatureTypes)921     public boolean overrideImsServiceConfiguration(String packageName, int slotId, int userId,
922             boolean isCarrierService, int[] overrideFeatureTypes) {
923         if (slotId < 0 || slotId >= mNumSlots) {
924             Log.w(TAG, "overrideImsServiceConfiguration: invalid slotId!");
925             return false;
926         }
927 
928         OverrideConfig overrideConfig = new OverrideConfig(packageName, slotId, userId,
929                 isCarrierService, overrideFeatureTypes);
930         Message.obtain(mHandler, HANDLER_OVERRIDE_IMS_SERVICE_CONFIG, overrideConfig)
931                 .sendToTarget();
932         return true;
933     }
934 
getDeviceConfiguration(@msFeature.FeatureType int featureType)935     private String getDeviceConfiguration(@ImsFeature.FeatureType int featureType) {
936         synchronized (mDeviceServices) {
937             return mDeviceServices.getOrDefault(featureType, "");
938         }
939     }
940 
setDeviceConfiguration(String name, @ImsFeature.FeatureType int featureType)941     private void setDeviceConfiguration(String name, @ImsFeature.FeatureType int featureType) {
942         synchronized (mDeviceServices) {
943             mDeviceServices.put(featureType, name);
944         }
945     }
946 
947     // not synchronized, access in handler ONLY.
setCarrierConfiguredPackageName(@onNull String packageName, int slotId, @ImsFeature.FeatureType int featureType)948     private void setCarrierConfiguredPackageName(@NonNull String packageName, int slotId,
949             @ImsFeature.FeatureType int featureType) {
950         getCarrierConfiguredPackageNames(slotId).put(featureType, packageName);
951     }
952 
953     // not synchronized, access in handler ONLY.
getCarrierConfiguredPackageName(int slotId, @ImsFeature.FeatureType int featureType)954     private @NonNull String getCarrierConfiguredPackageName(int slotId,
955             @ImsFeature.FeatureType int featureType) {
956         return getCarrierConfiguredPackageNames(slotId).getOrDefault(featureType, "");
957     }
958 
959     // not synchronized, access in handler ONLY.
getCarrierConfiguredPackageNames(int slotId)960     private @NonNull Map<Integer, String> getCarrierConfiguredPackageNames(int slotId) {
961         Map<Integer, String> carrierConfig = mCarrierServices.get(slotId);
962         if (carrierConfig == null) {
963             carrierConfig = new ArrayMap<>();
964             mCarrierServices.put(slotId, carrierConfig);
965         }
966         return carrierConfig;
967     }
968 
969     // not synchronized, access in handler ONLY.
removeOverridePackageName(int slotId)970     private Set<String> removeOverridePackageName(int slotId) {
971         Set<String> removedOverrides = new HashSet<>();
972         for (int f = ImsFeature.FEATURE_EMERGENCY_MMTEL; f < ImsFeature.FEATURE_MAX; f++) {
973             SparseArray<String> overrides = getOverridePackageName(slotId);
974             String packageName = overrides.removeReturnOld(f);
975             if (packageName != null) removedOverrides.add(packageName);
976         }
977         return removedOverrides;
978     }
979 
980     // not synchronized, access in handler ONLY.
setOverridePackageName(@ullable String packageName, int slotId, @ImsFeature.FeatureType int featureType)981     private void setOverridePackageName(@Nullable String packageName, int slotId,
982             @ImsFeature.FeatureType int featureType) {
983         getOverridePackageName(slotId).put(featureType, packageName);
984     }
985 
986     // not synchronized, access in handler ONLY.
setPackageNameUserOverride(String packageName, int userId)987     private void setPackageNameUserOverride(String packageName, int userId) {
988         if (packageName == null || packageName.isEmpty() || userId == UserHandle.USER_NULL) return;
989         Log.i(TAG, "setPackageNameUserOverride: set for " + packageName + ", user= " + userId);
990         mImsServiceTestUserRestrictions.put(packageName, UserHandle.of(userId));
991     }
992 
993     // not synchronized, access in handler ONLY.
clearPackageNameUserOverride(String packageName)994     private void clearPackageNameUserOverride(String packageName) {
995         UserHandle handle = mImsServiceTestUserRestrictions.remove(packageName);
996         if (handle != null) {
997             Log.i(TAG, "clearPackageNameUserOverride: cleared for " + packageName
998                     + "on user " + handle);
999         }
1000     }
1001 
1002     // not synchronized, access in handler ONLY.
getOverridePackageName(int slotId, @ImsFeature.FeatureType int featureType)1003     private @Nullable String getOverridePackageName(int slotId,
1004             @ImsFeature.FeatureType int featureType) {
1005         return getOverridePackageName(slotId).get(featureType);
1006     }
1007 
1008     // not synchronized, access in handler ONLY.
getOverridePackageName(int slotId)1009     private @NonNull SparseArray<String> getOverridePackageName(int slotId) {
1010         SparseArray<String> carrierConfig = mOverrideServices.get(slotId);
1011         if (carrierConfig == null) {
1012             carrierConfig = new SparseArray<>();
1013             mOverrideServices.put(slotId, carrierConfig);
1014         }
1015         return carrierConfig;
1016     }
1017 
1018     /**
1019      * @return true if there is a carrier configuration that exists for the slot & featureType pair
1020      * and the cached carrier ImsService associated with the configuration also supports the
1021      * requested ImsFeature type.
1022      */
1023     // not synchronized, access in handler ONLY.
doesCarrierConfigurationExist(int slotId, @ImsFeature.FeatureType int featureType)1024     private boolean doesCarrierConfigurationExist(int slotId,
1025             @ImsFeature.FeatureType int featureType) {
1026         String carrierPackage = getCarrierConfiguredPackageName(slotId, featureType);
1027         if (TextUtils.isEmpty(carrierPackage)) {
1028             return false;
1029         }
1030         // Config exists, but the carrier ImsService also needs to support this feature
1031         return doesCachedImsServiceExist(carrierPackage, slotId, featureType);
1032     }
1033 
1034     /**
1035      * Check the cached ImsServices that exist on this device to determine if there is a ImsService
1036      * with the same package name that matches the provided configuration and is configured to run
1037      * in one of the active users.
1038      */
1039     // not synchronized, access in handler ONLY.
doesCachedImsServiceExist(String packageName, int slotId, @ImsFeature.FeatureType int featureType)1040     private boolean doesCachedImsServiceExist(String packageName, int slotId,
1041             @ImsFeature.FeatureType int featureType) {
1042         // Config exists, but the carrier ImsService also needs to support this feature
1043         ImsServiceInfo info = getVisibleImsServiceInfoFromCache(packageName);
1044         return info != null && info.getSupportedFeatures().stream().anyMatch(
1045                 feature -> feature.slotId == slotId && feature.featureType == featureType);
1046     }
1047 
1048     /**
1049      * @return the package name of the ImsService with the requested configuration.
1050      */
1051     // used in shell commands queries during testing only.
getImsServiceConfiguration(int slotId, boolean isCarrierService, @ImsFeature.FeatureType int featureType)1052     public String getImsServiceConfiguration(int slotId, boolean isCarrierService,
1053             @ImsFeature.FeatureType int featureType) {
1054         if (slotId < 0 || slotId >= mNumSlots) {
1055             Log.w(TAG, "getImsServiceConfiguration: invalid slotId!");
1056             return "";
1057         }
1058 
1059         LinkedBlockingQueue<String> result = new LinkedBlockingQueue<>(1);
1060         // access the configuration on the handler.
1061         mHandler.post(() -> result.offer(isCarrierService
1062                 ? getCarrierConfiguredPackageName(slotId, featureType) :
1063                 getDeviceConfiguration(featureType)));
1064         try {
1065             return result.poll(GET_IMS_SERVICE_TIMEOUT_MS, TimeUnit.MILLISECONDS);
1066         } catch (InterruptedException e) {
1067             Log.w(TAG, "getImsServiceConfiguration: exception=" + e.getMessage());
1068             return null;
1069         }
1070     }
1071 
1072     /**
1073      * Determines if there is a valid ImsService configured for the specified ImsFeature.
1074      * @param slotId The slot ID to check for.
1075      * @param featureType The ImsFeature featureType to check for.
1076      * @return true if there is an ImsService configured for the specified ImsFeature type, false
1077      * if there is not.
1078      */
isImsServiceConfiguredForFeature(int slotId, @ImsFeature.FeatureType int featureType)1079     public boolean isImsServiceConfiguredForFeature(int slotId,
1080             @ImsFeature.FeatureType int featureType) {
1081         if (!TextUtils.isEmpty(getDeviceConfiguration(featureType))) {
1082             // Shortcut a little bit here - instead of dynamically looking up the configured
1083             // package name, which can be a long operation depending on the state, just return true
1084             // if there is a configured device ImsService for the requested feature because that
1085             // means there will always be at least a device configured ImsService.
1086             return true;
1087         }
1088         return !TextUtils.isEmpty(getConfiguredImsServicePackageName(slotId, featureType));
1089     }
1090 
1091     /**
1092      * Resolves the PackageName of the ImsService that is configured to be bound for the slotId and
1093      * FeatureType specified and returns it.
1094      * <p>
1095      * If there is a PackageName that is configured, but there is no application on the device that
1096      * fulfills that configuration, this method will also return {@code null} as the ImsService will
1097      * not be bound.
1098      *
1099      * @param slotId The slot ID that the request is for.
1100      * @param featureType The ImsService feature type that the request is for.
1101      * @return The package name of the ImsService that will be bound from telephony for the provided
1102      * slot id and featureType.
1103      */
getConfiguredImsServicePackageName(int slotId, @ImsFeature.FeatureType int featureType)1104     public String getConfiguredImsServicePackageName(int slotId,
1105             @ImsFeature.FeatureType int featureType) {
1106         if (slotId < 0 || slotId >= mNumSlots || featureType <= ImsFeature.FEATURE_INVALID
1107                 || featureType >= ImsFeature.FEATURE_MAX) {
1108             Log.w(TAG, "getResolvedImsServicePackageName received invalid parameters - slot: "
1109                     + slotId + ", feature: " + featureType);
1110             return null;
1111         }
1112         CompletableFuture<String> packageNameFuture = new CompletableFuture<>();
1113         final long startTimeMs = System.currentTimeMillis();
1114         if (mHandler.getLooper().isCurrentThread()) {
1115             // If we are on the same thread as the Handler's looper, run the internal method
1116             // directly.
1117             packageNameFuture.complete(getConfiguredImsServicePackageNameInternal(slotId,
1118                     featureType));
1119         } else {
1120             mHandler.post(() -> {
1121                 try {
1122                     packageNameFuture.complete(getConfiguredImsServicePackageNameInternal(slotId,
1123                             featureType));
1124                 } catch (Exception e) {
1125                     // Catch all Exceptions to ensure we do not block indefinitely in the case of an
1126                     // unexpected error.
1127                     packageNameFuture.completeExceptionally(e);
1128                 }
1129             });
1130         }
1131         try {
1132             String packageName = packageNameFuture.get();
1133             long timeDiff = System.currentTimeMillis() - startTimeMs;
1134             if (timeDiff > 50) {
1135                 // Took an unusually long amount of time (> 50 ms), so log it.
1136                 mEventLog.log("getResolvedImsServicePackageName - [" + slotId + ", "
1137                         + ImsFeature.FEATURE_LOG_MAP.get(featureType)
1138                         + "], async query complete, took " + timeDiff + " ms with package name: "
1139                         + packageName);
1140                 Log.w(TAG, "getResolvedImsServicePackageName: [" + slotId + ", "
1141                         + ImsFeature.FEATURE_LOG_MAP.get(featureType)
1142                         + "], async query complete, took " + timeDiff + " ms with package name: "
1143                         + packageName);
1144             }
1145             return packageName;
1146         } catch (Exception e) {
1147             mEventLog.log("getResolvedImsServicePackageName - [" + slotId + ", "
1148                     + ImsFeature.FEATURE_LOG_MAP.get(featureType) + "] -> Exception: " + e);
1149             Log.w(TAG, "getResolvedImsServicePackageName: [" + slotId + ", "
1150                     + ImsFeature.FEATURE_LOG_MAP.get(featureType) + "] returned Exception: " + e);
1151             return null;
1152         }
1153     }
1154 
1155     /**
1156      * @return the package name for the configured carrier ImsService if it exists on the device and
1157      * supports the supplied slotId and featureType. If no such configuration exists, fall back to
1158      * the device ImsService. If neither exist, then return {@code null};
1159      */
1160     // Not synchronized, access on Handler ONLY!
getConfiguredImsServicePackageNameInternal(int slotId, @ImsFeature.FeatureType int featureType)1161     private String getConfiguredImsServicePackageNameInternal(int slotId,
1162             @ImsFeature.FeatureType int featureType) {
1163         // If a carrier ImsService is configured to be used for the provided slotId and
1164         // featureType, then return that one.
1165         String carrierPackage = getCarrierConfiguredPackageName(slotId, featureType);
1166         if (!TextUtils.isEmpty(carrierPackage)
1167                 && doesCachedImsServiceExist(carrierPackage, slotId, featureType)) {
1168             return carrierPackage;
1169         }
1170         // If there is no carrier ImsService configured for that configuration, then
1171         // return the device's default ImsService for the provided slotId and
1172         // featureType.
1173         String devicePackage = getDeviceConfiguration(featureType);
1174         if (!TextUtils.isEmpty(devicePackage)
1175                 && doesCachedImsServiceExist(devicePackage, slotId, featureType)) {
1176             return devicePackage;
1177         }
1178         // There is no ImsService configuration that exists for the slotId and
1179         // featureType.
1180         return null;
1181     }
1182 
putImsController(int slotId, int subId, int feature, ImsServiceController controller)1183     private void putImsController(int slotId, int subId, int feature,
1184             ImsServiceController controller) {
1185         if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.FEATURE_INVALID
1186                 || feature >= ImsFeature.FEATURE_MAX) {
1187             Log.w(TAG, "putImsController received invalid parameters - slot: " + slotId
1188                     + ", feature: " + feature);
1189             return;
1190         }
1191         synchronized (mBoundServicesLock) {
1192             SparseArray<ImsServiceController> services = mBoundImsServicesByFeature.get(slotId);
1193             if (services == null) {
1194                 services = new SparseArray<>();
1195                 mBoundImsServicesByFeature.put(slotId, services);
1196             }
1197             mEventLog.log("putImsController - [" + slotId + ", "
1198                     + ImsFeature.FEATURE_LOG_MAP.get(feature) + "] -> " + controller);
1199             Log.i(TAG, "ImsServiceController added on slot: " + slotId + ", subId: " + subId
1200                     + " with feature: " + ImsFeature.FEATURE_LOG_MAP.get(feature)
1201                     + " using package: " + controller.getComponentName());
1202             services.put(feature, controller);
1203         }
1204     }
1205 
removeImsController(int slotId, int feature)1206     private ImsServiceController removeImsController(int slotId, int feature) {
1207         if (slotId < 0 || feature <= ImsFeature.FEATURE_INVALID
1208                 || feature >= ImsFeature.FEATURE_MAX) {
1209             Log.w(TAG, "removeImsController received invalid parameters - slot: " + slotId
1210                     + ", feature: " + feature);
1211             return null;
1212         }
1213         synchronized (mBoundServicesLock) {
1214             SparseArray<ImsServiceController> services = mBoundImsServicesByFeature.get(slotId);
1215             if (services == null) {
1216                 return null;
1217             }
1218             ImsServiceController c = services.get(feature, null);
1219             if (c != null) {
1220                 mEventLog.log("removeImsController - [" + slotId + ", "
1221                         + ImsFeature.FEATURE_LOG_MAP.get(feature) + "] -> " + c);
1222                 Log.i(TAG, "ImsServiceController removed on slot: " + slotId + " with feature: "
1223                         + ImsFeature.FEATURE_LOG_MAP.get(feature) + " using package: "
1224                         + c.getComponentName());
1225                 services.remove(feature);
1226             }
1227             return c;
1228         }
1229     }
1230 
1231     // Update the current cache with the new ImsService(s) if it has been added or update the
1232     // supported IMS features if they have changed.
1233     // Called from the handler ONLY
maybeAddedImsService(String packageName)1234     private void maybeAddedImsService(String packageName) {
1235         Log.d(TAG, "maybeAddedImsService, packageName: " + packageName);
1236         List<ImsServiceInfo> infos = getImsServiceInfo(packageName);
1237         // Wait until all ImsServiceInfo is cached before calling
1238         // calculateFeatureConfigurationChange to reduce churn.
1239         boolean requiresCalculation = false;
1240         for (ImsServiceInfo info : infos) {
1241             // Checking to see if the ComponentName is the same, so we can update the supported
1242             // features. Will only be one (if it exists), since it is a set.
1243             ImsServiceInfo match = getInfoByComponentName(mInstalledServicesCache, info.name);
1244             if (match != null) {
1245                 if (mFeatureFlags.imsResolverUserAware()) {
1246                     match.users.clear();
1247                     match.users.addAll(info.users);
1248                 }
1249                 // for dynamic query the new "info" will have no supported features yet. Don't wipe
1250                 // out the cache for the existing features or update yet. Instead start a query
1251                 // for features dynamically.
1252                 if (info.featureFromMetadata) {
1253                     mEventLog.log("maybeAddedImsService - updating features for " + info.name
1254                             + ": " + printFeatures(match.getSupportedFeatures()) + " -> "
1255                             + printFeatures(info.getSupportedFeatures()));
1256                     Log.d(TAG, "Updating features in cached ImsService: " + info.name
1257                             + ", old features: " + match + " new features: " + info);
1258                     // update features in the cache
1259                     match.replaceFeatures(info.getSupportedFeatures());
1260                     requiresCalculation = true;
1261                 } else {
1262                     mEventLog.log("maybeAddedImsService - scheduling query for " + info);
1263                     // start a query to get ImsService features
1264                     scheduleQueryForFeatures(info);
1265                 }
1266             } else {
1267                 Log.i(TAG, "Adding newly added ImsService to cache: " + info.name);
1268                 mEventLog.log("maybeAddedImsService - adding new ImsService: " + info);
1269                 mInstalledServicesCache.put(info.name, info);
1270                 if (info.featureFromMetadata) {
1271                     requiresCalculation = true;
1272                 } else {
1273                     // newly added ImsServiceInfo that has not had features queried yet. Start async
1274                     // bind and query features.
1275                     scheduleQueryForFeatures(info);
1276                 }
1277             }
1278         }
1279         if (requiresCalculation) calculateFeatureConfigurationChange();
1280     }
1281 
1282     // Remove the ImsService from the cache due to the ImsService package being removed.
1283     // Called from the handler ONLY
maybeRemovedImsServiceOld(String packageName)1284     private boolean maybeRemovedImsServiceOld(String packageName) {
1285         ImsServiceInfo match = getInfoByPackageName(mInstalledServicesCache, packageName);
1286         if (match != null) {
1287             mInstalledServicesCache.remove(match.name);
1288             mEventLog.log("maybeRemovedImsService - removing ImsService: " + match);
1289             Log.i(TAG, "Removing ImsService: " + match.name);
1290             unbindImsService(match);
1291             calculateFeatureConfigurationChange();
1292             return true;
1293         }
1294         return false;
1295     }
1296 
1297     // Remove the ImsService from the cache due to the ImsService package being removed.
1298     // Called from the handler ONLY
maybeRemovedImsService(String packageName)1299     private boolean maybeRemovedImsService(String packageName) {
1300         if (!mFeatureFlags.imsResolverUserAware()) {
1301             return maybeRemovedImsServiceOld(packageName);
1302         }
1303         ImsServiceInfo match = getInfoByPackageName(mInstalledServicesCache, packageName);
1304         if (match != null) {
1305             List<ImsServiceInfo> imsServices = searchForImsServices(packageName,
1306                     match.controllerFactory);
1307             ImsServiceInfo newMatch = imsServices.isEmpty() ? null : imsServices.getFirst();
1308             if (newMatch == null) {
1309                 clearPackageNameUserOverride(match.name.getPackageName());
1310                 // The package doesn't exist anymore on any user, so remove
1311                 mInstalledServicesCache.remove(match.name);
1312                 mEventLog.log("maybeRemovedImsService - removing ImsService: " + match);
1313                 Log.i(TAG, "maybeRemovedImsService Removing ImsService for all users: "
1314                         + match.name);
1315                 unbindImsService(match);
1316             } else {
1317                 // The Package exists on some users still, so modify the users
1318                 match.users.clear();
1319                 match.users.addAll(newMatch.users);
1320                 mEventLog.log("maybeRemovedImsService - modifying ImsService users: " + match);
1321                 Log.i(TAG, "maybeRemovedImsService - Modifying ImsService users " + match);
1322                 // If this package still remains on some users, then it is possible we are unbinding
1323                 // an active ImsService, but the assumption here is that the package is being
1324                 // removed on an active user. Be safe and unbind now - we will rebind below if
1325                 // needed.
1326                 unbindImsService(match);
1327             }
1328             calculateFeatureConfigurationChange();
1329             return true;
1330         }
1331         return false;
1332     }
1333 
1334     /**
1335      * Remove the cached ImsService for a specific user. If there are no more users available after
1336      * removing the specified user, remove the ImsService cache entry entirely.
1337      */
1338     // Called from the handler ONLY
maybeRemovedImsServiceForUser(String packageName, UserHandle user)1339     private boolean maybeRemovedImsServiceForUser(String packageName, UserHandle user) {
1340         ImsServiceInfo match = getInfoByPackageName(mInstalledServicesCache, packageName);
1341         if (match != null) {
1342             mEventLog.log("maybeRemovedImsServiceForUser - removing ImsService " + match
1343                     + "for user " + user);
1344             Log.i(TAG, "maybeRemovedImsServiceForUser: Removing ImsService "
1345                     + match + "for user " + user);
1346             unbindImsService(match);
1347             match.users.remove(user);
1348             if (match.users.isEmpty()) {
1349                 mEventLog.log("maybeRemovedImsServiceForUser - no more users, removing "
1350                         + "ImsService " + match);
1351                 Log.i(TAG, "maybeRemovedImsServiceForUser - no more users, removing "
1352                         + "ImsService " + match);
1353                 mInstalledServicesCache.remove(match.name);
1354             }
1355             calculateFeatureConfigurationChange();
1356             return true;
1357         }
1358         return false;
1359     }
1360 
isDeviceService(ImsServiceInfo info)1361     private boolean isDeviceService(ImsServiceInfo info) {
1362         if (info == null) return false;
1363         synchronized (mDeviceServices) {
1364             return mDeviceServices.containsValue(info.name.getPackageName());
1365         }
1366     }
1367 
getSlotsForActiveCarrierService(ImsServiceInfo info)1368     private List<Integer> getSlotsForActiveCarrierService(ImsServiceInfo info) {
1369         if (info == null) return Collections.emptyList();
1370         if (mFeatureFlags.imsResolverUserAware()) {
1371             UserHandle activeUser = getUserForBind(info);
1372             if (activeUser == null) {
1373                 Log.d(TAG, "getSlotsForActiveCarrierService: ImsService " + info.name + "is not "
1374                         + "configured to run for any users, skipping...");
1375                 return Collections.emptyList();
1376             }
1377         }
1378         List<Integer> slots = new ArrayList<>(mNumSlots);
1379         for (int i = 0; i < mNumSlots; i++) {
1380             if (!TextUtils.isEmpty(getCarrierConfiguredPackageNames(i).values().stream()
1381                     .filter(e -> e.equals(info.name.getPackageName())).findAny().orElse(""))) {
1382                 slots.add(i);
1383             }
1384         }
1385         return slots;
1386     }
1387 
getControllerByServiceInfo( Map<ComponentName, ImsServiceController> searchMap, ImsServiceInfo matchValue)1388     private ImsServiceController getControllerByServiceInfo(
1389             Map<ComponentName, ImsServiceController> searchMap, ImsServiceInfo matchValue) {
1390         return searchMap.values().stream()
1391                 .filter(c -> Objects.equals(c.getComponentName(), matchValue.name))
1392                 .findFirst().orElse(null);
1393     }
1394 
getInfoByPackageName(Map<ComponentName, ImsServiceInfo> searchMap, String matchValue)1395     private ImsServiceInfo getInfoByPackageName(Map<ComponentName, ImsServiceInfo> searchMap,
1396             String matchValue) {
1397         return searchMap.values().stream()
1398                 .filter((i) -> Objects.equals(i.name.getPackageName(), matchValue))
1399                 .findFirst().orElse(null);
1400     }
1401 
getInfoByComponentName( Map<ComponentName, ImsServiceInfo> searchMap, ComponentName matchValue)1402     private ImsServiceInfo getInfoByComponentName(
1403             Map<ComponentName, ImsServiceInfo> searchMap, ComponentName matchValue) {
1404         return searchMap.get(matchValue);
1405     }
1406 
bindImsServiceWithFeatures(ImsServiceInfo info, UserHandle user, Set<ImsFeatureConfiguration.FeatureSlotPair> features)1407     private void bindImsServiceWithFeatures(ImsServiceInfo info, UserHandle user,
1408             Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
1409         // Only bind if there are features that will be created by the service.
1410         if (shouldFeaturesCauseBind(features)) {
1411             // Check to see if an active controller already exists
1412             ImsServiceController controller = getControllerByServiceInfo(mActiveControllers, info);
1413             SparseIntArray slotIdToSubIdMap = mSlotIdToSubIdMap.clone();
1414             if (controller != null) {
1415                 try {
1416                     if (!mFeatureFlags.imsResolverUserAware()
1417                             || Objects.equals(user, controller.getBoundUser())) {
1418                         Log.i(TAG, "ImsService connection exists for " + info.name
1419                                 + ", updating features " + features);
1420                         controller.changeImsServiceFeatures(features, slotIdToSubIdMap);
1421                     } else {
1422                         // Changing a user is a pretty rare event, we need to unbind and rebind
1423                         // on the correct new user.
1424                         Log.i(TAG, "ImsService user changed for " + info.name
1425                                 + ", rebinding on user " + user + ", features " + features);
1426                         controller.unbind();
1427                         controller.bind(user, features, slotIdToSubIdMap);
1428                     }
1429 
1430                     // Features have been set, there was an error adding/removing. When the
1431                     // controller recovers, it will add/remove again.
1432                 } catch (RemoteException e) {
1433                     Log.w(TAG, "bindImsService: error=" + e.getMessage());
1434                 }
1435             } else {
1436                 controller = info.controllerFactory.create(mContext, info.name, this, mRepo,
1437                         mFeatureFlags);
1438                 Log.i(TAG, "Binding ImsService: " + controller.getComponentName()
1439                         + "on user " + user + " with features: " + features + ", subIdMap: "
1440                         + slotIdToSubIdMap);
1441                 controller.bind(user, features, slotIdToSubIdMap);
1442                 mEventLog.log("bindImsServiceWithFeatures - create new controller: "
1443                         + controller);
1444             }
1445             mActiveControllers.put(info.name, controller);
1446         }
1447     }
1448 
1449     // Clean up and unbind from an ImsService
unbindImsService(ImsServiceInfo info)1450     private void unbindImsService(ImsServiceInfo info) {
1451         if (info == null) {
1452             return;
1453         }
1454         ImsServiceController controller = getControllerByServiceInfo(mActiveControllers, info);
1455         if (controller != null) {
1456             // Calls imsServiceFeatureRemoved on all features in the controller
1457             try {
1458                 Log.i(TAG, "Unbinding ImsService: " + controller.getComponentName());
1459                 mEventLog.log("unbindImsService - unbinding and removing " + controller);
1460                 controller.unbind();
1461             } catch (RemoteException e) {
1462                 Log.e(TAG, "unbindImsService: Remote Exception: " + e.getMessage());
1463             }
1464             mActiveControllers.remove(info.name);
1465         }
1466     }
1467 
1468     // Calculate which features an ImsServiceController will need. If it is the carrier specific
1469     // ImsServiceController, it will be granted all of the features it requests on the associated
1470     // slot. If it is the device ImsService, it will get all of the features not covered by the
1471     // carrier implementation.
calculateFeaturesToCreate( ImsServiceInfo info)1472     private HashSet<ImsFeatureConfiguration.FeatureSlotPair> calculateFeaturesToCreate(
1473             ImsServiceInfo info) {
1474         HashSet<ImsFeatureConfiguration.FeatureSlotPair> imsFeaturesBySlot = new HashSet<>();
1475         List<Integer> slots = getSlotsForActiveCarrierService(info);
1476         if (!slots.isEmpty()) {
1477             // There is an active carrier config associated with this. Return with the ImsService's
1478             // supported features that are also within the carrier configuration
1479             imsFeaturesBySlot.addAll(info.getSupportedFeatures().stream()
1480                     .filter(feature -> info.name.getPackageName().equals(
1481                             getCarrierConfiguredPackageName(feature.slotId, feature.featureType)))
1482                     .toList());
1483             return imsFeaturesBySlot;
1484         }
1485         if (isDeviceService(info)) {
1486             imsFeaturesBySlot.addAll(info.getSupportedFeatures().stream()
1487                     // only allow supported features that are also set for this package as the
1488                     // device configuration.
1489                     .filter(feature -> info.name.getPackageName().equals(
1490                             getDeviceConfiguration(feature.featureType)))
1491                     // filter out any separate carrier configuration, since that feature is handled
1492                     // by the carrier ImsService.
1493                     .filter(feature -> !doesCarrierConfigurationExist(feature.slotId,
1494                             feature.featureType))
1495                     .toList());
1496         }
1497         return imsFeaturesBySlot;
1498     }
1499 
1500     /**
1501      * Implementation of
1502      * {@link ImsServiceController.ImsServiceControllerCallbacks#imsServiceFeatureCreated}, which
1503      * adds the ImsServiceController from the mBoundImsServicesByFeature structure.
1504      */
1505     @Override
imsServiceFeatureCreated(int slotId, int subId, int feature, ImsServiceController controller)1506     public void imsServiceFeatureCreated(int slotId, int subId, int feature,
1507             ImsServiceController controller) {
1508         putImsController(slotId, subId, feature, controller);
1509     }
1510 
1511     /**
1512      * Implementation of
1513      * {@link ImsServiceController.ImsServiceControllerCallbacks#imsServiceFeatureRemoved}, which
1514      * removes the ImsServiceController from the mBoundImsServicesByFeature structure.
1515      */
1516     @Override
imsServiceFeatureRemoved(int slotId, int feature, ImsServiceController controller)1517     public void imsServiceFeatureRemoved(int slotId, int feature, ImsServiceController controller) {
1518         removeImsController(slotId, feature);
1519     }
1520 
1521     /**
1522      * Implementation of
1523      * {@link ImsServiceController.ImsServiceControllerCallbacks#imsServiceFeaturesChanged, which
1524      * notify the ImsResolver of a change to the supported ImsFeatures of a connected ImsService.
1525      */
imsServiceFeaturesChanged(ImsFeatureConfiguration config, ImsServiceController controller)1526     public void imsServiceFeaturesChanged(ImsFeatureConfiguration config,
1527             ImsServiceController controller) {
1528         if (controller == null || config == null) {
1529             return;
1530         }
1531         Log.i(TAG, "imsServiceFeaturesChanged: config=" + config.getServiceFeatures()
1532                 + ", ComponentName=" + controller.getComponentName());
1533         mEventLog.log("imsServiceFeaturesChanged - for " + controller + ", new config "
1534                 + config.getServiceFeatures());
1535         handleFeaturesChanged(controller.getComponentName(), config.getServiceFeatures());
1536     }
1537 
1538     @Override
imsServiceBindPermanentError(ComponentName name, UserHandle user)1539     public void imsServiceBindPermanentError(ComponentName name, UserHandle user) {
1540         if (name == null) {
1541             return;
1542         }
1543         Log.w(TAG, "imsServiceBindPermanentError: component=" + name + ", user=" + user);
1544         mEventLog.log("imsServiceBindPermanentError - for " + name + ", user " + user);
1545         if (!mFeatureFlags.imsResolverUserAware()) {
1546             mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE,
1547                     name.getPackageName()).sendToTarget();
1548         } else {
1549             mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE_PERM_ERROR,
1550                     new Pair<>(name.getPackageName(), user)).sendToTarget();
1551         }
1552     }
1553 
1554     /**
1555      * Determines if the features specified should cause a bind or keep a binding active to an
1556      * ImsService.
1557      * @return true if MMTEL or RCS features are present, false if they are not or only
1558      * EMERGENCY_MMTEL is specified.
1559      */
shouldFeaturesCauseBind(Set<ImsFeatureConfiguration.FeatureSlotPair> features)1560     private boolean shouldFeaturesCauseBind(Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
1561         long bindableFeatures = features.stream()
1562                 // remove all emergency features
1563                 .filter(f -> f.featureType != ImsFeature.FEATURE_EMERGENCY_MMTEL).count();
1564         return bindableFeatures > 0;
1565     }
1566 
1567     // Possibly rebind to another ImsService for testing carrier ImsServices.
1568     // Called from the handler ONLY
overrideCarrierService(int slotId, Map<Integer, String> featureMap)1569     private void overrideCarrierService(int slotId, Map<Integer, String> featureMap) {
1570         for (Integer featureType : featureMap.keySet()) {
1571             String overridePackageName = featureMap.get(featureType);
1572             mEventLog.log("overriding carrier ImsService to " + overridePackageName
1573                     + " on slot " + slotId + " for feature "
1574                     + ImsFeature.FEATURE_LOG_MAP.getOrDefault(featureType, "invalid"));
1575             setOverridePackageName(overridePackageName, slotId, featureType);
1576         }
1577         updateBoundServices(slotId, Collections.emptyMap());
1578     }
1579 
1580     // Possibly rebind to another ImsService for testing carrier ImsServices.
1581     // Called from the handler ONLY
clearCarrierServiceOverrides(int slotId)1582     private void clearCarrierServiceOverrides(int slotId) {
1583         Log.i(TAG, "clearing carrier ImsService overrides");
1584         mEventLog.log("clearing carrier ImsService overrides");
1585         Set<String> removedPackages = removeOverridePackageName(slotId);
1586         for (String pkg : removedPackages) {
1587             clearPackageNameUserOverride(pkg);
1588         }
1589         carrierConfigChanged(slotId, getSubId(slotId));
1590     }
1591 
1592     // Possibly rebind to another ImsService for testing carrier ImsServices.
1593     // Called from the handler ONLY
overrideDeviceService(Map<Integer, String> featureMap)1594     private void overrideDeviceService(Map<Integer, String> featureMap) {
1595         boolean requiresRecalc = false;
1596         for (Integer featureType : featureMap.keySet()) {
1597             String overridePackageName = featureMap.get(featureType);
1598             mEventLog.log("overriding device ImsService to " + overridePackageName + " for feature "
1599                     + ImsFeature.FEATURE_LOG_MAP.getOrDefault(featureType, "invalid"));
1600             String oldPackageName = getDeviceConfiguration(featureType);
1601             if (!TextUtils.equals(oldPackageName, overridePackageName)) {
1602                 Log.i(TAG, "overrideDeviceService - device package changed (override): "
1603                         + oldPackageName + " -> " + overridePackageName);
1604                 mEventLog.log("overrideDeviceService - device package changed (override): "
1605                         + oldPackageName + " -> " + overridePackageName);
1606                 clearPackageNameUserOverride(oldPackageName);
1607                 setDeviceConfiguration(overridePackageName, featureType);
1608                 ImsServiceInfo info = getVisibleImsServiceInfoFromCache(overridePackageName);
1609                 if (info == null || info.featureFromMetadata) {
1610                     requiresRecalc = true;
1611                 } else {
1612                     // Config will change when this query completes
1613                     scheduleQueryForFeatures(info);
1614                 }
1615             }
1616         }
1617         if (requiresRecalc) calculateFeatureConfigurationChange();
1618     }
1619 
1620     // Called from handler ONLY.
carrierConfigChanged(int slotId, int subId)1621     private void carrierConfigChanged(int slotId, int subId) {
1622         setSubId(slotId, subId);
1623         updateBoundDeviceServices();
1624         if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
1625             // not specified, update carrier override cache and possibly rebind on all slots.
1626             for (int i = 0; i < mNumSlots; i++) {
1627                 updateBoundServices(i, getImsPackageOverrideConfig(getSubId(i)));
1628             }
1629         }
1630         updateBoundServices(slotId, getImsPackageOverrideConfig(subId));
1631     }
1632 
updateBoundDeviceServices()1633     private void updateBoundDeviceServices() {
1634         Log.d(TAG, "updateBoundDeviceServices: called");
1635         ArrayMap<String, ImsServiceInfo> featureDynamicImsPackages = new ArrayMap<>();
1636         for (int f = ImsFeature.FEATURE_EMERGENCY_MMTEL; f < ImsFeature.FEATURE_MAX; f++) {
1637             String packageName = getDeviceConfiguration(f);
1638             ImsServiceInfo serviceInfo = getVisibleImsServiceInfoFromCache(packageName);
1639             if (serviceInfo != null && !serviceInfo.featureFromMetadata
1640                     && !featureDynamicImsPackages.containsKey(packageName)) {
1641                 featureDynamicImsPackages.put(packageName, serviceInfo);
1642 
1643                 Log.d(TAG, "updateBoundDeviceServices: Schedule query for package=" + packageName);
1644                 scheduleQueryForFeatures(featureDynamicImsPackages.get(packageName));
1645             }
1646         }
1647     }
1648 
updateBoundServices(int slotId, Map<Integer, String> featureMap)1649     private void updateBoundServices(int slotId, Map<Integer, String> featureMap) {
1650         if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlots) {
1651             return;
1652         }
1653         boolean hasConfigChanged = false;
1654         boolean didQuerySchedule = false;
1655         for (int f = ImsFeature.FEATURE_EMERGENCY_MMTEL; f < ImsFeature.FEATURE_MAX; f++) {
1656             String overridePackageName = getOverridePackageName(slotId, f);
1657             String oldPackageName = getCarrierConfiguredPackageName(slotId, f);
1658             String newPackageName = featureMap.getOrDefault(f, "");
1659             if (!TextUtils.isEmpty(overridePackageName)) {
1660                 // Do not allow carrier config changes to change the override package while it
1661                 // is in effect.
1662                 Log.i(TAG, String.format("updateBoundServices: overriding %s with %s for feature"
1663                                 + " %s on slot %d",
1664                         TextUtils.isEmpty(newPackageName) ? "(none)" : newPackageName,
1665                         overridePackageName,
1666                         ImsFeature.FEATURE_LOG_MAP.getOrDefault(f, "invalid"), slotId));
1667                 newPackageName = overridePackageName;
1668             }
1669 
1670             setCarrierConfiguredPackageName(newPackageName, slotId, f);
1671             // Carrier config may have not changed, but we still want to kick off a recalculation
1672             // in case there has been a change to the supported device features.
1673             ImsServiceInfo info = getVisibleImsServiceInfoFromCache(newPackageName);
1674             if (info == null || info.featureFromMetadata) {
1675                 hasConfigChanged = true;
1676             } else {
1677                 // Config will change when this query completes
1678                 scheduleQueryForFeatures(info);
1679                 didQuerySchedule = true;
1680             }
1681             Log.i(TAG, "updateBoundServices - carrier package changed: "
1682                     + oldPackageName + " -> " + newPackageName + " on slot " + slotId
1683                     + ", hasConfigChanged=" + hasConfigChanged);
1684             mEventLog.log("updateBoundServices - carrier package changed: "
1685                     + oldPackageName + " -> " + newPackageName + " on slot " + slotId
1686                     + ", hasConfigChanged=" + hasConfigChanged);
1687         }
1688         if (hasConfigChanged) calculateFeatureConfigurationChange();
1689 
1690         if (hasConfigChanged && didQuerySchedule) {
1691             mEventLog.log("[warning] updateBoundServices - both hasConfigChange and query "
1692                     + "scheduled on slot " + slotId);
1693         }
1694     }
1695 
getImsPackageOverrideConfig(int subId)1696     private @NonNull Map<Integer, String> getImsPackageOverrideConfig(int subId) {
1697         PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId);
1698         if (config == null) return Collections.emptyMap();
1699         String packageNameMmTel = config.getString(
1700                 CarrierConfigManager.KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, null);
1701         // Set the config equal for the deprecated key.
1702         String packageNameRcs = packageNameMmTel;
1703         packageNameMmTel = config.getString(
1704                 CarrierConfigManager.KEY_CONFIG_IMS_MMTEL_PACKAGE_OVERRIDE_STRING,
1705                 packageNameMmTel);
1706         packageNameRcs = config.getString(
1707                 CarrierConfigManager.KEY_CONFIG_IMS_RCS_PACKAGE_OVERRIDE_STRING, packageNameRcs);
1708         Map<Integer, String> result = new ArrayMap<>();
1709         if (!TextUtils.isEmpty(packageNameMmTel)) {
1710             result.put(ImsFeature.FEATURE_EMERGENCY_MMTEL, packageNameMmTel);
1711             result.put(ImsFeature.FEATURE_MMTEL, packageNameMmTel);
1712         }
1713         if (!TextUtils.isEmpty(packageNameRcs)) {
1714             result.put(ImsFeature.FEATURE_RCS, packageNameRcs);
1715         }
1716         return result;
1717     }
1718 
1719     /**
1720      * Schedules a query for dynamic ImsService features.
1721      */
scheduleQueryForFeatures(ImsServiceInfo service, int delayMs)1722     private void scheduleQueryForFeatures(ImsServiceInfo service, int delayMs) {
1723         if (service == null) {
1724             return;
1725         }
1726         Message msg = Message.obtain(mHandler, HANDLER_START_DYNAMIC_FEATURE_QUERY, service);
1727         if (mHandler.hasMessages(HANDLER_START_DYNAMIC_FEATURE_QUERY, service)) {
1728             Log.d(TAG, "scheduleQueryForFeatures: dynamic query for " + service.name
1729                     + " already scheduled");
1730             return;
1731         }
1732         Log.d(TAG, "scheduleQueryForFeatures: starting dynamic query for " + service.name
1733                 + " in " + delayMs + "ms.");
1734         mHandler.sendMessageDelayed(msg, delayMs);
1735     }
1736 
scheduleQueryForFeatures(ComponentName name, int delayMs)1737     private void scheduleQueryForFeatures(ComponentName name, int delayMs) {
1738         ImsServiceInfo service = getVisibleImsServiceInfoFromCache(name.getPackageName());
1739         if (service == null) {
1740             Log.w(TAG, "scheduleQueryForFeatures: Couldn't find cached info for name: " + name);
1741             return;
1742         }
1743         scheduleQueryForFeatures(service, delayMs);
1744     }
1745 
scheduleQueryForFeatures(ImsServiceInfo service)1746     private void scheduleQueryForFeatures(ImsServiceInfo service) {
1747         scheduleQueryForFeatures(service, 0);
1748     }
1749 
1750     /**
1751      * Schedules the processing of a completed query.
1752      */
handleFeaturesChanged(ComponentName name, Set<ImsFeatureConfiguration.FeatureSlotPair> features)1753     private void handleFeaturesChanged(ComponentName name,
1754             Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
1755         SomeArgs args = SomeArgs.obtain();
1756         args.arg1 = name;
1757         args.arg2 = features;
1758         mHandler.obtainMessage(HANDLER_DYNAMIC_FEATURE_CHANGE, args).sendToTarget();
1759     }
1760 
handleMsimConfigChange(Integer newNumSlots)1761     private void handleMsimConfigChange(Integer newNumSlots) {
1762         int oldLen = mNumSlots;
1763         if (oldLen == newNumSlots) {
1764             return;
1765         }
1766         mNumSlots = newNumSlots;
1767         Log.i(TAG, "handleMsimConfigChange: oldLen=" + oldLen + ", newLen=" + newNumSlots);
1768         mEventLog.log("MSIM config change: " + oldLen + " -> " + newNumSlots);
1769         if (newNumSlots < oldLen) {
1770             // we need to trim data structures that use slots, however mBoundImsServicesByFeature
1771             // will be updated by ImsServiceController changing to remove features on old slots.
1772             // start at the index of the new highest slot + 1.
1773             for (int oldSlot = newNumSlots; oldSlot < oldLen; oldSlot++) {
1774                 // First clear old carrier configs
1775                 Map<Integer, String> carrierConfigs = getCarrierConfiguredPackageNames(oldSlot);
1776                 for (Integer feature : carrierConfigs.keySet()) {
1777                     setCarrierConfiguredPackageName("", oldSlot, feature);
1778                 }
1779                 // next clear old overrides
1780                 SparseArray<String> overrideConfigs = getOverridePackageName(oldSlot);
1781                 for (int i = 0; i < overrideConfigs.size(); i++) {
1782                     int feature = overrideConfigs.keyAt(i);
1783                     setOverridePackageName("", oldSlot, feature);
1784                 }
1785                 //clear removed slot.
1786                 removeSlotId(oldSlot);
1787             }
1788         }
1789         // Get the new config for each ImsService. For manifest queries, this will update the
1790         // number of slots.
1791         // This will get all services with the correct intent filter from PackageManager
1792         List<ImsServiceInfo> infos = getImsServiceInfo(null);
1793         for (ImsServiceInfo info : infos) {
1794             ImsServiceInfo cachedInfo = mInstalledServicesCache.get(info.name);
1795             if (cachedInfo != null) {
1796                 if (info.featureFromMetadata) {
1797                     cachedInfo.replaceFeatures(info.getSupportedFeatures());
1798                 } else {
1799                     // Remove features that are no longer supported by the device configuration.
1800                     cachedInfo.getSupportedFeatures()
1801                             .removeIf(filter -> filter.slotId >= newNumSlots);
1802                 }
1803             } else {
1804                 // This is unexpected, put the new service on the queue to be added
1805                 mEventLog.log("handleMsimConfigChange: detected untracked service - " + info);
1806                 Log.w(TAG, "handleMsimConfigChange: detected untracked package, queueing to add "
1807                         + info);
1808                 mHandler.obtainMessage(HANDLER_ADD_PACKAGE,
1809                         info.name.getPackageName()).sendToTarget();
1810             }
1811         }
1812 
1813         if (newNumSlots < oldLen) {
1814             // A CarrierConfigChange will happen for the new slot, so only recalculate if there are
1815             // less new slots because we need to remove the old capabilities.
1816             calculateFeatureConfigurationChange();
1817         }
1818     }
1819 
1820     // Starts a dynamic query. Called from handler ONLY.
startDynamicQuery(ImsServiceInfo service)1821     private void startDynamicQuery(ImsServiceInfo service) {
1822         UserHandle user = getUserForBind(service);
1823         if (user == null) {
1824             Log.i(TAG, "scheduleQueryForFeatures: skipping query for ImsService that is not"
1825                     + " running: " + service);
1826             return;
1827         }
1828         // if not current device/carrier service, don't perform query. If this changes, this method
1829         // will be called again.
1830         if (!isDeviceService(service) && getSlotsForActiveCarrierService(service).isEmpty()) {
1831             Log.i(TAG, "scheduleQueryForFeatures: skipping query for ImsService that is not"
1832                     + " set as carrier/device ImsService.");
1833             return;
1834         }
1835         mEventLog.log("startDynamicQuery - starting query for " + service);
1836         boolean queryStarted = mFeatureQueryManager.startQuery(service.name, user,
1837                 service.controllerFactory.getServiceInterface());
1838         if (!queryStarted) {
1839             Log.w(TAG, "startDynamicQuery: service could not connect. Retrying after delay.");
1840             mEventLog.log("startDynamicQuery - query failed. Retrying in "
1841                     + DELAY_DYNAMIC_QUERY_MS + " mS");
1842             scheduleQueryForFeatures(service, DELAY_DYNAMIC_QUERY_MS);
1843         } else {
1844             Log.d(TAG, "startDynamicQuery: Service queried, waiting for response.");
1845         }
1846     }
1847 
1848     // process complete dynamic query. Called from handler ONLY.
dynamicQueryComplete(ComponentName name, Set<ImsFeatureConfiguration.FeatureSlotPair> features)1849     private void dynamicQueryComplete(ComponentName name,
1850             Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
1851         ImsServiceInfo service = getVisibleImsServiceInfoFromCache(name.getPackageName());
1852         if (service == null) {
1853             Log.w(TAG, "dynamicQueryComplete: Couldn't find cached info for name: "
1854                     + name);
1855             return;
1856         }
1857         sanitizeFeatureConfig(features);
1858         mEventLog.log("dynamicQueryComplete: for package " + name + ", features: "
1859                 + printFeatures(service.getSupportedFeatures()) + " -> " + printFeatures(features));
1860         // Add features to service
1861         service.replaceFeatures(features);
1862         // Wait until all queries have completed before changing the configuration to reduce churn.
1863         if (!mFeatureQueryManager.isQueryInProgress()) {
1864             if (mHandler.hasMessages(HANDLER_DYNAMIC_FEATURE_CHANGE)) {
1865                 mEventLog.log("[warning] dynamicQueryComplete - HANDLER_DYNAMIC_FEATURE_CHANGE "
1866                         + "pending with calculateFeatureConfigurationChange()");
1867             }
1868             calculateFeatureConfigurationChange();
1869         }
1870     }
1871 
1872     /**
1873      * Sanitize feature configurations from the ImsService.
1874      * <ul>
1875      *     <li> Strip out feature configs for inactive slots.</li>
1876      *     <li> Ensure the feature includes MMTEL when it supports EMERGENCY_MMTEL, if not, remove.
1877      *     </li>
1878      * </ul>
1879      */
sanitizeFeatureConfig(Set<ImsFeatureConfiguration.FeatureSlotPair> features)1880     private void sanitizeFeatureConfig(Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
1881         // remove configs for slots that are mot active.
1882         features.removeIf(f -> f.slotId >= mNumSlots);
1883         // Ensure that if EMERGENCY_MMTEL is defined for a slot, MMTEL is also defined.
1884         Set<ImsFeatureConfiguration.FeatureSlotPair> emergencyMmtelFeatures = features.stream()
1885                 .filter(feature -> feature.featureType == ImsFeature.FEATURE_EMERGENCY_MMTEL)
1886                 .collect(Collectors.toSet());
1887         for (ImsFeatureConfiguration.FeatureSlotPair feature : emergencyMmtelFeatures) {
1888             if (!features.contains(new ImsFeatureConfiguration.FeatureSlotPair(feature.slotId,
1889                     ImsFeature.FEATURE_MMTEL))) {
1890                 features.remove(feature);
1891             }
1892         }
1893     }
1894 
1895     // Calculate the new configuration for the bound ImsServices.
1896     // Should ONLY be called from the handler.
calculateFeatureConfigurationChangeOld()1897     private void calculateFeatureConfigurationChangeOld() {
1898         for (ImsServiceInfo info : mInstalledServicesCache.values()) {
1899             Set<ImsFeatureConfiguration.FeatureSlotPair> features = calculateFeaturesToCreate(info);
1900             if (shouldFeaturesCauseBind(features)) {
1901                 bindImsServiceWithFeatures(info, mContext.getUser(), features);
1902             } else {
1903                 unbindImsService(info);
1904             }
1905         }
1906     }
1907 
1908     // Should ONLY be called from the handler.
calculateFeatureConfigurationChange()1909     private void calculateFeatureConfigurationChange() {
1910         if (!mFeatureFlags.imsResolverUserAware()) {
1911             calculateFeatureConfigurationChangeOld();
1912             return;
1913         }
1914         // There is an implicit assumption here that the ImsServiceController will remove itself
1915         // from caches BEFORE adding a new one. If this assumption is broken, we will remove a valid
1916         // ImsServiceController from the cache accidentally. To keep this assumption valid, we will
1917         // iterate through the cache twice - first to unbind, then to bind and change features of
1918         // existing ImsServiceControllers. This is a little inefficient, but there should be on the
1919         // order of 10 installed ImsServices at most, so running through this list twice is
1920         // reasonable vs the memory cost of caching binding vs unbinding services.
1921 
1922         // Unbind first if needed
1923         for (ImsServiceInfo info : mInstalledServicesCache.values()) {
1924             Set<ImsFeatureConfiguration.FeatureSlotPair> features = calculateFeaturesToCreate(info);
1925             UserHandle user = getUserForBind(info);
1926             if (shouldFeaturesCauseBind(features) && user != null) continue;
1927             unbindImsService(info);
1928         }
1929         // Bind/alter features second
1930         for (ImsServiceInfo info : mInstalledServicesCache.values()) {
1931             Set<ImsFeatureConfiguration.FeatureSlotPair> features = calculateFeaturesToCreate(info);
1932             UserHandle user = getUserForBind(info);
1933             if (shouldFeaturesCauseBind(features) && user != null) {
1934                 bindImsServiceWithFeatures(info, user, features);
1935             }
1936         }
1937     }
1938 
1939     /**
1940      * Returns the UserHandle that should be used to bind the ImsService.
1941      *
1942      * @return The UserHandle of the user that telephony is running in if the
1943      * ImsService is configured to run in that user, or the current active user
1944      * if not. Returns null if the ImsService is not configured to run in any
1945      * active user.
1946      */
getUserForBind(ImsServiceInfo info)1947     private UserHandle getUserForBind(ImsServiceInfo info) {
1948         if (!mFeatureFlags.imsResolverUserAware()) {
1949             return mContext.getUser();
1950         }
1951         UserHandle currentUser = mActivityManagerProxy.getCurrentUser();
1952         List<UserHandle> activeUsers = getActiveUsers().stream()
1953                 .filter(info.users::contains).toList();
1954         if (activeUsers.isEmpty()) return null;
1955         // If there is a test restriction in place for this package, prioritize that restriction
1956         UserHandle testRestriction = mImsServiceTestUserRestrictions.getOrDefault(
1957                 info.name.getPackageName(), null);
1958         if (testRestriction != null && activeUsers.stream()
1959                 .anyMatch(u -> Objects.equals(u, testRestriction))) {
1960             return testRestriction;
1961         }
1962         // Prioritize the User that Telephony is in, since it is always running
1963         if (activeUsers.stream()
1964                 .anyMatch(u -> Objects.equals(u, mContext.getUser()))) {
1965             return mContext.getUser();
1966         }
1967         if (activeUsers.stream().anyMatch(u -> Objects.equals(u, currentUser))) {
1968             return currentUser;
1969         }
1970         return null;
1971     }
1972 
1973   /**
1974    * Returns the set of full users that are currently active.
1975    */
getActiveUsers()1976     private Set<UserHandle> getActiveUsers() {
1977         Set<UserHandle> profiles = new HashSet<>();
1978         profiles.add(mContext.getUser());
1979         profiles.add(mActivityManagerProxy.getCurrentUser());
1980         return profiles;
1981     }
1982 
printFeatures(Set<ImsFeatureConfiguration.FeatureSlotPair> features)1983     private static String printFeatures(Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
1984         StringBuilder featureString = new StringBuilder();
1985         featureString.append(" features: [");
1986         if (features != null) {
1987             for (ImsFeatureConfiguration.FeatureSlotPair feature : features) {
1988                 featureString.append("{");
1989                 featureString.append(feature.slotId);
1990                 featureString.append(",");
1991                 featureString.append(ImsFeature.FEATURE_LOG_MAP.get(feature.featureType));
1992                 featureString.append("}");
1993             }
1994             featureString.append("]");
1995         }
1996         return featureString.toString();
1997     }
1998 
1999     /**
2000      * Returns the ImsServiceInfo that matches the provided packageName if it belongs to a
2001      * package that is visible as part of the set of active users.
2002      */
getVisibleImsServiceInfoFromCache(String packageName)2003     public ImsServiceInfo getVisibleImsServiceInfoFromCache(String packageName) {
2004         ImsServiceInfo match = getImsServiceInfoFromCache(packageName);
2005         if (!mFeatureFlags.imsResolverUserAware()) {
2006             return match;
2007         }
2008         if (match == null) return null;
2009         UserHandle targetUser = getUserForBind(match);
2010         Log.d(TAG, "getVisibleImsServiceInfoFromCache: " + packageName + ", match=" + match
2011                 + ", targetUser=" + targetUser);
2012         if (targetUser != null) return match; else return null;
2013     }
2014 
2015     /**
2016      * Returns the ImsServiceInfo that matches the provided packageName. This includes
2017      * ImsServiceInfos that are not currently visible for the active users.
2018      */
2019     @VisibleForTesting
getImsServiceInfoFromCache(String packageName)2020     public ImsServiceInfo getImsServiceInfoFromCache(String packageName) {
2021         if (TextUtils.isEmpty(packageName)) {
2022             return null;
2023         }
2024         ImsServiceInfo infoFilter = getInfoByPackageName(mInstalledServicesCache, packageName);
2025         if (infoFilter != null) {
2026             return infoFilter;
2027         } else {
2028             return null;
2029         }
2030     }
2031 
2032     // Return the ImsServiceInfo specified for the package name. If the package name is null,
2033     // get all packages that support ImsServices.
getImsServiceInfo(String packageName)2034     private List<ImsServiceInfo> getImsServiceInfo(String packageName) {
2035         List<ImsServiceInfo> infos = new ArrayList<>();
2036         // Search for Current ImsService implementations
2037         infos.addAll(searchForImsServices(packageName, mImsServiceControllerFactory));
2038         // Search for compat ImsService Implementations
2039         infos.addAll(searchForImsServices(packageName, mImsServiceControllerFactoryCompat));
2040         return infos;
2041     }
2042 
getInfoFromCache(List<ImsServiceInfo> infos, ComponentName componentName)2043     private ImsServiceInfo getInfoFromCache(List<ImsServiceInfo> infos,
2044             ComponentName componentName) {
2045         return infos.stream().filter(info -> Objects.equals(info.name, componentName)).findFirst()
2046                 .orElse(null);
2047     }
2048 
searchForImsServices(String packageName, ImsServiceControllerFactory controllerFactory)2049     private List<ImsServiceInfo> searchForImsServices(String packageName,
2050             ImsServiceControllerFactory controllerFactory) {
2051         List<ImsServiceInfo> infos = new ArrayList<>();
2052 
2053         Intent serviceIntent = new Intent(controllerFactory.getServiceInterface());
2054         serviceIntent.setPackage(packageName);
2055 
2056         Set<UserHandle> profiles;
2057         if (mFeatureFlags.imsResolverUserAware()) {
2058             profiles = getActiveUsers();
2059         } else {
2060             profiles = Collections.singleton(mContext.getUser());
2061         }
2062         Log.v(TAG, "searchForImsServices: package=" + packageName + ", users=" + profiles);
2063 
2064         PackageManager packageManager = mContext.getPackageManager();
2065         for (UserHandle handle : profiles) {
2066             for (ResolveInfo entry : packageManager.queryIntentServicesAsUser(serviceIntent,
2067                     PackageManager.GET_META_DATA, handle)) {
2068                 ServiceInfo serviceInfo = entry.serviceInfo;
2069 
2070                 if (serviceInfo != null) {
2071                     ComponentName name = new ComponentName(serviceInfo.packageName,
2072                             serviceInfo.name);
2073                     ImsServiceInfo info = getInfoFromCache(infos, name);
2074                     if (info != null) {
2075                         info.users.add(handle);
2076                         Log.d(TAG, "service modify users:" + info);
2077                         continue;
2078                     } else {
2079                         info = new ImsServiceInfo(name);
2080                         info.users.add(handle);
2081                     }
2082                     info.controllerFactory = controllerFactory;
2083 
2084                     // we will allow the manifest method of declaring manifest features in two
2085                     // cases:
2086 
2087                     // 1) it is the device overlay "default" ImsService, where the features do not
2088                     // change (the new method can still be used if the default does not define
2089                     // manifest entries).
2090                     // 2) using the "compat" ImsService, which only supports manifest query.
2091                     if (isDeviceService(info)
2092                             || mImsServiceControllerFactoryCompat == controllerFactory) {
2093                         if (serviceInfo.metaData != null) {
2094                             if (serviceInfo.metaData.getBoolean(METADATA_MMTEL_FEATURE, false)) {
2095                                 info.addFeatureForAllSlots(mNumSlots, ImsFeature.FEATURE_MMTEL);
2096                                 // only allow FEATURE_EMERGENCY_MMTEL if FEATURE_MMTEL is defined.
2097                                 if (serviceInfo.metaData.getBoolean(
2098                                         METADATA_EMERGENCY_MMTEL_FEATURE,
2099                                         false)) {
2100                                     info.addFeatureForAllSlots(mNumSlots,
2101                                             ImsFeature.FEATURE_EMERGENCY_MMTEL);
2102                                 }
2103                             }
2104                             if (serviceInfo.metaData.getBoolean(METADATA_RCS_FEATURE, false)) {
2105                                 info.addFeatureForAllSlots(mNumSlots, ImsFeature.FEATURE_RCS);
2106                             }
2107                         }
2108                         // Only dynamic query if we are not a compat version of ImsService and the
2109                         // default service.
2110                         if (mImsServiceControllerFactoryCompat != controllerFactory
2111                                 && info.getSupportedFeatures().isEmpty()) {
2112                             // metadata empty, try dynamic query instead
2113                             info.featureFromMetadata = false;
2114                         }
2115                     } else {
2116                         // We are a carrier service and not using the compat version of ImsService.
2117                         info.featureFromMetadata = false;
2118                     }
2119                     Log.d(TAG, "service name: " + info.name + ", manifest query: "
2120                             + info.featureFromMetadata + ", users: " + info.users);
2121                     // Check manifest permission to be sure that the service declares the correct
2122                     // permissions. Overridden if the METADATA_OVERRIDE_PERM_CHECK metadata is set
2123                     // to true.
2124                     // NOTE: METADATA_OVERRIDE_PERM_CHECK should only be set for testing.
2125                     if (TextUtils.equals(serviceInfo.permission,
2126                             Manifest.permission.BIND_IMS_SERVICE)
2127                             || serviceInfo.metaData.getBoolean(METADATA_OVERRIDE_PERM_CHECK,
2128                             false)) {
2129                         infos.add(info);
2130                     } else {
2131                         Log.w(TAG, "ImsService is not protected with BIND_IMS_SERVICE permission: "
2132                                 + info.name);
2133                     }
2134                 }
2135             }
2136         }
2137         return infos;
2138     }
2139 
setSubId(int slotId, int subId)2140     private void setSubId(int slotId, int subId) {
2141         synchronized (mSlotIdToSubIdMap) {
2142             mSlotIdToSubIdMap.put(slotId, subId);
2143         }
2144     }
2145 
getSubId(int slotId)2146     private int getSubId(int slotId) {
2147         synchronized (mSlotIdToSubIdMap) {
2148             return mSlotIdToSubIdMap.get(slotId, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
2149         }
2150     }
removeSlotId(int slotId)2151     private void removeSlotId(int slotId) {
2152         synchronized (mSlotIdToSubIdMap) {
2153             mSlotIdToSubIdMap.delete(slotId);
2154         }
2155     }
2156 
2157     // Dump is called on the main thread, since ImsResolver Handler is also handled on main thread,
2158     // we shouldn't need to worry about concurrent access of private params.
dump(FileDescriptor fd, PrintWriter printWriter, String[] args)2159     public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
2160         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
2161         pw.println("ImsResolver:");
2162         pw.increaseIndent();
2163         pw.println("Configurations:");
2164         pw.increaseIndent();
2165         pw.println("Device:");
2166         pw.increaseIndent();
2167         synchronized (mDeviceServices) {
2168             for (Integer i : mDeviceServices.keySet()) {
2169                 pw.println(ImsFeature.FEATURE_LOG_MAP.get(i) + " -> " + mDeviceServices.get(i));
2170             }
2171         }
2172         pw.decreaseIndent();
2173         pw.println("Carrier: ");
2174         pw.increaseIndent();
2175         for (int i = 0; i < mNumSlots; i++) {
2176             for (int j = 0; j < MmTelFeature.FEATURE_MAX; j++) {
2177                 pw.print("slot=");
2178                 pw.print(i);
2179                 pw.print(", feature=");
2180                 pw.print(ImsFeature.FEATURE_LOG_MAP.getOrDefault(j, "?"));
2181                 pw.println(": ");
2182                 pw.increaseIndent();
2183                 String name = getCarrierConfiguredPackageName(i, j);
2184                 pw.println(TextUtils.isEmpty(name) ? "none" : name);
2185                 pw.decreaseIndent();
2186             }
2187         }
2188         pw.decreaseIndent();
2189         pw.decreaseIndent();
2190         pw.println("Cached ImsServices:");
2191         pw.increaseIndent();
2192         for (ImsServiceInfo i : mInstalledServicesCache.values()) {
2193             pw.println(i);
2194         }
2195         pw.decreaseIndent();
2196         pw.println("Active controllers:");
2197         pw.increaseIndent();
2198         for (ImsServiceController c : mActiveControllers.values()) {
2199             pw.println(c);
2200             pw.increaseIndent();
2201             c.dump(pw);
2202             pw.decreaseIndent();
2203         }
2204         pw.decreaseIndent();
2205         pw.println("Connection Repository Log:");
2206         pw.increaseIndent();
2207         mRepo.dump(pw);
2208         pw.decreaseIndent();
2209         pw.println("Event Log:");
2210         pw.increaseIndent();
2211         mEventLog.dump(pw);
2212         pw.decreaseIndent();
2213     }
2214 }
2215