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.content.BroadcastReceiver;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.content.pm.PackageManager;
26 import android.content.pm.ResolveInfo;
27 import android.content.pm.ServiceInfo;
28 import android.os.Handler;
29 import android.os.Looper;
30 import android.os.RemoteException;
31 import android.os.UserHandle;
32 import android.telephony.CarrierConfigManager;
33 import android.telephony.SubscriptionManager;
34 import android.telephony.ims.feature.ImsFeature;
35 import android.text.TextUtils;
36 import android.util.ArraySet;
37 import android.util.Log;
38 import android.util.Pair;
39 import android.util.SparseArray;
40 
41 import com.android.ims.internal.IImsServiceController;
42 import com.android.ims.internal.IImsServiceFeatureListener;
43 import com.android.internal.annotations.VisibleForTesting;
44 import com.android.internal.telephony.PhoneConstants;
45 
46 import java.util.ArrayList;
47 import java.util.HashSet;
48 import java.util.List;
49 import java.util.Objects;
50 import java.util.Optional;
51 import java.util.Set;
52 import java.util.stream.Collectors;
53 import java.util.stream.Stream;
54 
55 /**
56  * Creates a list of ImsServices that are available to bind to based on the Device configuration
57  * overlay value "config_ims_package" and Carrier Configuration value
58  * "config_ims_package_override_string".
59  * These ImsServices are then bound to in the following order:
60  *
61  * 1. Carrier Config defined override value per SIM.
62  * 2. Device overlay default value (including no SIM case).
63  *
64  * ImsManager can then retrieve the binding to the correct ImsService using
65  * {@link #getImsServiceControllerAndListen} on a per-slot and per feature basis.
66  */
67 
68 public class ImsResolver implements ImsServiceController.ImsServiceControllerCallbacks {
69 
70     private static final String TAG = "ImsResolver";
71 
72     public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService";
73     public static final String METADATA_EMERGENCY_MMTEL_FEATURE =
74             "android.telephony.ims.EMERGENCY_MMTEL_FEATURE";
75     public static final String METADATA_MMTEL_FEATURE = "android.telephony.ims.MMTEL_FEATURE";
76     public static final String METADATA_RCS_FEATURE = "android.telephony.ims.RCS_FEATURE";
77 
78     // Based on updates from PackageManager
79     private static final int HANDLER_ADD_PACKAGE = 0;
80     // Based on updates from PackageManager
81     private static final int HANDLER_REMOVE_PACKAGE = 1;
82     // Based on updates from CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED
83     private static final int HANDLER_CONFIG_CHANGED = 2;
84 
85     /**
86      * Stores information about an ImsService, including the package name, class name, and features
87      * that the service supports.
88      */
89     @VisibleForTesting
90     public static class ImsServiceInfo {
91         public ComponentName name;
92         public Set<Integer> supportedFeatures;
93 
94         @Override
equals(Object o)95         public boolean equals(Object o) {
96             if (this == o) return true;
97             if (o == null || getClass() != o.getClass()) return false;
98 
99             ImsServiceInfo that = (ImsServiceInfo) o;
100 
101             if (name != null ? !name.equals(that.name) : that.name != null) return false;
102             return supportedFeatures != null ? supportedFeatures.equals(that.supportedFeatures)
103                     : that.supportedFeatures == null;
104 
105         }
106 
107         @Override
hashCode()108         public int hashCode() {
109             int result = name != null ? name.hashCode() : 0;
110             result = 31 * result + (supportedFeatures != null ? supportedFeatures.hashCode() : 0);
111             return result;
112         }
113     }
114 
115     // Receives broadcasts from the system involving changes to the installed applications. If
116     // an ImsService that we are configured to use is installed, we must bind to it.
117     private BroadcastReceiver mAppChangedReceiver = new BroadcastReceiver() {
118         @Override
119         public void onReceive(Context context, Intent intent) {
120             final String action = intent.getAction();
121             final String packageName = intent.getData().getSchemeSpecificPart();
122             switch (action) {
123                 case Intent.ACTION_PACKAGE_ADDED:
124                     // intentional fall-through
125                 case Intent.ACTION_PACKAGE_CHANGED:
126                     mHandler.obtainMessage(HANDLER_ADD_PACKAGE, packageName).sendToTarget();
127                     break;
128                 case Intent.ACTION_PACKAGE_REMOVED:
129                     mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE, packageName).sendToTarget();
130                     break;
131                 default:
132                     return;
133             }
134         }
135     };
136 
137     // Receives the broadcast that a new Carrier Config has been loaded in order to possibly
138     // unbind from one service and bind to another.
139     private BroadcastReceiver mConfigChangedReceiver = new BroadcastReceiver() {
140         @Override
141         public void onReceive(Context context, Intent intent) {
142 
143             int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
144                     SubscriptionManager.INVALID_SUBSCRIPTION_ID);
145 
146             if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
147                 Log.i(TAG, "Received SIM change for invalid sub id.");
148                 return;
149             }
150 
151             Log.i(TAG, "Received Carrier Config Changed for SubId: " + subId);
152 
153             mHandler.obtainMessage(HANDLER_CONFIG_CHANGED, subId).sendToTarget();
154         }
155     };
156 
157     /**
158      * Testing interface used to mock SubscriptionManager in testing
159      */
160     @VisibleForTesting
161     public interface SubscriptionManagerProxy {
162         /**
163          * Mock-able interface for {@link SubscriptionManager#getSubId(int)} used for testing.
164          */
getSubId(int slotId)165         int getSubId(int slotId);
166         /**
167          * Mock-able interface for {@link SubscriptionManager#getSlotIndex(int)} used for testing.
168          */
getSlotIndex(int subId)169         int getSlotIndex(int subId);
170     }
171 
172     private SubscriptionManagerProxy mSubscriptionManagerProxy = new SubscriptionManagerProxy() {
173         @Override
174         public int getSubId(int slotId) {
175             int[] subIds = SubscriptionManager.getSubId(slotId);
176             if (subIds != null) {
177                 // This is done in all other places getSubId is used.
178                 return subIds[0];
179             }
180             return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
181         }
182 
183         @Override
184         public int getSlotIndex(int subId) {
185             return SubscriptionManager.getSlotIndex(subId);
186         }
187     };
188 
189     /**
190      * Testing interface for injecting mock ImsServiceControllers.
191      */
192     @VisibleForTesting
193     public interface ImsServiceControllerFactory {
194         /**
195          * Returns the ImsServiceController created usiing the context and componentName supplied.
196          * Used for DI when testing.
197          */
get(Context context, ComponentName componentName)198         ImsServiceController get(Context context, ComponentName componentName);
199     }
200 
201     private ImsServiceControllerFactory mImsServiceControllerFactory = (context, componentName) ->
202             new ImsServiceController(context, componentName, this);
203 
204     private final CarrierConfigManager mCarrierConfigManager;
205     private final Context mContext;
206     // Locks mBoundImsServicesByFeature only. Be careful to avoid deadlocks from
207     // ImsServiceController callbacks.
208     private final Object mBoundServicesLock = new Object();
209     private final int mNumSlots;
210 
211     // Synchronize all messages on a handler to ensure that the cache includes the most recent
212     // version of the installed ImsServices.
213     private Handler mHandler = new Handler(Looper.getMainLooper(), (msg) -> {
214         switch (msg.what) {
215             case HANDLER_ADD_PACKAGE: {
216                 String packageName = (String) msg.obj;
217                 maybeAddedImsService(packageName);
218                 break;
219             }
220             case HANDLER_REMOVE_PACKAGE: {
221                 String packageName = (String) msg.obj;
222                 maybeRemovedImsService(packageName);
223                 break;
224             }
225             case HANDLER_CONFIG_CHANGED: {
226                 int subId = (Integer) msg.obj;
227                 maybeRebindService(subId);
228                 break;
229             }
230             default:
231                 return false;
232         }
233         return true;
234     });
235 
236     // Package name of the default device service.
237     private String mDeviceService;
238     // Array index corresponds to slot Id associated with the service package name.
239     private String[] mCarrierServices;
240     // List index corresponds to Slot Id, Maps ImsFeature.FEATURE->bound ImsServiceController
241     // Locked on mBoundServicesLock
242     private List<SparseArray<ImsServiceController>> mBoundImsServicesByFeature;
243     // not locked, only accessed on a handler thread.
244     private Set<ImsServiceInfo> mInstalledServicesCache = new ArraySet<>();
245     // not locked, only accessed on a handler thread.
246     private Set<ImsServiceController> mActiveControllers = new ArraySet<>();
247 
ImsResolver(Context context, String defaultImsPackageName, int numSlots)248     public ImsResolver(Context context, String defaultImsPackageName, int numSlots) {
249         mContext = context;
250         mDeviceService = defaultImsPackageName;
251         mNumSlots = numSlots;
252         mCarrierConfigManager = (CarrierConfigManager) mContext.getSystemService(
253                 Context.CARRIER_CONFIG_SERVICE);
254         mCarrierServices = new String[numSlots];
255         mBoundImsServicesByFeature = Stream.generate(SparseArray<ImsServiceController>::new)
256                 .limit(mNumSlots).collect(Collectors.toList());
257 
258         IntentFilter appChangedFilter = new IntentFilter();
259         appChangedFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
260         appChangedFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
261         appChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
262         appChangedFilter.addDataScheme("package");
263         context.registerReceiverAsUser(mAppChangedReceiver, UserHandle.ALL, appChangedFilter, null,
264                 null);
265 
266         context.registerReceiver(mConfigChangedReceiver, new IntentFilter(
267                 CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
268     }
269 
270     @VisibleForTesting
setSubscriptionManagerProxy(SubscriptionManagerProxy proxy)271     public void setSubscriptionManagerProxy(SubscriptionManagerProxy proxy) {
272         mSubscriptionManagerProxy = proxy;
273     }
274 
275     @VisibleForTesting
setImsServiceControllerFactory(ImsServiceControllerFactory factory)276     public void setImsServiceControllerFactory(ImsServiceControllerFactory factory) {
277         mImsServiceControllerFactory = factory;
278     }
279 
280     @VisibleForTesting
getHandler()281     public Handler getHandler() {
282         return mHandler;
283     }
284 
285     /**
286      * Needs to be called after the constructor to first populate the cache and possibly bind to
287      * ImsServices.
288      */
populateCacheAndStartBind()289     public void populateCacheAndStartBind() {
290         Log.i(TAG, "Initializing cache and binding.");
291         // Populates the CarrierConfig override package names for each slot
292         mHandler.obtainMessage(HANDLER_CONFIG_CHANGED, -1).sendToTarget();
293         // Starts first bind to the system.
294         mHandler.obtainMessage(HANDLER_ADD_PACKAGE, null).sendToTarget();
295     }
296 
297     /**
298      * Returns the {@link IImsServiceController} that corresponds to the given slot Id and IMS
299      * feature or {@link null} if the service is not available. If an ImsServiceController is
300      * available, the {@link IImsServiceFeatureListener} callback is registered as a listener for
301      * feature updates.
302      * @param slotId The SIM slot that we are requesting the {@link IImsServiceController} for.
303      * @param feature The IMS Feature we are requesting.
304      * @param callback Listener that will send updates to ImsManager when there are updates to
305      * ImsServiceController.
306      * @return {@link IImsServiceController} interface for the feature specified or {@link null} if
307      * it is unavailable.
308      */
getImsServiceControllerAndListen(int slotId, int feature, IImsServiceFeatureListener callback)309     public IImsServiceController getImsServiceControllerAndListen(int slotId, int feature,
310             IImsServiceFeatureListener callback) {
311         if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.INVALID
312                 || feature >= ImsFeature.MAX) {
313             return null;
314         }
315         ImsServiceController controller;
316         synchronized (mBoundServicesLock) {
317             SparseArray<ImsServiceController> services = mBoundImsServicesByFeature.get(slotId);
318             if (services == null) {
319                 return null;
320             }
321             controller = services.get(feature);
322         }
323         if (controller != null) {
324             controller.addImsServiceFeatureListener(callback);
325             return controller.getImsServiceController();
326         }
327         return null;
328     }
329 
putImsController(int slotId, int feature, ImsServiceController controller)330     private void putImsController(int slotId, int feature, ImsServiceController controller) {
331         if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.INVALID
332                 || feature >= ImsFeature.MAX) {
333             Log.w(TAG, "putImsController received invalid parameters - slot: " + slotId
334                     + ", feature: " + feature);
335             return;
336         }
337         synchronized (mBoundServicesLock) {
338             SparseArray<ImsServiceController> services = mBoundImsServicesByFeature.get(slotId);
339             if (services == null) {
340                 services = new SparseArray<>();
341                 mBoundImsServicesByFeature.add(slotId, services);
342             }
343             Log.i(TAG, "ImsServiceController added on slot: " + slotId + " with feature: "
344                     + feature + " using package: " + controller.getComponentName());
345             services.put(feature, controller);
346         }
347     }
348 
removeImsController(int slotId, int feature)349     private ImsServiceController removeImsController(int slotId, int feature) {
350         if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.INVALID
351                 || feature >= ImsFeature.MAX) {
352             Log.w(TAG, "removeImsController received invalid parameters - slot: " + slotId
353                     + ", feature: " + feature);
354             return null;
355         }
356         synchronized (mBoundServicesLock) {
357             SparseArray<ImsServiceController> services = mBoundImsServicesByFeature.get(slotId);
358             if (services == null) {
359                 return null;
360             }
361             ImsServiceController c = services.get(feature, null);
362             if (c != null) {
363                 Log.i(TAG, "ImsServiceController removed on slot: " + slotId + " with feature: "
364                         + feature + " using package: " + c.getComponentName());
365                 services.remove(feature);
366             }
367             return c;
368         }
369     }
370 
371 
372     // Update the current cache with the new ImsService(s) if it has been added or update the
373     // supported IMS features if they have changed.
374     // Called from the handler ONLY
maybeAddedImsService(String packageName)375     private void maybeAddedImsService(String packageName) {
376         Log.d(TAG, "maybeAddedImsService, packageName: " + packageName);
377         List<ImsServiceInfo> infos = getImsServiceInfo(packageName);
378         List<ImsServiceInfo> newlyAddedInfos = new ArrayList<>();
379         for (ImsServiceInfo info : infos) {
380             // Checking to see if the ComponentName is the same, so we can update the supported
381             // features. Will only be one (if it exists), since it is a set.
382             Optional<ImsServiceInfo> match = getInfoByComponentName(mInstalledServicesCache,
383                     info.name);
384             if (match.isPresent()) {
385                 // update features in the cache
386                 Log.i(TAG, "Updating features in cached ImsService: " + info.name);
387                 Log.d(TAG, "Updating features - Old features: " + match.get().supportedFeatures
388                         + " new features: " + info.supportedFeatures);
389                 match.get().supportedFeatures = info.supportedFeatures;
390                 updateImsServiceFeatures(info);
391             } else {
392                 Log.i(TAG, "Adding newly added ImsService to cache: " + info.name);
393                 mInstalledServicesCache.add(info);
394                 newlyAddedInfos.add(info);
395             }
396         }
397         // Loop through the newly created ServiceInfos in a separate loop to make sure the cache
398         // is fully updated.
399         for (ImsServiceInfo info : newlyAddedInfos) {
400             if (isActiveCarrierService(info)) {
401                 // New ImsService is registered to active carrier services and must be newly
402                 // bound.
403                 bindNewImsService(info);
404                 // Update existing device service features
405                 updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
406             } else if (isDeviceService(info)) {
407                 // New ImsService is registered as device default and must be newly bound.
408                 bindNewImsService(info);
409             }
410         }
411     }
412 
413     // Remove the ImsService from the cache. At this point, the ImsService will have already been
414     // killed.
415     // Called from the handler ONLY
maybeRemovedImsService(String packageName)416     private boolean maybeRemovedImsService(String packageName) {
417         Optional<ImsServiceInfo> match = getInfoByPackageName(mInstalledServicesCache, packageName);
418         if (match.isPresent()) {
419             mInstalledServicesCache.remove(match.get());
420             Log.i(TAG, "Removing ImsService: " + match.get().name);
421             unbindImsService(match.get());
422             updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
423             return true;
424         }
425         return false;
426     }
427 
428     // Returns true if the CarrierConfig that has been loaded includes this ImsServiceInfo
429     // package name.
430     // Called from Handler ONLY
isActiveCarrierService(ImsServiceInfo info)431     private boolean isActiveCarrierService(ImsServiceInfo info) {
432         for (int i = 0; i < mNumSlots; i++) {
433             if (TextUtils.equals(mCarrierServices[i], info.name.getPackageName())) {
434                 return true;
435             }
436         }
437         return false;
438     }
439 
isDeviceService(ImsServiceInfo info)440     private boolean isDeviceService(ImsServiceInfo info) {
441         return TextUtils.equals(mDeviceService, info.name.getPackageName());
442     }
443 
getSlotForActiveCarrierService(ImsServiceInfo info)444     private int getSlotForActiveCarrierService(ImsServiceInfo info) {
445         for (int i = 0; i < mNumSlots; i++) {
446             if (TextUtils.equals(mCarrierServices[i], info.name.getPackageName())) {
447                 return i;
448             }
449         }
450         return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
451     }
452 
getControllerByServiceInfo( Set<ImsServiceController> searchSet, ImsServiceInfo matchValue)453     private Optional<ImsServiceController> getControllerByServiceInfo(
454             Set<ImsServiceController> searchSet, ImsServiceInfo matchValue) {
455         return searchSet.stream()
456                 .filter(c -> Objects.equals(c.getComponentName(), matchValue.name)).findFirst();
457     }
458 
getInfoByPackageName(Set<ImsServiceInfo> searchSet, String matchValue)459     private Optional<ImsServiceInfo> getInfoByPackageName(Set<ImsServiceInfo> searchSet,
460             String matchValue) {
461         return searchSet.stream()
462                 .filter((i) -> Objects.equals(i.name.getPackageName(), matchValue)).findFirst();
463     }
464 
getInfoByComponentName(Set<ImsServiceInfo> searchSet, ComponentName matchValue)465     private Optional<ImsServiceInfo> getInfoByComponentName(Set<ImsServiceInfo> searchSet,
466             ComponentName matchValue) {
467         return searchSet.stream()
468                 .filter((i) -> Objects.equals(i.name, matchValue)).findFirst();
469     }
470 
471     // Creates new features in active ImsServices and removes obsolete cached features. If
472     // cachedInfo == null, then newInfo is assumed to be a new ImsService and will have all features
473     // created.
updateImsServiceFeatures(ImsServiceInfo newInfo)474     private void updateImsServiceFeatures(ImsServiceInfo newInfo) {
475         if (newInfo == null) {
476             return;
477         }
478         Optional<ImsServiceController> o = getControllerByServiceInfo(mActiveControllers,
479                 newInfo);
480         if (o.isPresent()) {
481             Log.i(TAG, "Updating features for ImsService: " + o.get().getComponentName());
482             HashSet<Pair<Integer, Integer>> features = calculateFeaturesToCreate(newInfo);
483             try {
484                 if (features.size() > 0) {
485                     Log.d(TAG, "Updating Features - New Features: " + features);
486                     o.get().changeImsServiceFeatures(features);
487 
488                     // If the carrier service features have changed, the device features will also
489                     // need to be recalculated.
490                     if (isActiveCarrierService(newInfo)
491                             // Prevent infinite recursion from bad behavior
492                             && !TextUtils.equals(newInfo.name.getPackageName(), mDeviceService)) {
493                         Log.i(TAG, "Updating device default");
494                         updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
495                     }
496                 } else {
497                     Log.i(TAG, "Unbinding: features = 0 for ImsService: "
498                             + o.get().getComponentName());
499                     o.get().unbind();
500                 }
501             } catch (RemoteException e) {
502                 Log.e(TAG, "updateImsServiceFeatures: Remote Exception: " + e.getMessage());
503             }
504         }
505     }
506 
507     // Bind to a new ImsService and wait for the service to be connected to create ImsFeatures.
bindNewImsService(ImsServiceInfo info)508     private void bindNewImsService(ImsServiceInfo info) {
509         if (info == null) {
510             return;
511         }
512         ImsServiceController controller = mImsServiceControllerFactory.get(mContext, info.name);
513         HashSet<Pair<Integer, Integer>> features = calculateFeaturesToCreate(info);
514         // Only bind if there are features that will be created by the service.
515         if (features.size() > 0) {
516             Log.i(TAG, "Binding ImsService: " + controller.getComponentName() + " with features: "
517                     + features);
518             controller.bind(features);
519             mActiveControllers.add(controller);
520         }
521     }
522 
523     // Clean up and unbind from an ImsService
unbindImsService(ImsServiceInfo info)524     private void unbindImsService(ImsServiceInfo info) {
525         if (info == null) {
526             return;
527         }
528         Optional<ImsServiceController> o = getControllerByServiceInfo(mActiveControllers, info);
529         if (o.isPresent()) {
530             // Calls imsServiceFeatureRemoved on all features in the controller
531             try {
532                 Log.i(TAG, "Unbinding ImsService: " + o.get().getComponentName());
533                 o.get().unbind();
534             } catch (RemoteException e) {
535                 Log.e(TAG, "unbindImsService: Remote Exception: " + e.getMessage());
536             }
537             mActiveControllers.remove(o.get());
538         }
539     }
540 
541     // Calculate which features an ImsServiceController will need. If it is the carrier specific
542     // ImsServiceController, it will be granted all of the features it requests on the associated
543     // slot. If it is the device ImsService, it will get all of the features not covered by the
544     // carrier implementation.
calculateFeaturesToCreate(ImsServiceInfo info)545     private HashSet<Pair<Integer, Integer>> calculateFeaturesToCreate(ImsServiceInfo info) {
546         HashSet<Pair<Integer, Integer>> imsFeaturesBySlot = new HashSet<>();
547         // Check if the info is a carrier service
548         int slotId = getSlotForActiveCarrierService(info);
549         if (slotId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
550             imsFeaturesBySlot.addAll(info.supportedFeatures.stream().map(
551                     feature -> new Pair<>(slotId, feature)).collect(Collectors.toList()));
552         } else if (isDeviceService(info)) {
553             // For all slots that are not currently using a carrier ImsService, enable all features
554             // for the device default.
555             for (int i = 0; i < mNumSlots; i++) {
556                 final int currSlotId = i;
557                 ImsServiceInfo carrierImsInfo = getImsServiceInfoFromCache(mCarrierServices[i]);
558                 if (carrierImsInfo == null) {
559                     // No Carrier override, add all features
560                     imsFeaturesBySlot.addAll(info.supportedFeatures.stream().map(
561                             feature -> new Pair<>(currSlotId, feature)).collect(
562                             Collectors.toList()));
563                 } else {
564                     // Add all features to the device service that are not currently covered by
565                     // the carrier ImsService.
566                     Set<Integer> deviceFeatures = new HashSet<>(info.supportedFeatures);
567                     deviceFeatures.removeAll(carrierImsInfo.supportedFeatures);
568                     imsFeaturesBySlot.addAll(deviceFeatures.stream().map(
569                             feature -> new Pair<>(currSlotId, feature)).collect(
570                             Collectors.toList()));
571                 }
572             }
573         }
574         return imsFeaturesBySlot;
575     }
576 
577     /**
578      * Implementation of
579      * {@link ImsServiceController.ImsServiceControllerCallbacks#imsServiceFeatureCreated}, which
580      * removes the ImsServiceController from the mBoundImsServicesByFeature structure.
581      */
imsServiceFeatureCreated(int slotId, int feature, ImsServiceController controller)582     public void imsServiceFeatureCreated(int slotId, int feature, ImsServiceController controller) {
583         putImsController(slotId, feature, controller);
584     }
585 
586     /**
587      * Implementation of
588      * {@link ImsServiceController.ImsServiceControllerCallbacks#imsServiceFeatureRemoved}, which
589      * removes the ImsServiceController from the mBoundImsServicesByFeature structure.
590      */
imsServiceFeatureRemoved(int slotId, int feature, ImsServiceController controller)591     public void imsServiceFeatureRemoved(int slotId, int feature, ImsServiceController controller) {
592         removeImsController(slotId, feature);
593     }
594 
595     // Possibly rebind to another ImsService if currently installed ImsServices were changed or if
596     // the SIM card has changed.
597     // Called from the handler ONLY
maybeRebindService(int subId)598     private void maybeRebindService(int subId) {
599         if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
600             // not specified, replace package on all slots.
601             for (int i = 0; i < mNumSlots; i++) {
602                 // get Sub id from Slot Id
603                 subId = mSubscriptionManagerProxy.getSubId(i);
604                 updateBoundCarrierServices(subId);
605             }
606         } else {
607             updateBoundCarrierServices(subId);
608         }
609 
610     }
611 
updateBoundCarrierServices(int subId)612     private void updateBoundCarrierServices(int subId) {
613         int slotId = mSubscriptionManagerProxy.getSlotIndex(subId);
614         String newPackageName = mCarrierConfigManager.getConfigForSubId(subId).getString(
615                 CarrierConfigManager.KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, null);
616         if (slotId != SubscriptionManager.INVALID_SIM_SLOT_INDEX && slotId < mNumSlots) {
617             String oldPackageName = mCarrierServices[slotId];
618             mCarrierServices[slotId] = newPackageName;
619             if (!TextUtils.equals(newPackageName, oldPackageName)) {
620                 Log.i(TAG, "Carrier Config updated, binding new ImsService");
621                 // Unbind old ImsService, not needed anymore
622                 // ImsService is retrieved from the cache. If the cache hasn't been populated yet,
623                 // the calls to unbind/bind will fail (intended during initial start up).
624                 unbindImsService(getImsServiceInfoFromCache(oldPackageName));
625                 bindNewImsService(getImsServiceInfoFromCache(newPackageName));
626                 // Recalculate the device ImsService features to reflect changes.
627                 updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
628             }
629         }
630     }
631 
632     /**
633      * Returns the ImsServiceInfo that matches the provided packageName. Visible for testing
634      * the ImsService caching functionality.
635      */
636     @VisibleForTesting
getImsServiceInfoFromCache(String packageName)637     public ImsServiceInfo getImsServiceInfoFromCache(String packageName) {
638         if (TextUtils.isEmpty(packageName)) {
639             return null;
640         }
641         Optional<ImsServiceInfo> infoFilter = getInfoByPackageName(mInstalledServicesCache,
642                 packageName);
643         if (infoFilter.isPresent()) {
644             return infoFilter.get();
645         } else {
646             return null;
647         }
648     }
649 
650     // Return the ImsServiceInfo specified for the package name. If the package name is null,
651     // get all packages that support ImsServices.
getImsServiceInfo(String packageName)652     private List<ImsServiceInfo> getImsServiceInfo(String packageName) {
653         List<ImsServiceInfo> infos = new ArrayList<>();
654 
655         Intent serviceIntent = new Intent(SERVICE_INTERFACE);
656         serviceIntent.setPackage(packageName);
657 
658         PackageManager packageManager = mContext.getPackageManager();
659         for (ResolveInfo entry : packageManager.queryIntentServicesAsUser(
660                 serviceIntent,
661                 PackageManager.GET_META_DATA,
662                 mContext.getUserId())) {
663             ServiceInfo serviceInfo = entry.serviceInfo;
664 
665             if (serviceInfo != null) {
666                 ImsServiceInfo info = new ImsServiceInfo();
667                 info.name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
668                 info.supportedFeatures = new HashSet<>(ImsFeature.MAX);
669                 // Add all supported features
670                 if (serviceInfo.metaData != null) {
671                     if (serviceInfo.metaData.getBoolean(METADATA_EMERGENCY_MMTEL_FEATURE, false)) {
672                         info.supportedFeatures.add(ImsFeature.EMERGENCY_MMTEL);
673                     }
674                     if (serviceInfo.metaData.getBoolean(METADATA_MMTEL_FEATURE, false)) {
675                         info.supportedFeatures.add(ImsFeature.MMTEL);
676                     }
677                     if (serviceInfo.metaData.getBoolean(METADATA_RCS_FEATURE, false)) {
678                         info.supportedFeatures.add(ImsFeature.RCS);
679                     }
680                 }
681                 // Check manifest permission to be sure that the service declares the correct
682                 // permissions.
683                 if (TextUtils.equals(serviceInfo.permission,
684                         Manifest.permission.BIND_IMS_SERVICE)) {
685                     Log.d(TAG, "ImsService added to cache: " + info.name + " with features: "
686                             + info.supportedFeatures);
687                     infos.add(info);
688                 } else {
689                     Log.w(TAG, "ImsService does not have BIND_IMS_SERVICE permission: "
690                             + info.name);
691                 }
692             }
693         }
694         return infos;
695     }
696 }
697