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