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