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