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
17package com.android.internal.telephony.ims;
18
19import android.Manifest;
20import android.content.BroadcastReceiver;
21import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.content.pm.PackageManager;
26import android.content.pm.ResolveInfo;
27import android.content.pm.ServiceInfo;
28import android.os.Handler;
29import android.os.Looper;
30import android.os.RemoteException;
31import android.os.UserHandle;
32import android.telephony.CarrierConfigManager;
33import android.telephony.SubscriptionManager;
34import android.telephony.ims.feature.ImsFeature;
35import android.text.TextUtils;
36import android.util.ArraySet;
37import android.util.Log;
38import android.util.Pair;
39import android.util.SparseArray;
40
41import com.android.ims.internal.IImsServiceController;
42import com.android.ims.internal.IImsServiceFeatureListener;
43import com.android.internal.annotations.VisibleForTesting;
44import com.android.internal.telephony.PhoneConstants;
45
46import java.util.ArrayList;
47import java.util.HashSet;
48import java.util.List;
49import java.util.Objects;
50import java.util.Optional;
51import java.util.Set;
52import java.util.stream.Collectors;
53import 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
68public 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
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
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         */
165        int getSubId(int slotId);
166        /**
167         * Mock-able interface for {@link SubscriptionManager#getSlotIndex(int)} used for testing.
168         */
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         */
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
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
271    public void setSubscriptionManagerProxy(SubscriptionManagerProxy proxy) {
272        mSubscriptionManagerProxy = proxy;
273    }
274
275    @VisibleForTesting
276    public void setImsServiceControllerFactory(ImsServiceControllerFactory factory) {
277        mImsServiceControllerFactory = factory;
278    }
279
280    @VisibleForTesting
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     */
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     */
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
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
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
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
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
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
440    private boolean isDeviceService(ImsServiceInfo info) {
441        return TextUtils.equals(mDeviceService, info.name.getPackageName());
442    }
443
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
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
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
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.
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.
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
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.
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     */
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     */
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
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
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
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.
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