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