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