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