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