Home | History | Annotate | Download | only in ims
      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.Nullable;
     21 import android.content.BroadcastReceiver;
     22 import android.content.ComponentName;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.content.IntentFilter;
     26 import android.content.pm.PackageManager;
     27 import android.content.pm.ResolveInfo;
     28 import android.content.pm.ServiceInfo;
     29 import android.os.Handler;
     30 import android.os.Looper;
     31 import android.os.Message;
     32 import android.os.PersistableBundle;
     33 import android.os.RemoteException;
     34 import android.os.UserHandle;
     35 import android.telephony.CarrierConfigManager;
     36 import android.telephony.SubscriptionManager;
     37 import android.telephony.ims.ImsService;
     38 import android.telephony.ims.aidl.IImsConfig;
     39 import android.telephony.ims.aidl.IImsMmTelFeature;
     40 import android.telephony.ims.aidl.IImsRcsFeature;
     41 import android.telephony.ims.aidl.IImsRegistration;
     42 import android.telephony.ims.feature.ImsFeature;
     43 import android.telephony.ims.stub.ImsFeatureConfiguration;
     44 import android.text.TextUtils;
     45 import android.util.Log;
     46 import android.util.SparseArray;
     47 
     48 import com.android.ims.internal.IImsServiceFeatureCallback;
     49 import com.android.internal.annotations.VisibleForTesting;
     50 import com.android.internal.os.SomeArgs;
     51 
     52 import java.util.ArrayList;
     53 import java.util.HashMap;
     54 import java.util.HashSet;
     55 import java.util.List;
     56 import java.util.Map;
     57 import java.util.Objects;
     58 import java.util.Set;
     59 import java.util.stream.Collectors;
     60 import java.util.stream.Stream;
     61 
     62 /**
     63  * Creates a list of ImsServices that are available to bind to based on the Device configuration
     64  * overlay value "config_ims_package" and Carrier Configuration value
     65  * "config_ims_package_override_string".
     66  * These ImsServices are then bound to in the following order:
     67  *
     68  * 1. Carrier Config defined override value per SIM.
     69  * 2. Device overlay default value (including no SIM case).
     70  *
     71  * ImsManager can then retrieve the binding to the correct ImsService using
     72  * {@link #getImsServiceControllerAndListen} on a per-slot and per feature basis.
     73  */
     74 
     75 public class ImsResolver implements ImsServiceController.ImsServiceControllerCallbacks {
     76 
     77     private static final String TAG = "ImsResolver";
     78 
     79     public static final String METADATA_EMERGENCY_MMTEL_FEATURE =
     80             "android.telephony.ims.EMERGENCY_MMTEL_FEATURE";
     81     public static final String METADATA_MMTEL_FEATURE = "android.telephony.ims.MMTEL_FEATURE";
     82     public static final String METADATA_RCS_FEATURE = "android.telephony.ims.RCS_FEATURE";
     83     // Overrides the sanity permission check of android.permission.BIND_IMS_SERVICE for any
     84     // ImsService that is connecting to the platform.
     85     // This should ONLY be used for testing and should not be used in production ImsServices.
     86     private static final String METADATA_OVERRIDE_PERM_CHECK = "override_bind_check";
     87 
     88     // Based on updates from PackageManager
     89     private static final int HANDLER_ADD_PACKAGE = 0;
     90     // Based on updates from PackageManager
     91     private static final int HANDLER_REMOVE_PACKAGE = 1;
     92     // Based on updates from CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED
     93     private static final int HANDLER_CONFIG_CHANGED = 2;
     94     // A query has been started for an ImsService to relay the features they support.
     95     private static final int HANDLER_START_DYNAMIC_FEATURE_QUERY = 3;
     96     // A query to request ImsService features has completed or the ImsService has updated features.
     97     private static final int HANDLER_DYNAMIC_FEATURE_CHANGE = 4;
     98     // Testing: Overrides the current configuration for ImsService binding
     99     private static final int HANDLER_OVERRIDE_IMS_SERVICE_CONFIG = 5;
    100 
    101     // Delay between dynamic ImsService queries.
    102     private static final int DELAY_DYNAMIC_QUERY_MS = 5000;
    103 
    104 
    105     /**
    106      * Stores information about an ImsService, including the package name, class name, and features
    107      * that the service supports.
    108      */
    109     @VisibleForTesting
    110     public static class ImsServiceInfo {
    111         public ComponentName name;
    112         // Determines if features were created from metadata in the manifest or through dynamic
    113         // query.
    114         public boolean featureFromMetadata = true;
    115         public ImsServiceControllerFactory controllerFactory;
    116 
    117         // Map slotId->Feature
    118         private final HashSet<ImsFeatureConfiguration.FeatureSlotPair> mSupportedFeatures;
    119         private final int mNumSlots;
    120 
    121         public ImsServiceInfo(int numSlots) {
    122             mNumSlots = numSlots;
    123             mSupportedFeatures = new HashSet<>();
    124         }
    125 
    126         void addFeatureForAllSlots(int feature) {
    127             for (int i = 0; i < mNumSlots; i++) {
    128                 mSupportedFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(i, feature));
    129             }
    130         }
    131 
    132         void replaceFeatures(Set<ImsFeatureConfiguration.FeatureSlotPair> newFeatures) {
    133             mSupportedFeatures.clear();
    134             mSupportedFeatures.addAll(newFeatures);
    135         }
    136 
    137         @VisibleForTesting
    138         public HashSet<ImsFeatureConfiguration.FeatureSlotPair> getSupportedFeatures() {
    139             return mSupportedFeatures;
    140         }
    141 
    142         @Override
    143         public boolean equals(Object o) {
    144             if (this == o) return true;
    145             if (o == null || getClass() != o.getClass()) return false;
    146 
    147             ImsServiceInfo that = (ImsServiceInfo) o;
    148 
    149             if (name != null ? !name.equals(that.name) : that.name != null) return false;
    150             if (!mSupportedFeatures.equals(that.mSupportedFeatures)) {
    151                 return false;
    152             }
    153             return controllerFactory != null ? controllerFactory.equals(that.controllerFactory)
    154                     : that.controllerFactory == null;
    155         }
    156 
    157         @Override
    158         public int hashCode() {
    159             // We do not include mSupportedFeatures in hashcode because the internal structure
    160             // changes after adding.
    161             int result = name != null ? name.hashCode() : 0;
    162             result = 31 * result + (controllerFactory != null ? controllerFactory.hashCode() : 0);
    163             return result;
    164         }
    165 
    166         @Override
    167         public String toString() {
    168             StringBuilder res = new StringBuilder();
    169             res.append("[ImsServiceInfo] name=");
    170             res.append(name);
    171             res.append(", supportedFeatures=[ ");
    172             for (ImsFeatureConfiguration.FeatureSlotPair feature : mSupportedFeatures) {
    173                 res.append("(");
    174                 res.append(feature.slotId);
    175                 res.append(",");
    176                 res.append(feature.featureType);
    177                 res.append(") ");
    178             }
    179             return res.toString();
    180         }
    181     }
    182 
    183     // Receives broadcasts from the system involving changes to the installed applications. If
    184     // an ImsService that we are configured to use is installed, we must bind to it.
    185     private BroadcastReceiver mAppChangedReceiver = new BroadcastReceiver() {
    186         @Override
    187         public void onReceive(Context context, Intent intent) {
    188             final String action = intent.getAction();
    189             final String packageName = intent.getData().getSchemeSpecificPart();
    190             switch (action) {
    191                 case Intent.ACTION_PACKAGE_ADDED:
    192                     // intentional fall-through
    193                 case Intent.ACTION_PACKAGE_REPLACED:
    194                     // intentional fall-through
    195                 case Intent.ACTION_PACKAGE_CHANGED:
    196                     mHandler.obtainMessage(HANDLER_ADD_PACKAGE, packageName).sendToTarget();
    197                     break;
    198                 case Intent.ACTION_PACKAGE_REMOVED:
    199                     mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE, packageName).sendToTarget();
    200                     break;
    201                 default:
    202                     return;
    203             }
    204         }
    205     };
    206 
    207     // Receives the broadcast that a new Carrier Config has been loaded in order to possibly
    208     // unbind from one service and bind to another.
    209     private BroadcastReceiver mConfigChangedReceiver = new BroadcastReceiver() {
    210         @Override
    211         public void onReceive(Context context, Intent intent) {
    212 
    213             int slotId = intent.getIntExtra(CarrierConfigManager.EXTRA_SLOT_INDEX,
    214                     SubscriptionManager.INVALID_SIM_SLOT_INDEX);
    215 
    216             if (slotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
    217                 Log.i(TAG, "Received SIM change for invalid slot id.");
    218                 return;
    219             }
    220 
    221             Log.i(TAG, "Received Carrier Config Changed for SlotId: " + slotId);
    222 
    223             mHandler.obtainMessage(HANDLER_CONFIG_CHANGED, slotId).sendToTarget();
    224         }
    225     };
    226 
    227     /**
    228      * Testing interface used to mock SubscriptionManager in testing
    229      */
    230     @VisibleForTesting
    231     public interface SubscriptionManagerProxy {
    232         /**
    233          * Mock-able interface for {@link SubscriptionManager#getSubId(int)} used for testing.
    234          */
    235         int getSubId(int slotId);
    236         /**
    237          * Mock-able interface for {@link SubscriptionManager#getSlotIndex(int)} used for testing.
    238          */
    239         int getSlotIndex(int subId);
    240     }
    241 
    242     private SubscriptionManagerProxy mSubscriptionManagerProxy = new SubscriptionManagerProxy() {
    243         @Override
    244         public int getSubId(int slotId) {
    245             int[] subIds = SubscriptionManager.getSubId(slotId);
    246             if (subIds != null) {
    247                 // This is done in all other places getSubId is used.
    248                 return subIds[0];
    249             }
    250             return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
    251         }
    252 
    253         @Override
    254         public int getSlotIndex(int subId) {
    255             return SubscriptionManager.getSlotIndex(subId);
    256         }
    257     };
    258 
    259     /**
    260      * Testing interface for injecting mock ImsServiceControllers.
    261      */
    262     @VisibleForTesting
    263     public interface ImsServiceControllerFactory {
    264         /**
    265          * @return the Service Interface String used for binding the ImsService.
    266          */
    267         String getServiceInterface();
    268         /**
    269          * @return the ImsServiceController created using the context and componentName supplied.
    270          */
    271         ImsServiceController create(Context context, ComponentName componentName,
    272                 ImsServiceController.ImsServiceControllerCallbacks callbacks);
    273     }
    274 
    275     private ImsServiceControllerFactory mImsServiceControllerFactory =
    276             new ImsServiceControllerFactory() {
    277 
    278         @Override
    279         public String getServiceInterface() {
    280             return ImsService.SERVICE_INTERFACE;
    281         }
    282 
    283         @Override
    284         public ImsServiceController create(Context context, ComponentName componentName,
    285                 ImsServiceController.ImsServiceControllerCallbacks callbacks) {
    286             return new ImsServiceController(context, componentName, callbacks);
    287         }
    288     };
    289 
    290     /**
    291      * Used for testing.
    292      */
    293     @VisibleForTesting
    294     public interface ImsDynamicQueryManagerFactory {
    295         ImsServiceFeatureQueryManager create(Context context,
    296                 ImsServiceFeatureQueryManager.Listener listener);
    297     }
    298 
    299     private ImsServiceControllerFactory mImsServiceControllerFactoryCompat =
    300             new ImsServiceControllerFactory() {
    301                 @Override
    302                 public String getServiceInterface() {
    303                     return android.telephony.ims.compat.ImsService.SERVICE_INTERFACE;
    304                 }
    305 
    306                 @Override
    307                 public ImsServiceController create(Context context, ComponentName componentName,
    308                         ImsServiceController.ImsServiceControllerCallbacks callbacks) {
    309                     return new ImsServiceControllerCompat(context, componentName, callbacks);
    310                 }
    311             };
    312 
    313     private ImsServiceControllerFactory mImsServiceControllerFactoryStaticBindingCompat =
    314             new ImsServiceControllerFactory() {
    315                 @Override
    316                 public String getServiceInterface() {
    317                     // The static method of binding does not use service interfaces.
    318                     return null;
    319                 }
    320 
    321                 @Override
    322                 public ImsServiceController create(Context context, ComponentName componentName,
    323                         ImsServiceController.ImsServiceControllerCallbacks callbacks) {
    324                     return new ImsServiceControllerStaticCompat(context, componentName, callbacks);
    325                 }
    326             };
    327 
    328     private ImsDynamicQueryManagerFactory mDynamicQueryManagerFactory =
    329             ImsServiceFeatureQueryManager::new;
    330 
    331     private final CarrierConfigManager mCarrierConfigManager;
    332     private final Context mContext;
    333     // Locks mBoundImsServicesByFeature only. Be careful to avoid deadlocks from
    334     // ImsServiceController callbacks.
    335     private final Object mBoundServicesLock = new Object();
    336     private final int mNumSlots;
    337     private final boolean mIsDynamicBinding;
    338     // Package name of the default device service.
    339     private String mDeviceService;
    340 
    341     // Synchronize all messages on a handler to ensure that the cache includes the most recent
    342     // version of the installed ImsServices.
    343     private Handler mHandler = new Handler(Looper.getMainLooper(), (msg) -> {
    344         switch (msg.what) {
    345             case HANDLER_ADD_PACKAGE: {
    346                 String packageName = (String) msg.obj;
    347                 maybeAddedImsService(packageName);
    348                 break;
    349             }
    350             case HANDLER_REMOVE_PACKAGE: {
    351                 String packageName = (String) msg.obj;
    352                 maybeRemovedImsService(packageName);
    353                 break;
    354             }
    355             case HANDLER_CONFIG_CHANGED: {
    356                 int slotId = (Integer) msg.obj;
    357                 carrierConfigChanged(slotId);
    358                 break;
    359             }
    360             case HANDLER_START_DYNAMIC_FEATURE_QUERY: {
    361                 ImsServiceInfo info = (ImsServiceInfo) msg.obj;
    362                 startDynamicQuery(info);
    363                 break;
    364             }
    365             case HANDLER_DYNAMIC_FEATURE_CHANGE: {
    366                 SomeArgs args = (SomeArgs) msg.obj;
    367                 ComponentName name = (ComponentName) args.arg1;
    368                 Set<ImsFeatureConfiguration.FeatureSlotPair> features =
    369                         (Set<ImsFeatureConfiguration.FeatureSlotPair>) args.arg2;
    370                 args.recycle();
    371                 dynamicQueryComplete(name, features);
    372                 break;
    373             }
    374             case HANDLER_OVERRIDE_IMS_SERVICE_CONFIG: {
    375                 int slotId = msg.arg1;
    376                 // arg2 will be equal to 1 if it is a carrier service.
    377                 boolean isCarrierImsService = (msg.arg2 == 1);
    378                 String packageName = (String) msg.obj;
    379                 if (isCarrierImsService) {
    380                     Log.i(TAG, "overriding carrier ImsService - slot=" + slotId + " packageName="
    381                             + packageName);
    382                     maybeRebindService(slotId, packageName);
    383                 } else {
    384                     Log.i(TAG, "overriding device ImsService -  packageName=" + packageName);
    385                     if (packageName == null || packageName.isEmpty()) {
    386                         unbindImsService(getImsServiceInfoFromCache(mDeviceService));
    387                     }
    388                     mDeviceService = packageName;
    389                     ImsServiceInfo deviceInfo = getImsServiceInfoFromCache(mDeviceService);
    390                     if (deviceInfo == null) {
    391                         // The package name is either "" or does not exist on the device.
    392                         break;
    393                     }
    394                     if (deviceInfo.featureFromMetadata) {
    395                         bindImsService(deviceInfo);
    396                     } else {
    397                         // newly added ImsServiceInfo that has not had features queried yet. Start
    398                         // async bind and query features.
    399                         scheduleQueryForFeatures(deviceInfo);
    400                     }
    401                 }
    402                 break;
    403             }
    404             default:
    405                 return false;
    406         }
    407         return true;
    408     });
    409 
    410     // Results from dynamic queries to ImsService regarding the features they support.
    411     private ImsServiceFeatureQueryManager.Listener mDynamicQueryListener =
    412             new ImsServiceFeatureQueryManager.Listener() {
    413 
    414                 @Override
    415                 public void onComplete(ComponentName name,
    416                         Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
    417                     Log.d(TAG, "onComplete called for name: " + name + "features:"
    418                             + printFeatures(features));
    419                     handleFeaturesChanged(name, features);
    420                 }
    421 
    422                 @Override
    423                 public void onError(ComponentName name) {
    424                     Log.w(TAG, "onError: " + name + "returned with an error result");
    425                     scheduleQueryForFeatures(name, DELAY_DYNAMIC_QUERY_MS);
    426                 }
    427             };
    428 
    429     // Array index corresponds to slot Id associated with the service package name.
    430     private String[] mCarrierServices;
    431     // List index corresponds to Slot Id, Maps ImsFeature.FEATURE->bound ImsServiceController
    432     // Locked on mBoundServicesLock
    433     private List<SparseArray<ImsServiceController>> mBoundImsServicesByFeature;
    434     // not locked, only accessed on a handler thread.
    435     private Map<ComponentName, ImsServiceInfo> mInstalledServicesCache = new HashMap<>();
    436     // not locked, only accessed on a handler thread.
    437     private Map<ComponentName, ImsServiceController> mActiveControllers = new HashMap<>();
    438     // Only used as the Component name for legacy ImsServices that did not use dynamic binding.
    439     private final ComponentName mStaticComponent;
    440     private ImsServiceFeatureQueryManager mFeatureQueryManager;
    441 
    442     public ImsResolver(Context context, String defaultImsPackageName, int numSlots,
    443             boolean isDynamicBinding) {
    444         mContext = context;
    445         mDeviceService = defaultImsPackageName;
    446         mNumSlots = numSlots;
    447         mIsDynamicBinding = isDynamicBinding;
    448         mStaticComponent = new ComponentName(mContext, ImsResolver.class);
    449         if (!mIsDynamicBinding) {
    450             Log.i(TAG, "ImsResolver initialized with static binding.");
    451             mDeviceService = mStaticComponent.getPackageName();
    452         }
    453         mCarrierConfigManager = (CarrierConfigManager) mContext.getSystemService(
    454                 Context.CARRIER_CONFIG_SERVICE);
    455         mCarrierServices = new String[numSlots];
    456         mBoundImsServicesByFeature = Stream.generate(SparseArray<ImsServiceController>::new)
    457                 .limit(mNumSlots).collect(Collectors.toList());
    458 
    459         // Only register for Package/CarrierConfig updates if dynamic binding.
    460         if(mIsDynamicBinding) {
    461             IntentFilter appChangedFilter = new IntentFilter();
    462             appChangedFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
    463             appChangedFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    464             appChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
    465             appChangedFilter.addDataScheme("package");
    466             context.registerReceiverAsUser(mAppChangedReceiver, UserHandle.ALL, appChangedFilter,
    467                     null,
    468                     null);
    469 
    470             context.registerReceiver(mConfigChangedReceiver, new IntentFilter(
    471                     CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
    472         }
    473     }
    474 
    475     @VisibleForTesting
    476     public void setSubscriptionManagerProxy(SubscriptionManagerProxy proxy) {
    477         mSubscriptionManagerProxy = proxy;
    478     }
    479 
    480     @VisibleForTesting
    481     public void setImsServiceControllerFactory(ImsServiceControllerFactory factory) {
    482         mImsServiceControllerFactory = factory;
    483     }
    484 
    485     @VisibleForTesting
    486     public Handler getHandler() {
    487         return mHandler;
    488     }
    489 
    490     @VisibleForTesting
    491     public void setImsDynamicQueryManagerFactory(ImsDynamicQueryManagerFactory m) {
    492         mDynamicQueryManagerFactory = m;
    493     }
    494 
    495     /**
    496      * Needs to be called after the constructor to first populate the cache and possibly bind to
    497      * ImsServices.
    498      */
    499     public void initPopulateCacheAndStartBind() {
    500         Log.i(TAG, "Initializing cache and binding.");
    501         mFeatureQueryManager = mDynamicQueryManagerFactory.create(mContext, mDynamicQueryListener);
    502         // Populates the CarrierConfig override package names for each slot
    503         mHandler.obtainMessage(HANDLER_CONFIG_CHANGED,
    504                 SubscriptionManager.INVALID_SIM_SLOT_INDEX).sendToTarget();
    505         // Starts first bind to the system.
    506         mHandler.obtainMessage(HANDLER_ADD_PACKAGE, null).sendToTarget();
    507     }
    508 
    509     /**
    510      * Notify ImsService to enable IMS for the framework. This will trigger IMS registration and
    511      * trigger ImsFeature status updates.
    512      */
    513     public void enableIms(int slotId) {
    514         SparseArray<ImsServiceController> controllers = getImsServiceControllers(slotId);
    515         if (controllers != null) {
    516             for (int i = 0; i < controllers.size(); i++) {
    517                 int key = controllers.keyAt(i);
    518                 controllers.get(key).enableIms(slotId);
    519             }
    520         }
    521     }
    522 
    523     /**
    524      * Notify ImsService to disable IMS for the framework. This will trigger IMS de-registration and
    525      * trigger ImsFeature capability status to become false.
    526      */
    527     public void disableIms(int slotId) {
    528         SparseArray<ImsServiceController> controllers = getImsServiceControllers(slotId);
    529         if (controllers != null) {
    530             for (int i = 0; i < controllers.size(); i++) {
    531                 int key = controllers.keyAt(i);
    532                 controllers.get(key).disableIms(slotId);
    533             }
    534         }
    535     }
    536 
    537     /**
    538      * Returns the {@link IImsMmTelFeature} that corresponds to the given slot Id or {@link null} if
    539      * the service is not available. If an IImsMMTelFeature is available, the
    540      * {@link IImsServiceFeatureCallback} callback is registered as a listener for feature updates.
    541      * @param slotId The SIM slot that we are requesting the {@link IImsMmTelFeature} for.
    542      * @param callback Listener that will send updates to ImsManager when there are updates to
    543      * the feature.
    544      * @return {@link IImsMmTelFeature} interface or {@link null} if it is unavailable.
    545      */
    546     public IImsMmTelFeature getMmTelFeatureAndListen(int slotId,
    547             IImsServiceFeatureCallback callback) {
    548         ImsServiceController controller = getImsServiceControllerAndListen(slotId,
    549                 ImsFeature.FEATURE_MMTEL, callback);
    550         return (controller != null) ? controller.getMmTelFeature(slotId) : null;
    551     }
    552 
    553     /**
    554      * Returns the {@link IImsRcsFeature} that corresponds to the given slot Id for emergency
    555      * calling or {@link null} if the service is not available. If an IImsMMTelFeature is
    556      * available, the {@link IImsServiceFeatureCallback} callback is registered as a listener for
    557      * feature updates.
    558      * @param slotId The SIM slot that we are requesting the {@link IImsRcsFeature} for.
    559      * @param callback listener that will send updates to ImsManager when there are updates to
    560      * the feature.
    561      * @return {@link IImsRcsFeature} interface or {@link null} if it is unavailable.
    562      */
    563     public IImsRcsFeature getRcsFeatureAndListen(int slotId, IImsServiceFeatureCallback callback) {
    564         ImsServiceController controller = getImsServiceControllerAndListen(slotId,
    565                 ImsFeature.FEATURE_RCS, callback);
    566         return (controller != null) ? controller.getRcsFeature(slotId) : null;
    567     }
    568 
    569     /**
    570      * Returns the ImsRegistration structure associated with the slotId and feature specified.
    571      */
    572     public @Nullable IImsRegistration getImsRegistration(int slotId, int feature)
    573             throws RemoteException {
    574         ImsServiceController controller = getImsServiceController(slotId, feature);
    575         if (controller != null) {
    576             return controller.getRegistration(slotId);
    577         }
    578         return null;
    579     }
    580 
    581     /**
    582      * Returns the ImsConfig structure associated with the slotId and feature specified.
    583      */
    584     public @Nullable IImsConfig getImsConfig(int slotId, int feature)
    585             throws RemoteException {
    586         ImsServiceController controller = getImsServiceController(slotId, feature);
    587         if (controller != null) {
    588             return controller.getConfig(slotId);
    589         }
    590         return null;
    591     }
    592 
    593     @VisibleForTesting
    594     public ImsServiceController getImsServiceController(int slotId, int feature) {
    595         if (slotId < 0 || slotId >= mNumSlots) {
    596             return null;
    597         }
    598         ImsServiceController controller;
    599         synchronized (mBoundServicesLock) {
    600             SparseArray<ImsServiceController> services = mBoundImsServicesByFeature.get(slotId);
    601             if (services == null) {
    602                 return null;
    603             }
    604             controller = services.get(feature);
    605         }
    606         return controller;
    607     }
    608 
    609     private  SparseArray<ImsServiceController> getImsServiceControllers(int slotId) {
    610         if (slotId < 0 || slotId >= mNumSlots) {
    611             return null;
    612         }
    613         synchronized (mBoundServicesLock) {
    614             SparseArray<ImsServiceController> services = mBoundImsServicesByFeature.get(slotId);
    615             if (services == null) {
    616                 return null;
    617             }
    618             return services;
    619         }
    620     }
    621 
    622     @VisibleForTesting
    623     public ImsServiceController getImsServiceControllerAndListen(int slotId, int feature,
    624             IImsServiceFeatureCallback callback) {
    625         ImsServiceController controller = getImsServiceController(slotId, feature);
    626 
    627         if (controller != null) {
    628             controller.addImsServiceFeatureCallback(callback);
    629             return controller;
    630         }
    631         return null;
    632     }
    633 
    634     // Used for testing only.
    635     public boolean overrideImsServiceConfiguration(int slotId, boolean isCarrierService,
    636             String packageName) {
    637         if (slotId < 0 || slotId >= mNumSlots) {
    638             Log.w(TAG, "overrideImsServiceConfiguration: invalid slotId!");
    639             return false;
    640         }
    641 
    642         if (packageName == null) {
    643             Log.w(TAG, "overrideImsServiceConfiguration: null packageName!");
    644             return false;
    645         }
    646 
    647         // encode boolean to int for Message.
    648         int carrierService = isCarrierService ? 1 : 0;
    649         Message.obtain(mHandler, HANDLER_OVERRIDE_IMS_SERVICE_CONFIG, slotId, carrierService,
    650                 packageName).sendToTarget();
    651         return true;
    652     }
    653 
    654     // used for testing only.
    655     public String getImsServiceConfiguration(int slotId, boolean isCarrierService) {
    656         if (slotId < 0 || slotId >= mNumSlots) {
    657             Log.w(TAG, "getImsServiceConfiguration: invalid slotId!");
    658             return "";
    659         }
    660 
    661         return isCarrierService ? mCarrierServices[slotId] : mDeviceService;
    662     }
    663 
    664     private void putImsController(int slotId, int feature, ImsServiceController controller) {
    665         if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.FEATURE_INVALID
    666                 || feature >= ImsFeature.FEATURE_MAX) {
    667             Log.w(TAG, "putImsController received invalid parameters - slot: " + slotId
    668                     + ", feature: " + feature);
    669             return;
    670         }
    671         synchronized (mBoundServicesLock) {
    672             SparseArray<ImsServiceController> services = mBoundImsServicesByFeature.get(slotId);
    673             if (services == null) {
    674                 services = new SparseArray<>();
    675                 mBoundImsServicesByFeature.add(slotId, services);
    676             }
    677             Log.i(TAG, "ImsServiceController added on slot: " + slotId + " with feature: "
    678                     + feature + " using package: " + controller.getComponentName());
    679             services.put(feature, controller);
    680         }
    681     }
    682 
    683     private ImsServiceController removeImsController(int slotId, int feature) {
    684         if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.FEATURE_INVALID
    685                 || feature >= ImsFeature.FEATURE_MAX) {
    686             Log.w(TAG, "removeImsController received invalid parameters - slot: " + slotId
    687                     + ", feature: " + feature);
    688             return null;
    689         }
    690         synchronized (mBoundServicesLock) {
    691             SparseArray<ImsServiceController> services = mBoundImsServicesByFeature.get(slotId);
    692             if (services == null) {
    693                 return null;
    694             }
    695             ImsServiceController c = services.get(feature, null);
    696             if (c != null) {
    697                 Log.i(TAG, "ImsServiceController removed on slot: " + slotId + " with feature: "
    698                         + feature + " using package: " + c.getComponentName());
    699                 services.remove(feature);
    700             }
    701             return c;
    702         }
    703     }
    704 
    705     // Update the current cache with the new ImsService(s) if it has been added or update the
    706     // supported IMS features if they have changed.
    707     // Called from the handler ONLY
    708     private void maybeAddedImsService(String packageName) {
    709         Log.d(TAG, "maybeAddedImsService, packageName: " + packageName);
    710         List<ImsServiceInfo> infos = getImsServiceInfo(packageName);
    711         List<ImsServiceInfo> newlyAddedInfos = new ArrayList<>();
    712         for (ImsServiceInfo info : infos) {
    713             // Checking to see if the ComponentName is the same, so we can update the supported
    714             // features. Will only be one (if it exists), since it is a set.
    715             ImsServiceInfo match = getInfoByComponentName(mInstalledServicesCache, info.name);
    716             if (match != null) {
    717                 // for dynamic query the new "info" will have no supported features yet. Don't wipe
    718                 // out the cache for the existing features or update yet. Instead start a query
    719                 // for features dynamically.
    720                 if (info.featureFromMetadata) {
    721                     // update features in the cache
    722                     Log.i(TAG, "Updating features in cached ImsService: " + info.name);
    723                     Log.d(TAG, "Updating features - Old features: " + match + " new features: "
    724                             + info);
    725                     match.replaceFeatures(info.getSupportedFeatures());
    726                     updateImsServiceFeatures(info);
    727                 } else {
    728                     // start a query to get ImsService features
    729                     scheduleQueryForFeatures(info);
    730                 }
    731             } else {
    732                 Log.i(TAG, "Adding newly added ImsService to cache: " + info.name);
    733                 mInstalledServicesCache.put(info.name, info);
    734                 if (info.featureFromMetadata) {
    735                     newlyAddedInfos.add(info);
    736                 } else {
    737                     // newly added ImsServiceInfo that has not had features queried yet. Start async
    738                     // bind and query features.
    739                     scheduleQueryForFeatures(info);
    740                 }
    741             }
    742         }
    743         // Loop through the newly created ServiceInfos in a separate loop to make sure the cache
    744         // is fully updated.
    745         for (ImsServiceInfo info : newlyAddedInfos) {
    746             if (isActiveCarrierService(info)) {
    747                 // New ImsService is registered to active carrier services and must be newly
    748                 // bound.
    749                 bindImsService(info);
    750                 // Update existing device service features
    751                 updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
    752             } else if (isDeviceService(info)) {
    753                 // New ImsService is registered as device default and must be newly bound.
    754                 bindImsService(info);
    755             }
    756         }
    757     }
    758 
    759     // Remove the ImsService from the cache. At this point, the ImsService will have already been
    760     // killed.
    761     // Called from the handler ONLY
    762     private boolean maybeRemovedImsService(String packageName) {
    763         ImsServiceInfo match = getInfoByPackageName(mInstalledServicesCache, packageName);
    764         if (match != null) {
    765             mInstalledServicesCache.remove(match.name);
    766             Log.i(TAG, "Removing ImsService: " + match.name);
    767             unbindImsService(match);
    768             updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
    769             return true;
    770         }
    771         return false;
    772     }
    773 
    774     // Returns true if the CarrierConfig that has been loaded includes this ImsServiceInfo
    775     // package name.
    776     // Called from Handler ONLY
    777     private boolean isActiveCarrierService(ImsServiceInfo info) {
    778         for (int i = 0; i < mNumSlots; i++) {
    779             if (TextUtils.equals(mCarrierServices[i], info.name.getPackageName())) {
    780                 return true;
    781             }
    782         }
    783         return false;
    784     }
    785 
    786     private boolean isDeviceService(ImsServiceInfo info) {
    787         return TextUtils.equals(mDeviceService, info.name.getPackageName());
    788     }
    789 
    790     private int getSlotForActiveCarrierService(ImsServiceInfo info) {
    791         for (int i = 0; i < mNumSlots; i++) {
    792             if (TextUtils.equals(mCarrierServices[i], info.name.getPackageName())) {
    793                 return i;
    794             }
    795         }
    796         return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
    797     }
    798 
    799     private ImsServiceController getControllerByServiceInfo(
    800             Map<ComponentName, ImsServiceController> searchMap, ImsServiceInfo matchValue) {
    801         return searchMap.values().stream()
    802                 .filter(c -> Objects.equals(c.getComponentName(), matchValue.name))
    803                 .findFirst().orElse(null);
    804     }
    805 
    806     private ImsServiceInfo getInfoByPackageName(Map<ComponentName, ImsServiceInfo> searchMap,
    807             String matchValue) {
    808         return searchMap.values().stream()
    809                 .filter((i) -> Objects.equals(i.name.getPackageName(), matchValue))
    810                 .findFirst().orElse(null);
    811     }
    812 
    813     private ImsServiceInfo getInfoByComponentName(
    814             Map<ComponentName, ImsServiceInfo> searchMap, ComponentName matchValue) {
    815         return searchMap.get(matchValue);
    816     }
    817 
    818     // Creates new features in active ImsServices and removes obsolete cached features. If
    819     // cachedInfo == null, then newInfo is assumed to be a new ImsService and will have all features
    820     // created.
    821     private void updateImsServiceFeatures(ImsServiceInfo newInfo) {
    822         if (newInfo == null) {
    823             return;
    824         }
    825         ImsServiceController controller = getControllerByServiceInfo(mActiveControllers, newInfo);
    826         // Will return zero if these features are overridden or it should not currently have any
    827         // features because it is not carrier/device.
    828         HashSet<ImsFeatureConfiguration.FeatureSlotPair> features =
    829                 calculateFeaturesToCreate(newInfo);
    830         if (shouldFeaturesCauseBind(features)) {
    831             try {
    832                 if (controller != null) {
    833                     Log.i(TAG, "Updating features for ImsService: "
    834                             + controller.getComponentName());
    835                     Log.d(TAG, "Updating Features - New Features: " + features);
    836                     controller.changeImsServiceFeatures(features);
    837                 } else {
    838                     Log.i(TAG, "updateImsServiceFeatures: unbound with active features, rebinding");
    839                     bindImsServiceWithFeatures(newInfo, features);
    840                 }
    841                 // If the carrier service features have changed, the device features will also
    842                 // need to be recalculated.
    843                 if (isActiveCarrierService(newInfo)
    844                         // Prevent infinite recursion from bad behavior
    845                         && !TextUtils.equals(newInfo.name.getPackageName(), mDeviceService)) {
    846                     Log.i(TAG, "Updating device default");
    847                     updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
    848                 }
    849             } catch (RemoteException e) {
    850                 Log.e(TAG, "updateImsServiceFeatures: Remote Exception: " + e.getMessage());
    851             }
    852         // Don't stay bound if the ImsService is providing no features.
    853         } else if (controller != null) {
    854             Log.i(TAG, "Unbinding: features = 0 for ImsService: " + controller.getComponentName());
    855             unbindImsService(newInfo);
    856         }
    857     }
    858 
    859     // Bind to an ImsService and wait for the service to be connected to create ImsFeatures.
    860     private void bindImsService(ImsServiceInfo info) {
    861         if (info == null) {
    862             return;
    863         }
    864         HashSet<ImsFeatureConfiguration.FeatureSlotPair> features = calculateFeaturesToCreate(info);
    865         bindImsServiceWithFeatures(info, features);
    866     }
    867 
    868     private void bindImsServiceWithFeatures(ImsServiceInfo info,
    869             HashSet<ImsFeatureConfiguration.FeatureSlotPair> features) {
    870         // Only bind if there are features that will be created by the service.
    871         if (shouldFeaturesCauseBind(features)) {
    872             // Check to see if an active controller already exists
    873             ImsServiceController controller = getControllerByServiceInfo(mActiveControllers, info);
    874             if (controller != null) {
    875                 Log.i(TAG, "ImsService connection exists, updating features " + features);
    876                 try {
    877                     controller.changeImsServiceFeatures(features);
    878                     // Features have been set, there was an error adding/removing. When the
    879                     // controller recovers, it will add/remove again.
    880                 } catch (RemoteException e) {
    881                     Log.w(TAG, "bindImsService: error=" + e.getMessage());
    882                 }
    883             } else {
    884                 controller = info.controllerFactory.create(mContext, info.name, this);
    885                 Log.i(TAG, "Binding ImsService: " + controller.getComponentName()
    886                         + " with features: " + features);
    887                 controller.bind(features);
    888             }
    889             mActiveControllers.put(info.name, controller);
    890         }
    891     }
    892 
    893     // Clean up and unbind from an ImsService
    894     private void unbindImsService(ImsServiceInfo info) {
    895         if (info == null) {
    896             return;
    897         }
    898         ImsServiceController controller = getControllerByServiceInfo(mActiveControllers, info);
    899         if (controller != null) {
    900             // Calls imsServiceFeatureRemoved on all features in the controller
    901             try {
    902                 Log.i(TAG, "Unbinding ImsService: " + controller.getComponentName());
    903                 controller.unbind();
    904             } catch (RemoteException e) {
    905                 Log.e(TAG, "unbindImsService: Remote Exception: " + e.getMessage());
    906             }
    907             mActiveControllers.remove(info.name);
    908         }
    909     }
    910 
    911     // Calculate which features an ImsServiceController will need. If it is the carrier specific
    912     // ImsServiceController, it will be granted all of the features it requests on the associated
    913     // slot. If it is the device ImsService, it will get all of the features not covered by the
    914     // carrier implementation.
    915     private HashSet<ImsFeatureConfiguration.FeatureSlotPair> calculateFeaturesToCreate(
    916             ImsServiceInfo info) {
    917         HashSet<ImsFeatureConfiguration.FeatureSlotPair> imsFeaturesBySlot = new HashSet<>();
    918         // Check if the info is a carrier service
    919         int slotId = getSlotForActiveCarrierService(info);
    920         if (slotId != SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
    921             imsFeaturesBySlot.addAll(info.getSupportedFeatures().stream()
    922                     // Match slotId with feature slotId.
    923                     .filter(feature -> slotId == feature.slotId)
    924                     .collect(Collectors.toList()));
    925         } else if (isDeviceService(info)) {
    926             // For all slots that are not currently using a carrier ImsService, enable all features
    927             // for the device default.
    928             for (int i = 0; i < mNumSlots; i++) {
    929                 final int currSlotId = i;
    930                 ImsServiceInfo carrierImsInfo = getImsServiceInfoFromCache(mCarrierServices[i]);
    931                 if (carrierImsInfo == null) {
    932                     // No Carrier override, add all features for this slot
    933                     imsFeaturesBySlot.addAll(info.getSupportedFeatures().stream()
    934                             .filter(feature -> currSlotId == feature.slotId)
    935                             .collect(Collectors.toList()));
    936                 } else {
    937                     // Add all features to the device service that are not currently covered by
    938                     // the carrier ImsService.
    939                     HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatures =
    940                             new HashSet<>(info.getSupportedFeatures());
    941                     deviceFeatures.removeAll(carrierImsInfo.getSupportedFeatures());
    942                     // only add features for current slot
    943                     imsFeaturesBySlot.addAll(deviceFeatures.stream()
    944                             .filter(feature -> currSlotId == feature.slotId).collect(
    945                             Collectors.toList()));
    946                 }
    947             }
    948         }
    949         return imsFeaturesBySlot;
    950     }
    951 
    952     /**
    953      * Implementation of
    954      * {@link ImsServiceController.ImsServiceControllerCallbacks#imsServiceFeatureCreated}, which
    955      * removes the ImsServiceController from the mBoundImsServicesByFeature structure.
    956      */
    957     public void imsServiceFeatureCreated(int slotId, int feature, ImsServiceController controller) {
    958         putImsController(slotId, feature, controller);
    959     }
    960 
    961     /**
    962      * Implementation of
    963      * {@link ImsServiceController.ImsServiceControllerCallbacks#imsServiceFeatureRemoved}, which
    964      * removes the ImsServiceController from the mBoundImsServicesByFeature structure.
    965      */
    966     public void imsServiceFeatureRemoved(int slotId, int feature, ImsServiceController controller) {
    967         removeImsController(slotId, feature);
    968     }
    969 
    970     /**
    971      * Implementation of
    972      * {@link ImsServiceController.ImsServiceControllerCallbacks#imsServiceFeaturesChanged, which
    973      * notify the ImsResolver of a change to the supported ImsFeatures of a connected ImsService.
    974      */
    975     public void imsServiceFeaturesChanged(ImsFeatureConfiguration config,
    976             ImsServiceController controller) {
    977         if (controller == null || config == null) {
    978             return;
    979         }
    980         Log.i(TAG, "imsServiceFeaturesChanged: config=" + config.getServiceFeatures()
    981                 + ", ComponentName=" + controller.getComponentName());
    982         handleFeaturesChanged(controller.getComponentName(), config.getServiceFeatures());
    983     }
    984 
    985     /**
    986      * Determines if the features specified should cause a bind or keep a binding active to an
    987      * ImsService.
    988      * @return true if MMTEL or RCS features are present, false if they are not or only
    989      * EMERGENCY_MMTEL is specified.
    990      */
    991     private boolean shouldFeaturesCauseBind(
    992             HashSet<ImsFeatureConfiguration.FeatureSlotPair> features) {
    993         long bindableFeatures = features.stream()
    994                 // remove all emergency features
    995                 .filter(f -> f.featureType != ImsFeature.FEATURE_EMERGENCY_MMTEL).count();
    996         return bindableFeatures > 0;
    997     }
    998 
    999     // Possibly rebind to another ImsService if currently installed ImsServices were changed or if
   1000     // the SIM card has changed.
   1001     // Called from the handler ONLY
   1002     private void maybeRebindService(int slotId, String newPackageName) {
   1003         if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
   1004             // not specified, replace package on all slots.
   1005             for (int i = 0; i < mNumSlots; i++) {
   1006                 updateBoundCarrierServices(i, newPackageName);
   1007             }
   1008         } else {
   1009             updateBoundCarrierServices(slotId, newPackageName);
   1010         }
   1011 
   1012     }
   1013 
   1014     private void carrierConfigChanged(int slotId) {
   1015         int subId = mSubscriptionManagerProxy.getSubId(slotId);
   1016         PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId);
   1017         if (config != null) {
   1018             String newPackageName = config.getString(
   1019                     CarrierConfigManager.KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, null);
   1020             maybeRebindService(slotId, newPackageName);
   1021         } else {
   1022             Log.w(TAG, "carrierConfigChanged: CarrierConfig is null!");
   1023         }
   1024     }
   1025 
   1026     private void updateBoundCarrierServices(int slotId, String newPackageName) {
   1027         if (slotId > SubscriptionManager.INVALID_SIM_SLOT_INDEX && slotId < mNumSlots) {
   1028             String oldPackageName = mCarrierServices[slotId];
   1029             mCarrierServices[slotId] = newPackageName;
   1030             if (!TextUtils.equals(newPackageName, oldPackageName)) {
   1031                 Log.i(TAG, "Carrier Config updated, binding new ImsService");
   1032                 // Unbind old ImsService, not needed anymore
   1033                 // ImsService is retrieved from the cache. If the cache hasn't been populated yet,
   1034                 // the calls to unbind/bind will fail (intended during initial start up).
   1035                 unbindImsService(getImsServiceInfoFromCache(oldPackageName));
   1036                 ImsServiceInfo newInfo = getImsServiceInfoFromCache(newPackageName);
   1037                 // if there is no carrier ImsService, newInfo is null. This we still want to update
   1038                 // bindings for device ImsService to pick up the missing features.
   1039                 if (newInfo == null || newInfo.featureFromMetadata) {
   1040                     bindImsService(newInfo);
   1041                     // Recalculate the device ImsService features to reflect changes.
   1042                     updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
   1043                 } else {
   1044                     // ImsServiceInfo that has not had features queried yet. Start async
   1045                     // bind and query features.
   1046                     scheduleQueryForFeatures(newInfo);
   1047                 }
   1048             }
   1049         }
   1050     }
   1051 
   1052     /**
   1053      * Schedules a query for dynamic ImsService features.
   1054      */
   1055     private void scheduleQueryForFeatures(ImsServiceInfo service, int delayMs) {
   1056         // if not current device/carrier service, don't perform query. If this changes, this method
   1057         // will be called again.
   1058         if (!isDeviceService(service) && getSlotForActiveCarrierService(service)
   1059                 == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
   1060             Log.i(TAG, "scheduleQueryForFeatures: skipping query for ImsService that is not"
   1061                     + " set as carrier/device ImsService.");
   1062             return;
   1063         }
   1064         Message msg = Message.obtain(mHandler, HANDLER_START_DYNAMIC_FEATURE_QUERY, service);
   1065         if (mHandler.hasMessages(HANDLER_START_DYNAMIC_FEATURE_QUERY, service)) {
   1066             Log.d(TAG, "scheduleQueryForFeatures: dynamic query for " + service.name
   1067                     + " already scheduled");
   1068             return;
   1069         }
   1070         Log.d(TAG, "scheduleQueryForFeatures: starting dynamic query for " + service.name
   1071                 + " in " + delayMs + "ms.");
   1072         mHandler.sendMessageDelayed(msg, delayMs);
   1073     }
   1074 
   1075     private void scheduleQueryForFeatures(ComponentName name, int delayMs) {
   1076         ImsServiceInfo service = getImsServiceInfoFromCache(name.getPackageName());
   1077         if (service == null) {
   1078             Log.w(TAG, "scheduleQueryForFeatures: Couldn't find cached info for name: " + name);
   1079             return;
   1080         }
   1081         scheduleQueryForFeatures(service, delayMs);
   1082     }
   1083 
   1084     private void scheduleQueryForFeatures(ImsServiceInfo service) {
   1085         scheduleQueryForFeatures(service, 0);
   1086     }
   1087 
   1088     /**
   1089      * Schedules the processing of a completed query.
   1090      */
   1091     private void handleFeaturesChanged(ComponentName name,
   1092             Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
   1093         SomeArgs args = SomeArgs.obtain();
   1094         args.arg1 = name;
   1095         args.arg2 = features;
   1096         mHandler.obtainMessage(HANDLER_DYNAMIC_FEATURE_CHANGE, args).sendToTarget();
   1097     }
   1098 
   1099     // Starts a dynamic query. Called from handler ONLY.
   1100     private void startDynamicQuery(ImsServiceInfo service) {
   1101         boolean queryStarted = mFeatureQueryManager.startQuery(service.name,
   1102                 service.controllerFactory.getServiceInterface());
   1103         if (!queryStarted) {
   1104             Log.w(TAG, "startDynamicQuery: service could not connect. Retrying after delay.");
   1105             scheduleQueryForFeatures(service, DELAY_DYNAMIC_QUERY_MS);
   1106         } else {
   1107             Log.d(TAG, "startDynamicQuery: Service queried, waiting for response.");
   1108         }
   1109     }
   1110 
   1111     // process complete dynamic query. Called from handler ONLY.
   1112     private void dynamicQueryComplete(ComponentName name,
   1113             Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
   1114         ImsServiceInfo service = getImsServiceInfoFromCache(name.getPackageName());
   1115         if (service == null) {
   1116             Log.w(TAG, "handleFeaturesChanged: Couldn't find cached info for name: "
   1117                     + name);
   1118             return;
   1119         }
   1120         // Add features to service
   1121         service.replaceFeatures(features);
   1122         if (isActiveCarrierService(service)) {
   1123             // New ImsService is registered to active carrier services and must be newly
   1124             // bound.
   1125             bindImsService(service);
   1126             // Update existing device service features
   1127             updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
   1128         } else if (isDeviceService(service)) {
   1129             // New ImsService is registered as device default and must be newly bound.
   1130             bindImsService(service);
   1131         }
   1132     }
   1133 
   1134     /**
   1135      * @return true if the ImsResolver is in the process of resolving a dynamic query and should not
   1136      * be considered available, false if the ImsResolver is idle.
   1137      */
   1138     public boolean isResolvingBinding() {
   1139         return mHandler.hasMessages(HANDLER_START_DYNAMIC_FEATURE_QUERY)
   1140                 // We haven't processed this message yet, so it is still resolving.
   1141                 || mHandler.hasMessages(HANDLER_DYNAMIC_FEATURE_CHANGE)
   1142                 || mFeatureQueryManager.isQueryInProgress();
   1143     }
   1144 
   1145     private String printFeatures(Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
   1146         StringBuilder featureString = new StringBuilder();
   1147         featureString.append("features: [");
   1148         if (features != null) {
   1149             for (ImsFeatureConfiguration.FeatureSlotPair feature : features) {
   1150                 featureString.append("{");
   1151                 featureString.append(feature.slotId);
   1152                 featureString.append(",");
   1153                 featureString.append(feature.featureType);
   1154                 featureString.append("} ");
   1155             }
   1156             featureString.append("]");
   1157         }
   1158         return featureString.toString();
   1159     }
   1160 
   1161     /**
   1162      * Returns the ImsServiceInfo that matches the provided packageName. Visible for testing
   1163      * the ImsService caching functionality.
   1164      */
   1165     @VisibleForTesting
   1166     public ImsServiceInfo getImsServiceInfoFromCache(String packageName) {
   1167         if (TextUtils.isEmpty(packageName)) {
   1168             return null;
   1169         }
   1170         ImsServiceInfo infoFilter = getInfoByPackageName(mInstalledServicesCache, packageName);
   1171         if (infoFilter != null) {
   1172             return infoFilter;
   1173         } else {
   1174             return null;
   1175         }
   1176     }
   1177 
   1178     // Return the ImsServiceInfo specified for the package name. If the package name is null,
   1179     // get all packages that support ImsServices.
   1180     private List<ImsServiceInfo> getImsServiceInfo(String packageName) {
   1181         List<ImsServiceInfo> infos = new ArrayList<>();
   1182         if (!mIsDynamicBinding) {
   1183             // always return the same ImsService info.
   1184             infos.addAll(getStaticImsService());
   1185         } else {
   1186             // Search for Current ImsService implementations
   1187             infos.addAll(searchForImsServices(packageName, mImsServiceControllerFactory));
   1188             // Search for compat ImsService Implementations
   1189             infos.addAll(searchForImsServices(packageName, mImsServiceControllerFactoryCompat));
   1190         }
   1191         return infos;
   1192     }
   1193 
   1194     private List<ImsServiceInfo> getStaticImsService() {
   1195         List<ImsServiceInfo> infos = new ArrayList<>();
   1196 
   1197         ImsServiceInfo info = new ImsServiceInfo(mNumSlots);
   1198         info.name = mStaticComponent;
   1199         info.controllerFactory = mImsServiceControllerFactoryStaticBindingCompat;
   1200         info.addFeatureForAllSlots(ImsFeature.FEATURE_EMERGENCY_MMTEL);
   1201         info.addFeatureForAllSlots(ImsFeature.FEATURE_MMTEL);
   1202         infos.add(info);
   1203         return infos;
   1204     }
   1205 
   1206     private List<ImsServiceInfo> searchForImsServices(String packageName,
   1207             ImsServiceControllerFactory controllerFactory) {
   1208         List<ImsServiceInfo> infos = new ArrayList<>();
   1209 
   1210         Intent serviceIntent = new Intent(controllerFactory.getServiceInterface());
   1211         serviceIntent.setPackage(packageName);
   1212 
   1213         PackageManager packageManager = mContext.getPackageManager();
   1214         for (ResolveInfo entry : packageManager.queryIntentServicesAsUser(
   1215                 serviceIntent,
   1216                 PackageManager.GET_META_DATA,
   1217                 mContext.getUserId())) {
   1218             ServiceInfo serviceInfo = entry.serviceInfo;
   1219 
   1220             if (serviceInfo != null) {
   1221                 ImsServiceInfo info = new ImsServiceInfo(mNumSlots);
   1222                 info.name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
   1223                 info.controllerFactory = controllerFactory;
   1224 
   1225                 // we will allow the manifest method of declaring manifest features in two cases:
   1226                 // 1) it is the device overlay "default" ImsService, where the features do not
   1227                 // change (the new method can still be used if the default does not define manifest
   1228                 // entries).
   1229                 // 2) using the "compat" ImsService, which only supports manifest query.
   1230                 if (isDeviceService(info)
   1231                         || mImsServiceControllerFactoryCompat == controllerFactory) {
   1232                     if (serviceInfo.metaData != null) {
   1233                         if (serviceInfo.metaData.getBoolean(METADATA_EMERGENCY_MMTEL_FEATURE,
   1234                                 false)) {
   1235                             info.addFeatureForAllSlots(ImsFeature.FEATURE_EMERGENCY_MMTEL);
   1236                         }
   1237                         if (serviceInfo.metaData.getBoolean(METADATA_MMTEL_FEATURE, false)) {
   1238                             info.addFeatureForAllSlots(ImsFeature.FEATURE_MMTEL);
   1239                         }
   1240                         if (serviceInfo.metaData.getBoolean(METADATA_RCS_FEATURE, false)) {
   1241                             info.addFeatureForAllSlots(ImsFeature.FEATURE_RCS);
   1242                         }
   1243                     }
   1244                     // Only dynamic query if we are not a compat version of ImsService and the
   1245                     // default service.
   1246                     if (mImsServiceControllerFactoryCompat != controllerFactory
   1247                             && info.getSupportedFeatures().isEmpty()) {
   1248                         // metadata empty, try dynamic query instead
   1249                         info.featureFromMetadata = false;
   1250                     }
   1251                 } else {
   1252                     // We are a carrier service and not using the compat version of ImsService.
   1253                     info.featureFromMetadata = false;
   1254                 }
   1255                 Log.i(TAG, "service name: " + info.name + ", manifest query: "
   1256                         + info.featureFromMetadata);
   1257                 // Check manifest permission to be sure that the service declares the correct
   1258                 // permissions. Overridden if the METADATA_OVERRIDE_PERM_CHECK metadata is set to
   1259                 // true.
   1260                 // NOTE: METADATA_OVERRIDE_PERM_CHECK should only be set for testing.
   1261                 if (TextUtils.equals(serviceInfo.permission, Manifest.permission.BIND_IMS_SERVICE)
   1262                         || serviceInfo.metaData.getBoolean(METADATA_OVERRIDE_PERM_CHECK, false)) {
   1263                     infos.add(info);
   1264                 } else {
   1265                     Log.w(TAG, "ImsService is not protected with BIND_IMS_SERVICE permission: "
   1266                             + info.name);
   1267                 }
   1268             }
   1269         }
   1270         return infos;
   1271     }
   1272 }
   1273