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 (packageName == null || packageName.isEmpty()) { 386 unbindImsService(getImsServiceInfoFromCache(mDeviceService)); 387 } 388 mDeviceService = packageName; 389 ImsServiceInfo deviceInfo = getImsServiceInfoFromCache(mDeviceService); 390 if (deviceInfo == null) { 391 // The package name is either "" or does not exist on the device. 392 break; 393 } 394 if (deviceInfo.featureFromMetadata) { 395 bindImsService(deviceInfo); 396 } else { 397 // newly added ImsServiceInfo that has not had features queried yet. Start 398 // async bind and query features. 399 scheduleQueryForFeatures(deviceInfo); 400 } 401 } 402 break; 403 } 404 default: 405 return false; 406 } 407 return true; 408 }); 409 410 // Results from dynamic queries to ImsService regarding the features they support. 411 private ImsServiceFeatureQueryManager.Listener mDynamicQueryListener = 412 new ImsServiceFeatureQueryManager.Listener() { 413 414 @Override 415 public void onComplete(ComponentName name, 416 Set<ImsFeatureConfiguration.FeatureSlotPair> features) { 417 Log.d(TAG, "onComplete called for name: " + name + "features:" 418 + printFeatures(features)); 419 handleFeaturesChanged(name, features); 420 } 421 422 @Override 423 public void onError(ComponentName name) { 424 Log.w(TAG, "onError: " + name + "returned with an error result"); 425 scheduleQueryForFeatures(name, DELAY_DYNAMIC_QUERY_MS); 426 } 427 }; 428 429 // Array index corresponds to slot Id associated with the service package name. 430 private String[] mCarrierServices; 431 // List index corresponds to Slot Id, Maps ImsFeature.FEATURE->bound ImsServiceController 432 // Locked on mBoundServicesLock 433 private List<SparseArray<ImsServiceController>> mBoundImsServicesByFeature; 434 // not locked, only accessed on a handler thread. 435 private Map<ComponentName, ImsServiceInfo> mInstalledServicesCache = new HashMap<>(); 436 // not locked, only accessed on a handler thread. 437 private Map<ComponentName, ImsServiceController> mActiveControllers = new HashMap<>(); 438 // Only used as the Component name for legacy ImsServices that did not use dynamic binding. 439 private final ComponentName mStaticComponent; 440 private ImsServiceFeatureQueryManager mFeatureQueryManager; 441 ImsResolver(Context context, String defaultImsPackageName, int numSlots, boolean isDynamicBinding)442 public ImsResolver(Context context, String defaultImsPackageName, int numSlots, 443 boolean isDynamicBinding) { 444 mContext = context; 445 mDeviceService = defaultImsPackageName; 446 mNumSlots = numSlots; 447 mIsDynamicBinding = isDynamicBinding; 448 mStaticComponent = new ComponentName(mContext, ImsResolver.class); 449 if (!mIsDynamicBinding) { 450 Log.i(TAG, "ImsResolver initialized with static binding."); 451 mDeviceService = mStaticComponent.getPackageName(); 452 } 453 mCarrierConfigManager = (CarrierConfigManager) mContext.getSystemService( 454 Context.CARRIER_CONFIG_SERVICE); 455 mCarrierServices = new String[numSlots]; 456 mBoundImsServicesByFeature = Stream.generate(SparseArray<ImsServiceController>::new) 457 .limit(mNumSlots).collect(Collectors.toList()); 458 459 // Only register for Package/CarrierConfig updates if dynamic binding. 460 if(mIsDynamicBinding) { 461 IntentFilter appChangedFilter = new IntentFilter(); 462 appChangedFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 463 appChangedFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 464 appChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 465 appChangedFilter.addDataScheme("package"); 466 context.registerReceiverAsUser(mAppChangedReceiver, UserHandle.ALL, appChangedFilter, 467 null, 468 null); 469 470 context.registerReceiver(mConfigChangedReceiver, new IntentFilter( 471 CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); 472 } 473 } 474 475 @VisibleForTesting setSubscriptionManagerProxy(SubscriptionManagerProxy proxy)476 public void setSubscriptionManagerProxy(SubscriptionManagerProxy proxy) { 477 mSubscriptionManagerProxy = proxy; 478 } 479 480 @VisibleForTesting setImsServiceControllerFactory(ImsServiceControllerFactory factory)481 public void setImsServiceControllerFactory(ImsServiceControllerFactory factory) { 482 mImsServiceControllerFactory = factory; 483 } 484 485 @VisibleForTesting getHandler()486 public Handler getHandler() { 487 return mHandler; 488 } 489 490 @VisibleForTesting setImsDynamicQueryManagerFactory(ImsDynamicQueryManagerFactory m)491 public void setImsDynamicQueryManagerFactory(ImsDynamicQueryManagerFactory m) { 492 mDynamicQueryManagerFactory = m; 493 } 494 495 /** 496 * Needs to be called after the constructor to first populate the cache and possibly bind to 497 * ImsServices. 498 */ initPopulateCacheAndStartBind()499 public void initPopulateCacheAndStartBind() { 500 Log.i(TAG, "Initializing cache and binding."); 501 mFeatureQueryManager = mDynamicQueryManagerFactory.create(mContext, mDynamicQueryListener); 502 // Populates the CarrierConfig override package names for each slot 503 mHandler.obtainMessage(HANDLER_CONFIG_CHANGED, 504 SubscriptionManager.INVALID_SIM_SLOT_INDEX).sendToTarget(); 505 // Starts first bind to the system. 506 mHandler.obtainMessage(HANDLER_ADD_PACKAGE, null).sendToTarget(); 507 } 508 509 /** 510 * Notify ImsService to enable IMS for the framework. This will trigger IMS registration and 511 * trigger ImsFeature status updates. 512 */ enableIms(int slotId)513 public void enableIms(int slotId) { 514 SparseArray<ImsServiceController> controllers = getImsServiceControllers(slotId); 515 if (controllers != null) { 516 for (int i = 0; i < controllers.size(); i++) { 517 int key = controllers.keyAt(i); 518 controllers.get(key).enableIms(slotId); 519 } 520 } 521 } 522 523 /** 524 * Notify ImsService to disable IMS for the framework. This will trigger IMS de-registration and 525 * trigger ImsFeature capability status to become false. 526 */ disableIms(int slotId)527 public void disableIms(int slotId) { 528 SparseArray<ImsServiceController> controllers = getImsServiceControllers(slotId); 529 if (controllers != null) { 530 for (int i = 0; i < controllers.size(); i++) { 531 int key = controllers.keyAt(i); 532 controllers.get(key).disableIms(slotId); 533 } 534 } 535 } 536 537 /** 538 * Returns the {@link IImsMmTelFeature} that corresponds to the given slot Id or {@link null} if 539 * the service is not available. If an IImsMMTelFeature is available, the 540 * {@link IImsServiceFeatureCallback} callback is registered as a listener for feature updates. 541 * @param slotId The SIM slot that we are requesting the {@link IImsMmTelFeature} for. 542 * @param callback Listener that will send updates to ImsManager when there are updates to 543 * the feature. 544 * @return {@link IImsMmTelFeature} interface or {@link null} if it is unavailable. 545 */ getMmTelFeatureAndListen(int slotId, IImsServiceFeatureCallback callback)546 public IImsMmTelFeature getMmTelFeatureAndListen(int slotId, 547 IImsServiceFeatureCallback callback) { 548 ImsServiceController controller = getImsServiceControllerAndListen(slotId, 549 ImsFeature.FEATURE_MMTEL, callback); 550 return (controller != null) ? controller.getMmTelFeature(slotId) : null; 551 } 552 553 /** 554 * Returns the {@link IImsRcsFeature} that corresponds to the given slot Id for emergency 555 * calling or {@link null} if the service is not available. If an IImsMMTelFeature is 556 * available, the {@link IImsServiceFeatureCallback} callback is registered as a listener for 557 * feature updates. 558 * @param slotId The SIM slot that we are requesting the {@link IImsRcsFeature} for. 559 * @param callback listener that will send updates to ImsManager when there are updates to 560 * the feature. 561 * @return {@link IImsRcsFeature} interface or {@link null} if it is unavailable. 562 */ getRcsFeatureAndListen(int slotId, IImsServiceFeatureCallback callback)563 public IImsRcsFeature getRcsFeatureAndListen(int slotId, IImsServiceFeatureCallback callback) { 564 ImsServiceController controller = getImsServiceControllerAndListen(slotId, 565 ImsFeature.FEATURE_RCS, callback); 566 return (controller != null) ? controller.getRcsFeature(slotId) : null; 567 } 568 569 /** 570 * Returns the ImsRegistration structure associated with the slotId and feature specified. 571 */ getImsRegistration(int slotId, int feature)572 public @Nullable IImsRegistration getImsRegistration(int slotId, int feature) 573 throws RemoteException { 574 ImsServiceController controller = getImsServiceController(slotId, feature); 575 if (controller != null) { 576 return controller.getRegistration(slotId); 577 } 578 return null; 579 } 580 581 /** 582 * Returns the ImsConfig structure associated with the slotId and feature specified. 583 */ getImsConfig(int slotId, int feature)584 public @Nullable IImsConfig getImsConfig(int slotId, int feature) 585 throws RemoteException { 586 ImsServiceController controller = getImsServiceController(slotId, feature); 587 if (controller != null) { 588 return controller.getConfig(slotId); 589 } 590 return null; 591 } 592 593 @VisibleForTesting getImsServiceController(int slotId, int feature)594 public ImsServiceController getImsServiceController(int slotId, int feature) { 595 if (slotId < 0 || slotId >= mNumSlots) { 596 return null; 597 } 598 ImsServiceController controller; 599 synchronized (mBoundServicesLock) { 600 SparseArray<ImsServiceController> services = mBoundImsServicesByFeature.get(slotId); 601 if (services == null) { 602 return null; 603 } 604 controller = services.get(feature); 605 } 606 return controller; 607 } 608 getImsServiceControllers(int slotId)609 private SparseArray<ImsServiceController> getImsServiceControllers(int slotId) { 610 if (slotId < 0 || slotId >= mNumSlots) { 611 return null; 612 } 613 synchronized (mBoundServicesLock) { 614 SparseArray<ImsServiceController> services = mBoundImsServicesByFeature.get(slotId); 615 if (services == null) { 616 return null; 617 } 618 return services; 619 } 620 } 621 622 @VisibleForTesting getImsServiceControllerAndListen(int slotId, int feature, IImsServiceFeatureCallback callback)623 public ImsServiceController getImsServiceControllerAndListen(int slotId, int feature, 624 IImsServiceFeatureCallback callback) { 625 ImsServiceController controller = getImsServiceController(slotId, feature); 626 627 if (controller != null) { 628 controller.addImsServiceFeatureCallback(callback); 629 return controller; 630 } 631 return null; 632 } 633 634 // Used for testing only. overrideImsServiceConfiguration(int slotId, boolean isCarrierService, String packageName)635 public boolean overrideImsServiceConfiguration(int slotId, boolean isCarrierService, 636 String packageName) { 637 if (slotId < 0 || slotId >= mNumSlots) { 638 Log.w(TAG, "overrideImsServiceConfiguration: invalid slotId!"); 639 return false; 640 } 641 642 if (packageName == null) { 643 Log.w(TAG, "overrideImsServiceConfiguration: null packageName!"); 644 return false; 645 } 646 647 // encode boolean to int for Message. 648 int carrierService = isCarrierService ? 1 : 0; 649 Message.obtain(mHandler, HANDLER_OVERRIDE_IMS_SERVICE_CONFIG, slotId, carrierService, 650 packageName).sendToTarget(); 651 return true; 652 } 653 654 // used for testing only. getImsServiceConfiguration(int slotId, boolean isCarrierService)655 public String getImsServiceConfiguration(int slotId, boolean isCarrierService) { 656 if (slotId < 0 || slotId >= mNumSlots) { 657 Log.w(TAG, "getImsServiceConfiguration: invalid slotId!"); 658 return ""; 659 } 660 661 return isCarrierService ? mCarrierServices[slotId] : mDeviceService; 662 } 663 putImsController(int slotId, int feature, ImsServiceController controller)664 private void putImsController(int slotId, int feature, ImsServiceController controller) { 665 if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.FEATURE_INVALID 666 || feature >= ImsFeature.FEATURE_MAX) { 667 Log.w(TAG, "putImsController received invalid parameters - slot: " + slotId 668 + ", feature: " + feature); 669 return; 670 } 671 synchronized (mBoundServicesLock) { 672 SparseArray<ImsServiceController> services = mBoundImsServicesByFeature.get(slotId); 673 if (services == null) { 674 services = new SparseArray<>(); 675 mBoundImsServicesByFeature.add(slotId, services); 676 } 677 Log.i(TAG, "ImsServiceController added on slot: " + slotId + " with feature: " 678 + feature + " using package: " + controller.getComponentName()); 679 services.put(feature, controller); 680 } 681 } 682 removeImsController(int slotId, int feature)683 private ImsServiceController removeImsController(int slotId, int feature) { 684 if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.FEATURE_INVALID 685 || feature >= ImsFeature.FEATURE_MAX) { 686 Log.w(TAG, "removeImsController received invalid parameters - slot: " + slotId 687 + ", feature: " + feature); 688 return null; 689 } 690 synchronized (mBoundServicesLock) { 691 SparseArray<ImsServiceController> services = mBoundImsServicesByFeature.get(slotId); 692 if (services == null) { 693 return null; 694 } 695 ImsServiceController c = services.get(feature, null); 696 if (c != null) { 697 Log.i(TAG, "ImsServiceController removed on slot: " + slotId + " with feature: " 698 + feature + " using package: " + c.getComponentName()); 699 services.remove(feature); 700 } 701 return c; 702 } 703 } 704 705 // Update the current cache with the new ImsService(s) if it has been added or update the 706 // supported IMS features if they have changed. 707 // Called from the handler ONLY maybeAddedImsService(String packageName)708 private void maybeAddedImsService(String packageName) { 709 Log.d(TAG, "maybeAddedImsService, packageName: " + packageName); 710 List<ImsServiceInfo> infos = getImsServiceInfo(packageName); 711 List<ImsServiceInfo> newlyAddedInfos = new ArrayList<>(); 712 for (ImsServiceInfo info : infos) { 713 // Checking to see if the ComponentName is the same, so we can update the supported 714 // features. Will only be one (if it exists), since it is a set. 715 ImsServiceInfo match = getInfoByComponentName(mInstalledServicesCache, info.name); 716 if (match != null) { 717 // for dynamic query the new "info" will have no supported features yet. Don't wipe 718 // out the cache for the existing features or update yet. Instead start a query 719 // for features dynamically. 720 if (info.featureFromMetadata) { 721 // update features in the cache 722 Log.i(TAG, "Updating features in cached ImsService: " + info.name); 723 Log.d(TAG, "Updating features - Old features: " + match + " new features: " 724 + info); 725 match.replaceFeatures(info.getSupportedFeatures()); 726 updateImsServiceFeatures(info); 727 } else { 728 // start a query to get ImsService features 729 scheduleQueryForFeatures(info); 730 } 731 } else { 732 Log.i(TAG, "Adding newly added ImsService to cache: " + info.name); 733 mInstalledServicesCache.put(info.name, info); 734 if (info.featureFromMetadata) { 735 newlyAddedInfos.add(info); 736 } else { 737 // newly added ImsServiceInfo that has not had features queried yet. Start async 738 // bind and query features. 739 scheduleQueryForFeatures(info); 740 } 741 } 742 } 743 // Loop through the newly created ServiceInfos in a separate loop to make sure the cache 744 // is fully updated. 745 for (ImsServiceInfo info : newlyAddedInfos) { 746 if (isActiveCarrierService(info)) { 747 // New ImsService is registered to active carrier services and must be newly 748 // bound. 749 bindImsService(info); 750 // Update existing device service features 751 updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService)); 752 } else if (isDeviceService(info)) { 753 // New ImsService is registered as device default and must be newly bound. 754 bindImsService(info); 755 } 756 } 757 } 758 759 // Remove the ImsService from the cache. At this point, the ImsService will have already been 760 // killed. 761 // Called from the handler ONLY maybeRemovedImsService(String packageName)762 private boolean maybeRemovedImsService(String packageName) { 763 ImsServiceInfo match = getInfoByPackageName(mInstalledServicesCache, packageName); 764 if (match != null) { 765 mInstalledServicesCache.remove(match.name); 766 Log.i(TAG, "Removing ImsService: " + match.name); 767 unbindImsService(match); 768 updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService)); 769 return true; 770 } 771 return false; 772 } 773 774 // Returns true if the CarrierConfig that has been loaded includes this ImsServiceInfo 775 // package name. 776 // Called from Handler ONLY isActiveCarrierService(ImsServiceInfo info)777 private boolean isActiveCarrierService(ImsServiceInfo info) { 778 for (int i = 0; i < mNumSlots; i++) { 779 if (TextUtils.equals(mCarrierServices[i], info.name.getPackageName())) { 780 return true; 781 } 782 } 783 return false; 784 } 785 isDeviceService(ImsServiceInfo info)786 private boolean isDeviceService(ImsServiceInfo info) { 787 return TextUtils.equals(mDeviceService, info.name.getPackageName()); 788 } 789 getSlotForActiveCarrierService(ImsServiceInfo info)790 private int getSlotForActiveCarrierService(ImsServiceInfo info) { 791 for (int i = 0; i < mNumSlots; i++) { 792 if (TextUtils.equals(mCarrierServices[i], info.name.getPackageName())) { 793 return i; 794 } 795 } 796 return SubscriptionManager.INVALID_SIM_SLOT_INDEX; 797 } 798 getControllerByServiceInfo( Map<ComponentName, ImsServiceController> searchMap, ImsServiceInfo matchValue)799 private ImsServiceController getControllerByServiceInfo( 800 Map<ComponentName, ImsServiceController> searchMap, ImsServiceInfo matchValue) { 801 return searchMap.values().stream() 802 .filter(c -> Objects.equals(c.getComponentName(), matchValue.name)) 803 .findFirst().orElse(null); 804 } 805 getInfoByPackageName(Map<ComponentName, ImsServiceInfo> searchMap, String matchValue)806 private ImsServiceInfo getInfoByPackageName(Map<ComponentName, ImsServiceInfo> searchMap, 807 String matchValue) { 808 return searchMap.values().stream() 809 .filter((i) -> Objects.equals(i.name.getPackageName(), matchValue)) 810 .findFirst().orElse(null); 811 } 812 getInfoByComponentName( Map<ComponentName, ImsServiceInfo> searchMap, ComponentName matchValue)813 private ImsServiceInfo getInfoByComponentName( 814 Map<ComponentName, ImsServiceInfo> searchMap, ComponentName matchValue) { 815 return searchMap.get(matchValue); 816 } 817 818 // Creates new features in active ImsServices and removes obsolete cached features. If 819 // cachedInfo == null, then newInfo is assumed to be a new ImsService and will have all features 820 // created. updateImsServiceFeatures(ImsServiceInfo newInfo)821 private void updateImsServiceFeatures(ImsServiceInfo newInfo) { 822 if (newInfo == null) { 823 return; 824 } 825 ImsServiceController controller = getControllerByServiceInfo(mActiveControllers, newInfo); 826 // Will return zero if these features are overridden or it should not currently have any 827 // features because it is not carrier/device. 828 HashSet<ImsFeatureConfiguration.FeatureSlotPair> features = 829 calculateFeaturesToCreate(newInfo); 830 if (shouldFeaturesCauseBind(features)) { 831 try { 832 if (controller != null) { 833 Log.i(TAG, "Updating features for ImsService: " 834 + controller.getComponentName()); 835 Log.d(TAG, "Updating Features - New Features: " + features); 836 controller.changeImsServiceFeatures(features); 837 } else { 838 Log.i(TAG, "updateImsServiceFeatures: unbound with active features, rebinding"); 839 bindImsServiceWithFeatures(newInfo, features); 840 } 841 // If the carrier service features have changed, the device features will also 842 // need to be recalculated. 843 if (isActiveCarrierService(newInfo) 844 // Prevent infinite recursion from bad behavior 845 && !TextUtils.equals(newInfo.name.getPackageName(), mDeviceService)) { 846 Log.i(TAG, "Updating device default"); 847 updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService)); 848 } 849 } catch (RemoteException e) { 850 Log.e(TAG, "updateImsServiceFeatures: Remote Exception: " + e.getMessage()); 851 } 852 // Don't stay bound if the ImsService is providing no features. 853 } else if (controller != null) { 854 Log.i(TAG, "Unbinding: features = 0 for ImsService: " + controller.getComponentName()); 855 unbindImsService(newInfo); 856 } 857 } 858 859 // Bind to an ImsService and wait for the service to be connected to create ImsFeatures. bindImsService(ImsServiceInfo info)860 private void bindImsService(ImsServiceInfo info) { 861 if (info == null) { 862 return; 863 } 864 HashSet<ImsFeatureConfiguration.FeatureSlotPair> features = calculateFeaturesToCreate(info); 865 bindImsServiceWithFeatures(info, features); 866 } 867 bindImsServiceWithFeatures(ImsServiceInfo info, HashSet<ImsFeatureConfiguration.FeatureSlotPair> features)868 private void bindImsServiceWithFeatures(ImsServiceInfo info, 869 HashSet<ImsFeatureConfiguration.FeatureSlotPair> features) { 870 // Only bind if there are features that will be created by the service. 871 if (shouldFeaturesCauseBind(features)) { 872 // Check to see if an active controller already exists 873 ImsServiceController controller = getControllerByServiceInfo(mActiveControllers, info); 874 if (controller != null) { 875 Log.i(TAG, "ImsService connection exists, updating features " + features); 876 try { 877 controller.changeImsServiceFeatures(features); 878 // Features have been set, there was an error adding/removing. When the 879 // controller recovers, it will add/remove again. 880 } catch (RemoteException e) { 881 Log.w(TAG, "bindImsService: error=" + e.getMessage()); 882 } 883 } else { 884 controller = info.controllerFactory.create(mContext, info.name, this); 885 Log.i(TAG, "Binding ImsService: " + controller.getComponentName() 886 + " with features: " + features); 887 controller.bind(features); 888 } 889 mActiveControllers.put(info.name, controller); 890 } 891 } 892 893 // Clean up and unbind from an ImsService unbindImsService(ImsServiceInfo info)894 private void unbindImsService(ImsServiceInfo info) { 895 if (info == null) { 896 return; 897 } 898 ImsServiceController controller = getControllerByServiceInfo(mActiveControllers, info); 899 if (controller != null) { 900 // Calls imsServiceFeatureRemoved on all features in the controller 901 try { 902 Log.i(TAG, "Unbinding ImsService: " + controller.getComponentName()); 903 controller.unbind(); 904 } catch (RemoteException e) { 905 Log.e(TAG, "unbindImsService: Remote Exception: " + e.getMessage()); 906 } 907 mActiveControllers.remove(info.name); 908 } 909 } 910 911 // Calculate which features an ImsServiceController will need. If it is the carrier specific 912 // ImsServiceController, it will be granted all of the features it requests on the associated 913 // slot. If it is the device ImsService, it will get all of the features not covered by the 914 // carrier implementation. calculateFeaturesToCreate( ImsServiceInfo info)915 private HashSet<ImsFeatureConfiguration.FeatureSlotPair> calculateFeaturesToCreate( 916 ImsServiceInfo info) { 917 HashSet<ImsFeatureConfiguration.FeatureSlotPair> imsFeaturesBySlot = new HashSet<>(); 918 // Check if the info is a carrier service 919 int slotId = getSlotForActiveCarrierService(info); 920 if (slotId != SubscriptionManager.INVALID_SIM_SLOT_INDEX) { 921 imsFeaturesBySlot.addAll(info.getSupportedFeatures().stream() 922 // Match slotId with feature slotId. 923 .filter(feature -> slotId == feature.slotId) 924 .collect(Collectors.toList())); 925 } else if (isDeviceService(info)) { 926 // For all slots that are not currently using a carrier ImsService, enable all features 927 // for the device default. 928 for (int i = 0; i < mNumSlots; i++) { 929 final int currSlotId = i; 930 ImsServiceInfo carrierImsInfo = getImsServiceInfoFromCache(mCarrierServices[i]); 931 if (carrierImsInfo == null) { 932 // No Carrier override, add all features for this slot 933 imsFeaturesBySlot.addAll(info.getSupportedFeatures().stream() 934 .filter(feature -> currSlotId == feature.slotId) 935 .collect(Collectors.toList())); 936 } else { 937 // Add all features to the device service that are not currently covered by 938 // the carrier ImsService. 939 HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatures = 940 new HashSet<>(info.getSupportedFeatures()); 941 deviceFeatures.removeAll(carrierImsInfo.getSupportedFeatures()); 942 // only add features for current slot 943 imsFeaturesBySlot.addAll(deviceFeatures.stream() 944 .filter(feature -> currSlotId == feature.slotId).collect( 945 Collectors.toList())); 946 } 947 } 948 } 949 return imsFeaturesBySlot; 950 } 951 952 /** 953 * Implementation of 954 * {@link ImsServiceController.ImsServiceControllerCallbacks#imsServiceFeatureCreated}, which 955 * removes the ImsServiceController from the mBoundImsServicesByFeature structure. 956 */ imsServiceFeatureCreated(int slotId, int feature, ImsServiceController controller)957 public void imsServiceFeatureCreated(int slotId, int feature, ImsServiceController controller) { 958 putImsController(slotId, feature, controller); 959 } 960 961 /** 962 * Implementation of 963 * {@link ImsServiceController.ImsServiceControllerCallbacks#imsServiceFeatureRemoved}, which 964 * removes the ImsServiceController from the mBoundImsServicesByFeature structure. 965 */ imsServiceFeatureRemoved(int slotId, int feature, ImsServiceController controller)966 public void imsServiceFeatureRemoved(int slotId, int feature, ImsServiceController controller) { 967 removeImsController(slotId, feature); 968 } 969 970 /** 971 * Implementation of 972 * {@link ImsServiceController.ImsServiceControllerCallbacks#imsServiceFeaturesChanged, which 973 * notify the ImsResolver of a change to the supported ImsFeatures of a connected ImsService. 974 */ imsServiceFeaturesChanged(ImsFeatureConfiguration config, ImsServiceController controller)975 public void imsServiceFeaturesChanged(ImsFeatureConfiguration config, 976 ImsServiceController controller) { 977 if (controller == null || config == null) { 978 return; 979 } 980 Log.i(TAG, "imsServiceFeaturesChanged: config=" + config.getServiceFeatures() 981 + ", ComponentName=" + controller.getComponentName()); 982 handleFeaturesChanged(controller.getComponentName(), config.getServiceFeatures()); 983 } 984 985 /** 986 * Determines if the features specified should cause a bind or keep a binding active to an 987 * ImsService. 988 * @return true if MMTEL or RCS features are present, false if they are not or only 989 * EMERGENCY_MMTEL is specified. 990 */ shouldFeaturesCauseBind( HashSet<ImsFeatureConfiguration.FeatureSlotPair> features)991 private boolean shouldFeaturesCauseBind( 992 HashSet<ImsFeatureConfiguration.FeatureSlotPair> features) { 993 long bindableFeatures = features.stream() 994 // remove all emergency features 995 .filter(f -> f.featureType != ImsFeature.FEATURE_EMERGENCY_MMTEL).count(); 996 return bindableFeatures > 0; 997 } 998 999 // Possibly rebind to another ImsService if currently installed ImsServices were changed or if 1000 // the SIM card has changed. 1001 // Called from the handler ONLY maybeRebindService(int slotId, String newPackageName)1002 private void maybeRebindService(int slotId, String newPackageName) { 1003 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) { 1004 // not specified, replace package on all slots. 1005 for (int i = 0; i < mNumSlots; i++) { 1006 updateBoundCarrierServices(i, newPackageName); 1007 } 1008 } else { 1009 updateBoundCarrierServices(slotId, newPackageName); 1010 } 1011 1012 } 1013 carrierConfigChanged(int slotId)1014 private void carrierConfigChanged(int slotId) { 1015 int subId = mSubscriptionManagerProxy.getSubId(slotId); 1016 PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId); 1017 if (config != null) { 1018 String newPackageName = config.getString( 1019 CarrierConfigManager.KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, null); 1020 maybeRebindService(slotId, newPackageName); 1021 } else { 1022 Log.w(TAG, "carrierConfigChanged: CarrierConfig is null!"); 1023 } 1024 } 1025 updateBoundCarrierServices(int slotId, String newPackageName)1026 private void updateBoundCarrierServices(int slotId, String newPackageName) { 1027 if (slotId > SubscriptionManager.INVALID_SIM_SLOT_INDEX && slotId < mNumSlots) { 1028 String oldPackageName = mCarrierServices[slotId]; 1029 mCarrierServices[slotId] = newPackageName; 1030 if (!TextUtils.equals(newPackageName, oldPackageName)) { 1031 Log.i(TAG, "Carrier Config updated, binding new ImsService"); 1032 // Unbind old ImsService, not needed anymore 1033 // ImsService is retrieved from the cache. If the cache hasn't been populated yet, 1034 // the calls to unbind/bind will fail (intended during initial start up). 1035 unbindImsService(getImsServiceInfoFromCache(oldPackageName)); 1036 ImsServiceInfo newInfo = getImsServiceInfoFromCache(newPackageName); 1037 // if there is no carrier ImsService, newInfo is null. This we still want to update 1038 // bindings for device ImsService to pick up the missing features. 1039 if (newInfo == null || newInfo.featureFromMetadata) { 1040 bindImsService(newInfo); 1041 // Recalculate the device ImsService features to reflect changes. 1042 updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService)); 1043 } else { 1044 // ImsServiceInfo that has not had features queried yet. Start async 1045 // bind and query features. 1046 scheduleQueryForFeatures(newInfo); 1047 } 1048 } 1049 } 1050 } 1051 1052 /** 1053 * Schedules a query for dynamic ImsService features. 1054 */ scheduleQueryForFeatures(ImsServiceInfo service, int delayMs)1055 private void scheduleQueryForFeatures(ImsServiceInfo service, int delayMs) { 1056 // if not current device/carrier service, don't perform query. If this changes, this method 1057 // will be called again. 1058 if (!isDeviceService(service) && getSlotForActiveCarrierService(service) 1059 == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { 1060 Log.i(TAG, "scheduleQueryForFeatures: skipping query for ImsService that is not" 1061 + " set as carrier/device ImsService."); 1062 return; 1063 } 1064 Message msg = Message.obtain(mHandler, HANDLER_START_DYNAMIC_FEATURE_QUERY, service); 1065 if (mHandler.hasMessages(HANDLER_START_DYNAMIC_FEATURE_QUERY, service)) { 1066 Log.d(TAG, "scheduleQueryForFeatures: dynamic query for " + service.name 1067 + " already scheduled"); 1068 return; 1069 } 1070 Log.d(TAG, "scheduleQueryForFeatures: starting dynamic query for " + service.name 1071 + " in " + delayMs + "ms."); 1072 mHandler.sendMessageDelayed(msg, delayMs); 1073 } 1074 scheduleQueryForFeatures(ComponentName name, int delayMs)1075 private void scheduleQueryForFeatures(ComponentName name, int delayMs) { 1076 ImsServiceInfo service = getImsServiceInfoFromCache(name.getPackageName()); 1077 if (service == null) { 1078 Log.w(TAG, "scheduleQueryForFeatures: Couldn't find cached info for name: " + name); 1079 return; 1080 } 1081 scheduleQueryForFeatures(service, delayMs); 1082 } 1083 scheduleQueryForFeatures(ImsServiceInfo service)1084 private void scheduleQueryForFeatures(ImsServiceInfo service) { 1085 scheduleQueryForFeatures(service, 0); 1086 } 1087 1088 /** 1089 * Schedules the processing of a completed query. 1090 */ handleFeaturesChanged(ComponentName name, Set<ImsFeatureConfiguration.FeatureSlotPair> features)1091 private void handleFeaturesChanged(ComponentName name, 1092 Set<ImsFeatureConfiguration.FeatureSlotPair> features) { 1093 SomeArgs args = SomeArgs.obtain(); 1094 args.arg1 = name; 1095 args.arg2 = features; 1096 mHandler.obtainMessage(HANDLER_DYNAMIC_FEATURE_CHANGE, args).sendToTarget(); 1097 } 1098 1099 // Starts a dynamic query. Called from handler ONLY. startDynamicQuery(ImsServiceInfo service)1100 private void startDynamicQuery(ImsServiceInfo service) { 1101 boolean queryStarted = mFeatureQueryManager.startQuery(service.name, 1102 service.controllerFactory.getServiceInterface()); 1103 if (!queryStarted) { 1104 Log.w(TAG, "startDynamicQuery: service could not connect. Retrying after delay."); 1105 scheduleQueryForFeatures(service, DELAY_DYNAMIC_QUERY_MS); 1106 } else { 1107 Log.d(TAG, "startDynamicQuery: Service queried, waiting for response."); 1108 } 1109 } 1110 1111 // process complete dynamic query. Called from handler ONLY. dynamicQueryComplete(ComponentName name, Set<ImsFeatureConfiguration.FeatureSlotPair> features)1112 private void dynamicQueryComplete(ComponentName name, 1113 Set<ImsFeatureConfiguration.FeatureSlotPair> features) { 1114 ImsServiceInfo service = getImsServiceInfoFromCache(name.getPackageName()); 1115 if (service == null) { 1116 Log.w(TAG, "handleFeaturesChanged: Couldn't find cached info for name: " 1117 + name); 1118 return; 1119 } 1120 // Add features to service 1121 service.replaceFeatures(features); 1122 if (isActiveCarrierService(service)) { 1123 // New ImsService is registered to active carrier services and must be newly 1124 // bound. 1125 bindImsService(service); 1126 // Update existing device service features 1127 updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService)); 1128 } else if (isDeviceService(service)) { 1129 // New ImsService is registered as device default and must be newly bound. 1130 bindImsService(service); 1131 } 1132 } 1133 1134 /** 1135 * @return true if the ImsResolver is in the process of resolving a dynamic query and should not 1136 * be considered available, false if the ImsResolver is idle. 1137 */ isResolvingBinding()1138 public boolean isResolvingBinding() { 1139 return mHandler.hasMessages(HANDLER_START_DYNAMIC_FEATURE_QUERY) 1140 // We haven't processed this message yet, so it is still resolving. 1141 || mHandler.hasMessages(HANDLER_DYNAMIC_FEATURE_CHANGE) 1142 || mFeatureQueryManager.isQueryInProgress(); 1143 } 1144 printFeatures(Set<ImsFeatureConfiguration.FeatureSlotPair> features)1145 private String printFeatures(Set<ImsFeatureConfiguration.FeatureSlotPair> features) { 1146 StringBuilder featureString = new StringBuilder(); 1147 featureString.append("features: ["); 1148 if (features != null) { 1149 for (ImsFeatureConfiguration.FeatureSlotPair feature : features) { 1150 featureString.append("{"); 1151 featureString.append(feature.slotId); 1152 featureString.append(","); 1153 featureString.append(feature.featureType); 1154 featureString.append("} "); 1155 } 1156 featureString.append("]"); 1157 } 1158 return featureString.toString(); 1159 } 1160 1161 /** 1162 * Returns the ImsServiceInfo that matches the provided packageName. Visible for testing 1163 * the ImsService caching functionality. 1164 */ 1165 @VisibleForTesting getImsServiceInfoFromCache(String packageName)1166 public ImsServiceInfo getImsServiceInfoFromCache(String packageName) { 1167 if (TextUtils.isEmpty(packageName)) { 1168 return null; 1169 } 1170 ImsServiceInfo infoFilter = getInfoByPackageName(mInstalledServicesCache, packageName); 1171 if (infoFilter != null) { 1172 return infoFilter; 1173 } else { 1174 return null; 1175 } 1176 } 1177 1178 // Return the ImsServiceInfo specified for the package name. If the package name is null, 1179 // get all packages that support ImsServices. getImsServiceInfo(String packageName)1180 private List<ImsServiceInfo> getImsServiceInfo(String packageName) { 1181 List<ImsServiceInfo> infos = new ArrayList<>(); 1182 if (!mIsDynamicBinding) { 1183 // always return the same ImsService info. 1184 infos.addAll(getStaticImsService()); 1185 } else { 1186 // Search for Current ImsService implementations 1187 infos.addAll(searchForImsServices(packageName, mImsServiceControllerFactory)); 1188 // Search for compat ImsService Implementations 1189 infos.addAll(searchForImsServices(packageName, mImsServiceControllerFactoryCompat)); 1190 } 1191 return infos; 1192 } 1193 getStaticImsService()1194 private List<ImsServiceInfo> getStaticImsService() { 1195 List<ImsServiceInfo> infos = new ArrayList<>(); 1196 1197 ImsServiceInfo info = new ImsServiceInfo(mNumSlots); 1198 info.name = mStaticComponent; 1199 info.controllerFactory = mImsServiceControllerFactoryStaticBindingCompat; 1200 info.addFeatureForAllSlots(ImsFeature.FEATURE_EMERGENCY_MMTEL); 1201 info.addFeatureForAllSlots(ImsFeature.FEATURE_MMTEL); 1202 infos.add(info); 1203 return infos; 1204 } 1205 searchForImsServices(String packageName, ImsServiceControllerFactory controllerFactory)1206 private List<ImsServiceInfo> searchForImsServices(String packageName, 1207 ImsServiceControllerFactory controllerFactory) { 1208 List<ImsServiceInfo> infos = new ArrayList<>(); 1209 1210 Intent serviceIntent = new Intent(controllerFactory.getServiceInterface()); 1211 serviceIntent.setPackage(packageName); 1212 1213 PackageManager packageManager = mContext.getPackageManager(); 1214 for (ResolveInfo entry : packageManager.queryIntentServicesAsUser( 1215 serviceIntent, 1216 PackageManager.GET_META_DATA, 1217 mContext.getUserId())) { 1218 ServiceInfo serviceInfo = entry.serviceInfo; 1219 1220 if (serviceInfo != null) { 1221 ImsServiceInfo info = new ImsServiceInfo(mNumSlots); 1222 info.name = new ComponentName(serviceInfo.packageName, serviceInfo.name); 1223 info.controllerFactory = controllerFactory; 1224 1225 // we will allow the manifest method of declaring manifest features in two cases: 1226 // 1) it is the device overlay "default" ImsService, where the features do not 1227 // change (the new method can still be used if the default does not define manifest 1228 // entries). 1229 // 2) using the "compat" ImsService, which only supports manifest query. 1230 if (isDeviceService(info) 1231 || mImsServiceControllerFactoryCompat == controllerFactory) { 1232 if (serviceInfo.metaData != null) { 1233 if (serviceInfo.metaData.getBoolean(METADATA_EMERGENCY_MMTEL_FEATURE, 1234 false)) { 1235 info.addFeatureForAllSlots(ImsFeature.FEATURE_EMERGENCY_MMTEL); 1236 } 1237 if (serviceInfo.metaData.getBoolean(METADATA_MMTEL_FEATURE, false)) { 1238 info.addFeatureForAllSlots(ImsFeature.FEATURE_MMTEL); 1239 } 1240 if (serviceInfo.metaData.getBoolean(METADATA_RCS_FEATURE, false)) { 1241 info.addFeatureForAllSlots(ImsFeature.FEATURE_RCS); 1242 } 1243 } 1244 // Only dynamic query if we are not a compat version of ImsService and the 1245 // default service. 1246 if (mImsServiceControllerFactoryCompat != controllerFactory 1247 && info.getSupportedFeatures().isEmpty()) { 1248 // metadata empty, try dynamic query instead 1249 info.featureFromMetadata = false; 1250 } 1251 } else { 1252 // We are a carrier service and not using the compat version of ImsService. 1253 info.featureFromMetadata = false; 1254 } 1255 Log.i(TAG, "service name: " + info.name + ", manifest query: " 1256 + info.featureFromMetadata); 1257 // Check manifest permission to be sure that the service declares the correct 1258 // permissions. Overridden if the METADATA_OVERRIDE_PERM_CHECK metadata is set to 1259 // true. 1260 // NOTE: METADATA_OVERRIDE_PERM_CHECK should only be set for testing. 1261 if (TextUtils.equals(serviceInfo.permission, Manifest.permission.BIND_IMS_SERVICE) 1262 || serviceInfo.metaData.getBoolean(METADATA_OVERRIDE_PERM_CHECK, false)) { 1263 infos.add(info); 1264 } else { 1265 Log.w(TAG, "ImsService is not protected with BIND_IMS_SERVICE permission: " 1266 + info.name); 1267 } 1268 } 1269 } 1270 return infos; 1271 } 1272 } 1273