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.NonNull; 21 import android.annotation.Nullable; 22 import android.content.BroadcastReceiver; 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.content.pm.PackageManager; 28 import android.content.pm.ResolveInfo; 29 import android.content.pm.ServiceInfo; 30 import android.os.AsyncResult; 31 import android.os.Handler; 32 import android.os.Looper; 33 import android.os.Message; 34 import android.os.PersistableBundle; 35 import android.os.RemoteException; 36 import android.os.UserHandle; 37 import android.os.UserManager; 38 import android.telephony.CarrierConfigManager; 39 import android.telephony.SubscriptionManager; 40 import android.telephony.TelephonyManager; 41 import android.telephony.ims.ImsService; 42 import android.telephony.ims.aidl.IImsConfig; 43 import android.telephony.ims.aidl.IImsMmTelFeature; 44 import android.telephony.ims.aidl.IImsRcsFeature; 45 import android.telephony.ims.aidl.IImsRegistration; 46 import android.telephony.ims.feature.ImsFeature; 47 import android.telephony.ims.feature.MmTelFeature; 48 import android.telephony.ims.stub.ImsFeatureConfiguration; 49 import android.text.TextUtils; 50 import android.util.ArrayMap; 51 import android.util.LocalLog; 52 import android.util.Log; 53 import android.util.SparseArray; 54 55 import com.android.ims.internal.IImsServiceFeatureCallback; 56 import com.android.internal.annotations.VisibleForTesting; 57 import com.android.internal.os.SomeArgs; 58 import com.android.internal.telephony.PhoneConfigurationManager; 59 import com.android.internal.util.IndentingPrintWriter; 60 61 import java.io.FileDescriptor; 62 import java.io.PrintWriter; 63 import java.util.ArrayList; 64 import java.util.Collections; 65 import java.util.HashMap; 66 import java.util.HashSet; 67 import java.util.List; 68 import java.util.Map; 69 import java.util.Objects; 70 import java.util.Set; 71 import java.util.concurrent.LinkedBlockingQueue; 72 import java.util.concurrent.TimeUnit; 73 import java.util.stream.Collectors; 74 75 /** 76 * Creates a list of ImsServices that are available to bind to based on the Device configuration 77 * overlay values "config_ims_rcs_package" and "config_ims_mmtel_package" as well as Carrier 78 * Configuration value "config_ims_rcs_package_override_string" and 79 * "config_ims_mmtel_package_override_string". 80 * These ImsServices are then bound to in the following order for each mmtel and rcs feature: 81 * 82 * 1. Carrier Config defined override value per SIM. 83 * 2. Device overlay default value (including no SIM case). 84 * 85 * ImsManager can then retrieve the binding to the correct ImsService using 86 * {@link #getImsServiceControllerAndListen} on a per-slot and per feature basis. 87 */ 88 89 public class ImsResolver implements ImsServiceController.ImsServiceControllerCallbacks { 90 91 private static final String TAG = "ImsResolver"; 92 private static final int GET_IMS_SERVICE_TIMEOUT_MS = 5000; 93 94 @VisibleForTesting 95 public static final String METADATA_EMERGENCY_MMTEL_FEATURE = 96 "android.telephony.ims.EMERGENCY_MMTEL_FEATURE"; 97 @VisibleForTesting 98 public static final String METADATA_MMTEL_FEATURE = "android.telephony.ims.MMTEL_FEATURE"; 99 @VisibleForTesting 100 public static final String METADATA_RCS_FEATURE = "android.telephony.ims.RCS_FEATURE"; 101 // Overrides the correctness permission check of android.permission.BIND_IMS_SERVICE for any 102 // ImsService that is connecting to the platform. 103 // This should ONLY be used for testing and should not be used in production ImsServices. 104 private static final String METADATA_OVERRIDE_PERM_CHECK = "override_bind_check"; 105 106 // Based on updates from PackageManager 107 private static final int HANDLER_ADD_PACKAGE = 0; 108 // Based on updates from PackageManager 109 private static final int HANDLER_REMOVE_PACKAGE = 1; 110 // Based on updates from CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED 111 private static final int HANDLER_CONFIG_CHANGED = 2; 112 // A query has been started for an ImsService to relay the features they support. 113 private static final int HANDLER_START_DYNAMIC_FEATURE_QUERY = 3; 114 // A dynamic query to request ImsService features has completed. 115 private static final int HANDLER_DYNAMIC_FEATURE_CHANGE = 4; 116 // Testing: Overrides the current configuration for ImsService binding 117 private static final int HANDLER_OVERRIDE_IMS_SERVICE_CONFIG = 5; 118 // Based on boot complete indication. When this happens, there may be ImsServices that are not 119 // direct boot aware that need to be started. 120 private static final int HANDLER_BOOT_COMPLETE = 6; 121 // Sent when the number of slots has dynamically changed on the device. We will need to 122 // resize available ImsServiceController slots and perform dynamic queries again. 123 private static final int HANDLER_MSIM_CONFIGURATION_CHANGE = 7; 124 125 // Delay between dynamic ImsService queries. 126 private static final int DELAY_DYNAMIC_QUERY_MS = 5000; 127 128 private static class OverrideConfig { 129 public final int slotId; 130 public final boolean isCarrierService; 131 public final Map<Integer, String> featureTypeToPackageMap; 132 OverrideConfig(int slotIndex, boolean isCarrier, Map<Integer, String> feature)133 OverrideConfig(int slotIndex, boolean isCarrier, Map<Integer, String> feature) { 134 slotId = slotIndex; 135 isCarrierService = isCarrier; 136 featureTypeToPackageMap = feature; 137 } 138 } 139 140 /** 141 * Stores information about an ImsService, including the package name, class name, and features 142 * that the service supports. 143 */ 144 @VisibleForTesting 145 public static class ImsServiceInfo { 146 public ComponentName name; 147 // Determines if features were created from metadata in the manifest or through dynamic 148 // query. 149 public boolean featureFromMetadata = true; 150 public ImsServiceControllerFactory controllerFactory; 151 152 // Map slotId->Feature 153 private final HashSet<ImsFeatureConfiguration.FeatureSlotPair> mSupportedFeatures; 154 ImsServiceInfo()155 public ImsServiceInfo() { 156 mSupportedFeatures = new HashSet<>(); 157 } 158 addFeatureForAllSlots(int numSlots, int feature)159 void addFeatureForAllSlots(int numSlots, int feature) { 160 for (int i = 0; i < numSlots; i++) { 161 mSupportedFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(i, feature)); 162 } 163 } 164 replaceFeatures(Set<ImsFeatureConfiguration.FeatureSlotPair> newFeatures)165 void replaceFeatures(Set<ImsFeatureConfiguration.FeatureSlotPair> newFeatures) { 166 mSupportedFeatures.clear(); 167 mSupportedFeatures.addAll(newFeatures); 168 } 169 170 @VisibleForTesting getSupportedFeatures()171 public Set<ImsFeatureConfiguration.FeatureSlotPair> getSupportedFeatures() { 172 return mSupportedFeatures; 173 } 174 175 @Override equals(Object o)176 public boolean equals(Object o) { 177 if (this == o) return true; 178 if (o == null || getClass() != o.getClass()) return false; 179 180 ImsServiceInfo that = (ImsServiceInfo) o; 181 182 if (name != null ? !name.equals(that.name) : that.name != null) return false; 183 if (!mSupportedFeatures.equals(that.mSupportedFeatures)) { 184 return false; 185 } 186 return controllerFactory != null ? controllerFactory.equals(that.controllerFactory) 187 : that.controllerFactory == null; 188 } 189 190 @Override hashCode()191 public int hashCode() { 192 // We do not include mSupportedFeatures in hashcode because the internal structure 193 // changes after adding. 194 int result = name != null ? name.hashCode() : 0; 195 result = 31 * result + (controllerFactory != null ? controllerFactory.hashCode() : 0); 196 return result; 197 } 198 199 @Override toString()200 public String toString() { 201 return "[ImsServiceInfo] name=" 202 + name 203 + ", featureFromMetadata=" 204 + featureFromMetadata 205 + "," 206 + printFeatures(mSupportedFeatures); 207 } 208 } 209 210 // Receives broadcasts from the system involving changes to the installed applications. If 211 // an ImsService that we are configured to use is installed, we must bind to it. 212 private BroadcastReceiver mAppChangedReceiver = new BroadcastReceiver() { 213 @Override 214 public void onReceive(Context context, Intent intent) { 215 final String action = intent.getAction(); 216 final String packageName = intent.getData().getSchemeSpecificPart(); 217 switch (action) { 218 case Intent.ACTION_PACKAGE_ADDED: 219 // intentional fall-through 220 case Intent.ACTION_PACKAGE_REPLACED: 221 // intentional fall-through 222 case Intent.ACTION_PACKAGE_CHANGED: 223 mHandler.obtainMessage(HANDLER_ADD_PACKAGE, packageName).sendToTarget(); 224 break; 225 case Intent.ACTION_PACKAGE_REMOVED: 226 mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE, packageName).sendToTarget(); 227 break; 228 default: 229 return; 230 } 231 } 232 }; 233 234 // Receives the broadcast that a new Carrier Config has been loaded in order to possibly 235 // unbind from one service and bind to another. 236 private BroadcastReceiver mConfigChangedReceiver = new BroadcastReceiver() { 237 @Override 238 public void onReceive(Context context, Intent intent) { 239 240 int slotId = intent.getIntExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, 241 SubscriptionManager.INVALID_SIM_SLOT_INDEX); 242 243 if (slotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { 244 Log.i(TAG, "Received CCC for invalid slot id."); 245 return; 246 } 247 248 int subId = intent.getIntExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, 249 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 250 int slotSimState = mTelephonyManagerProxy.getSimState(mContext, slotId); 251 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID 252 && slotSimState != TelephonyManager.SIM_STATE_ABSENT) { 253 // We only care about carrier config updates that happen when a slot is known to be 254 // absent or populated and the carrier config has been loaded. 255 Log.i(TAG, "Received CCC for slot " + slotId + " and sim state " 256 + slotSimState + ", ignoring."); 257 return; 258 } 259 260 Log.i(TAG, "Received Carrier Config Changed for SlotId: " + slotId); 261 262 mHandler.obtainMessage(HANDLER_CONFIG_CHANGED, slotId).sendToTarget(); 263 } 264 }; 265 266 // Receives the broadcast that the device has finished booting (and the device is no longer 267 // encrypted). 268 private BroadcastReceiver mBootCompleted = new BroadcastReceiver() { 269 @Override 270 public void onReceive(Context context, Intent intent) { 271 Log.i(TAG, "Received BOOT_COMPLETED"); 272 // Recalculate all cached services to pick up ones that have just been enabled since 273 // boot complete. 274 mHandler.obtainMessage(HANDLER_BOOT_COMPLETE, null).sendToTarget(); 275 } 276 }; 277 278 /** 279 * Testing interface used to mock SubscriptionManager in testing 280 */ 281 @VisibleForTesting 282 public interface SubscriptionManagerProxy { 283 /** 284 * Mock-able interface for {@link SubscriptionManager#getSubId(int)} used for testing. 285 */ getSubId(int slotId)286 int getSubId(int slotId); 287 /** 288 * Mock-able interface for {@link SubscriptionManager#getSlotIndex(int)} used for testing. 289 */ getSlotIndex(int subId)290 int getSlotIndex(int subId); 291 } 292 293 /** 294 * Testing interface used to stub out TelephonyManager dependencies. 295 */ 296 @VisibleForTesting 297 public interface TelephonyManagerProxy { 298 /** 299 * @return the SIM state for the slot ID specified. 300 */ getSimState(Context context, int slotId)301 int getSimState(Context context, int slotId); 302 } 303 304 private TelephonyManagerProxy mTelephonyManagerProxy = new TelephonyManagerProxy() { 305 @Override 306 public int getSimState(Context context, int slotId) { 307 TelephonyManager tm = context.getSystemService(TelephonyManager.class); 308 if (tm == null) { 309 return TelephonyManager.SIM_STATE_UNKNOWN; 310 } 311 return tm.getSimState(slotId); 312 } 313 }; 314 315 private SubscriptionManagerProxy mSubscriptionManagerProxy = new SubscriptionManagerProxy() { 316 @Override 317 public int getSubId(int slotId) { 318 int[] subIds = SubscriptionManager.getSubId(slotId); 319 if (subIds != null) { 320 // This is done in all other places getSubId is used. 321 return subIds[0]; 322 } 323 return SubscriptionManager.INVALID_SUBSCRIPTION_ID; 324 } 325 326 @Override 327 public int getSlotIndex(int subId) { 328 return SubscriptionManager.getSlotIndex(subId); 329 } 330 }; 331 332 /** 333 * Testing interface for injecting mock ImsServiceControllers. 334 */ 335 @VisibleForTesting 336 public interface ImsServiceControllerFactory { 337 /** 338 * @return the Service Interface String used for binding the ImsService. 339 */ getServiceInterface()340 String getServiceInterface(); 341 /** 342 * @return the ImsServiceController created using the context and componentName supplied. 343 */ create(Context context, ComponentName componentName, ImsServiceController.ImsServiceControllerCallbacks callbacks)344 ImsServiceController create(Context context, ComponentName componentName, 345 ImsServiceController.ImsServiceControllerCallbacks callbacks); 346 } 347 348 private ImsServiceControllerFactory mImsServiceControllerFactory = 349 new ImsServiceControllerFactory() { 350 351 @Override 352 public String getServiceInterface() { 353 return ImsService.SERVICE_INTERFACE; 354 } 355 356 @Override 357 public ImsServiceController create(Context context, ComponentName componentName, 358 ImsServiceController.ImsServiceControllerCallbacks callbacks) { 359 return new ImsServiceController(context, componentName, callbacks); 360 } 361 }; 362 363 /** 364 * Used for testing. 365 */ 366 @VisibleForTesting 367 public interface ImsDynamicQueryManagerFactory { create(Context context, ImsServiceFeatureQueryManager.Listener listener)368 ImsServiceFeatureQueryManager create(Context context, 369 ImsServiceFeatureQueryManager.Listener listener); 370 } 371 372 private ImsServiceControllerFactory mImsServiceControllerFactoryCompat = 373 new ImsServiceControllerFactory() { 374 @Override 375 public String getServiceInterface() { 376 return android.telephony.ims.compat.ImsService.SERVICE_INTERFACE; 377 } 378 379 @Override 380 public ImsServiceController create(Context context, ComponentName componentName, 381 ImsServiceController.ImsServiceControllerCallbacks callbacks) { 382 return new ImsServiceControllerCompat(context, componentName, callbacks); 383 } 384 }; 385 386 private ImsDynamicQueryManagerFactory mDynamicQueryManagerFactory = 387 ImsServiceFeatureQueryManager::new; 388 389 private final CarrierConfigManager mCarrierConfigManager; 390 private final Context mContext; 391 // Special context created only for registering receivers for all users using UserHandle.ALL. 392 // The lifetime of a registered receiver is bounded by the lifetime of the context it's 393 // registered through, so we must retain the Context as long as we need the receiver to be 394 // active. 395 private final Context mReceiverContext; 396 // Locks mBoundImsServicesByFeature only. Be careful to avoid deadlocks from 397 // ImsServiceController callbacks. 398 private final Object mBoundServicesLock = new Object(); 399 private int mNumSlots; 400 // Array index corresponds to slot, per slot there is a feature->package name mapping. 401 // should only be accessed from handler 402 private SparseArray<Map<Integer, String>> mCarrierServices; 403 // Package name of the default device services, Maps ImsFeature -> packageName. 404 // should only be accessed from handler 405 private Map<Integer, String> mDeviceServices; 406 // Persistent Logging 407 private final LocalLog mEventLog = new LocalLog(50); 408 409 private boolean mBootCompletedHandlerRan = false; 410 411 // Synchronize all events on a handler to ensure that the cache includes the most recent 412 // version of the installed ImsServices. 413 private Handler mHandler = new Handler(Looper.getMainLooper(), (msg) -> { 414 switch (msg.what) { 415 case HANDLER_ADD_PACKAGE: { 416 String packageName = (String) msg.obj; 417 maybeAddedImsService(packageName); 418 break; 419 } 420 case HANDLER_REMOVE_PACKAGE: { 421 String packageName = (String) msg.obj; 422 maybeRemovedImsService(packageName); 423 break; 424 } 425 case HANDLER_BOOT_COMPLETE: { 426 if (!mBootCompletedHandlerRan) { 427 mBootCompletedHandlerRan = true; 428 mEventLog.log("handling BOOT_COMPLETE"); 429 // Re-evaluate bound services for all slots after requerying packagemanager 430 maybeAddedImsService(null /*packageName*/); 431 } 432 break; 433 } 434 case HANDLER_CONFIG_CHANGED: { 435 int slotId = (Integer) msg.obj; 436 // If the msim config has changed and there is a residual event for an invalid slot, 437 // ignore. 438 if (slotId >= mNumSlots) { 439 Log.w(TAG, "HANDLER_CONFIG_CHANGED for invalid slotid=" + slotId); 440 break; 441 } 442 carrierConfigChanged(slotId); 443 break; 444 } 445 case HANDLER_START_DYNAMIC_FEATURE_QUERY: { 446 ImsServiceInfo info = (ImsServiceInfo) msg.obj; 447 startDynamicQuery(info); 448 break; 449 } 450 case HANDLER_DYNAMIC_FEATURE_CHANGE: { 451 SomeArgs args = (SomeArgs) msg.obj; 452 ComponentName name = (ComponentName) args.arg1; 453 Set<ImsFeatureConfiguration.FeatureSlotPair> features = 454 (Set<ImsFeatureConfiguration.FeatureSlotPair>) args.arg2; 455 args.recycle(); 456 dynamicQueryComplete(name, features); 457 break; 458 } 459 case HANDLER_OVERRIDE_IMS_SERVICE_CONFIG: { 460 OverrideConfig config = (OverrideConfig) msg.obj; 461 if (config.isCarrierService) { 462 overrideCarrierService(config.slotId, 463 config.featureTypeToPackageMap); 464 } else { 465 overrideDeviceService(config.featureTypeToPackageMap); 466 } 467 break; 468 } 469 case HANDLER_MSIM_CONFIGURATION_CHANGE: { 470 AsyncResult result = (AsyncResult) msg.obj; 471 handleMsimConfigChange((Integer) result.result); 472 break; 473 } 474 default: 475 return false; 476 } 477 return true; 478 }); 479 480 // Results from dynamic queries to ImsService regarding the features they support. 481 private ImsServiceFeatureQueryManager.Listener mDynamicQueryListener = 482 new ImsServiceFeatureQueryManager.Listener() { 483 484 @Override 485 public void onComplete(ComponentName name, 486 Set<ImsFeatureConfiguration.FeatureSlotPair> features) { 487 Log.d(TAG, "onComplete called for name: " + name + printFeatures(features)); 488 handleFeaturesChanged(name, features); 489 } 490 491 @Override 492 public void onError(ComponentName name) { 493 Log.w(TAG, "onError: " + name + "returned with an error result"); 494 mEventLog.log("onError - dynamic query error for " + name); 495 scheduleQueryForFeatures(name, DELAY_DYNAMIC_QUERY_MS); 496 } 497 498 @Override 499 public void onPermanentError(ComponentName name) { 500 Log.w(TAG, "onPermanentError: component=" + name); 501 mEventLog.log("onPermanentError - error for " + name); 502 mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE, 503 name.getPackageName()).sendToTarget(); 504 } 505 }; 506 507 // Used during testing, overrides the carrier services while non-empty. 508 // Array index corresponds to slot, per slot there is a feature->package name mapping. 509 // should only be accessed from handler 510 private SparseArray<SparseArray<String>> mOverrideServices; 511 // Outer array index corresponds to Slot Id, Maps ImsFeature.FEATURE->bound ImsServiceController 512 // Locked on mBoundServicesLock 513 private SparseArray<SparseArray<ImsServiceController>> mBoundImsServicesByFeature; 514 // not locked, only accessed on a handler thread. 515 // Tracks list of all installed ImsServices 516 private Map<ComponentName, ImsServiceInfo> mInstalledServicesCache = new HashMap<>(); 517 // not locked, only accessed on a handler thread. 518 // Active ImsServiceControllers, which are bound to ImsServices. 519 private Map<ComponentName, ImsServiceController> mActiveControllers = new HashMap<>(); 520 private ImsServiceFeatureQueryManager mFeatureQueryManager; 521 ImsResolver(Context context, String defaultMmTelPackageName, String defaultRcsPackageName, int numSlots)522 public ImsResolver(Context context, String defaultMmTelPackageName, 523 String defaultRcsPackageName, int numSlots) { 524 Log.i(TAG, "device MMTEL package: " + defaultMmTelPackageName + ", device RCS package:" 525 + defaultRcsPackageName); 526 mContext = context; 527 mNumSlots = numSlots; 528 mReceiverContext = context.createContextAsUser(UserHandle.ALL, 0 /*flags*/); 529 530 mCarrierServices = new SparseArray<>(mNumSlots); 531 mDeviceServices = new ArrayMap<>(); 532 setDeviceConfiguration(defaultMmTelPackageName, ImsFeature.FEATURE_EMERGENCY_MMTEL); 533 setDeviceConfiguration(defaultMmTelPackageName, ImsFeature.FEATURE_MMTEL); 534 setDeviceConfiguration(defaultRcsPackageName, ImsFeature.FEATURE_RCS); 535 mCarrierConfigManager = (CarrierConfigManager) mContext.getSystemService( 536 Context.CARRIER_CONFIG_SERVICE); 537 mOverrideServices = new SparseArray<>(0 /*initial size*/); 538 mBoundImsServicesByFeature = new SparseArray<>(mNumSlots); 539 540 IntentFilter appChangedFilter = new IntentFilter(); 541 appChangedFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 542 appChangedFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 543 appChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 544 appChangedFilter.addDataScheme("package"); 545 mReceiverContext.registerReceiver(mAppChangedReceiver, appChangedFilter); 546 mReceiverContext.registerReceiver(mConfigChangedReceiver, new IntentFilter( 547 CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); 548 549 UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 550 if (userManager.isUserUnlocked()) { 551 mHandler.obtainMessage(HANDLER_BOOT_COMPLETE, null).sendToTarget(); 552 } else { 553 mReceiverContext.registerReceiver(mBootCompleted, new IntentFilter( 554 Intent.ACTION_BOOT_COMPLETED)); 555 if (userManager.isUserUnlocked()) { 556 mHandler.obtainMessage(HANDLER_BOOT_COMPLETE, null).sendToTarget(); 557 } 558 } 559 } 560 561 @VisibleForTesting setTelephonyManagerProxy(TelephonyManagerProxy proxy)562 public void setTelephonyManagerProxy(TelephonyManagerProxy proxy) { 563 mTelephonyManagerProxy = proxy; 564 } 565 566 @VisibleForTesting setSubscriptionManagerProxy(SubscriptionManagerProxy proxy)567 public void setSubscriptionManagerProxy(SubscriptionManagerProxy proxy) { 568 mSubscriptionManagerProxy = proxy; 569 } 570 571 @VisibleForTesting setImsServiceControllerFactory(ImsServiceControllerFactory factory)572 public void setImsServiceControllerFactory(ImsServiceControllerFactory factory) { 573 mImsServiceControllerFactory = factory; 574 } 575 576 @VisibleForTesting getHandler()577 public Handler getHandler() { 578 return mHandler; 579 } 580 581 @VisibleForTesting setImsDynamicQueryManagerFactory(ImsDynamicQueryManagerFactory m)582 public void setImsDynamicQueryManagerFactory(ImsDynamicQueryManagerFactory m) { 583 mDynamicQueryManagerFactory = m; 584 } 585 586 /** 587 * Needs to be called after the constructor to kick off the process of binding to ImsServices. 588 */ initialize()589 public void initialize() { 590 mEventLog.log("Initializing"); 591 Log.i(TAG, "Initializing cache."); 592 PhoneConfigurationManager.registerForMultiSimConfigChange(mHandler, 593 HANDLER_MSIM_CONFIGURATION_CHANGE, null); 594 mFeatureQueryManager = mDynamicQueryManagerFactory.create(mContext, mDynamicQueryListener); 595 596 // This will get all services with the correct intent filter from PackageManager 597 List<ImsServiceInfo> infos = getImsServiceInfo(null); 598 for (ImsServiceInfo info : infos) { 599 if (!mInstalledServicesCache.containsKey(info.name)) { 600 mInstalledServicesCache.put(info.name, info); 601 } 602 } 603 // Update the package names of the carrier ImsServices if they do not exist already and 604 // possibly bind if carrier configs exist. Otherwise wait for CarrierConfigChanged 605 // indication. 606 bindCarrierServicesIfAvailable(); 607 } 608 609 /** 610 * Destroys this ImsResolver. Used for tearing down static resources during testing. 611 */ 612 @VisibleForTesting destroy()613 public void destroy() { 614 PhoneConfigurationManager.unregisterForMultiSimConfigChange(mHandler); 615 mHandler.removeCallbacksAndMessages(null); 616 } 617 618 // Only start the bind if there is an existing Carrier Configuration. Otherwise, wait for 619 // carrier config changed. bindCarrierServicesIfAvailable()620 private void bindCarrierServicesIfAvailable() { 621 boolean hasConfigChanged = false; 622 for (int slotId = 0; slotId < mNumSlots; slotId++) { 623 Map<Integer, String> featureMap = getImsPackageOverrideConfig(slotId); 624 for (int f = ImsFeature.FEATURE_EMERGENCY_MMTEL; f < ImsFeature.FEATURE_MAX; f++) { 625 String newPackageName = featureMap.getOrDefault(f, ""); 626 if (!TextUtils.isEmpty(newPackageName)) { 627 mEventLog.log("bindCarrierServicesIfAvailable - carrier package found: " 628 + newPackageName + " on slot " + slotId); 629 setCarrierConfiguredPackageName(newPackageName, slotId, f); 630 ImsServiceInfo info = getImsServiceInfoFromCache(newPackageName); 631 // We do not want to trigger feature configuration changes unless there is 632 // already a valid carrier config change. 633 if (info != null && info.featureFromMetadata) { 634 hasConfigChanged = true; 635 } else { 636 // Config will change when this query completes 637 scheduleQueryForFeatures(info); 638 } 639 } 640 } 641 } 642 if (hasConfigChanged) calculateFeatureConfigurationChange(); 643 } 644 645 /** 646 * Notify ImsService to enable IMS for the framework. This will trigger IMS registration and 647 * trigger ImsFeature status updates. 648 */ enableIms(int slotId)649 public void enableIms(int slotId) { 650 SparseArray<ImsServiceController> controllers = getImsServiceControllers(slotId); 651 if (controllers != null) { 652 for (int i = 0; i < controllers.size(); i++) { 653 int key = controllers.keyAt(i); 654 controllers.get(key).enableIms(slotId); 655 } 656 } 657 } 658 659 /** 660 * Notify ImsService to disable IMS for the framework. This will trigger IMS de-registration and 661 * trigger ImsFeature capability status to become false. 662 */ disableIms(int slotId)663 public void disableIms(int slotId) { 664 SparseArray<ImsServiceController> controllers = getImsServiceControllers(slotId); 665 if (controllers != null) { 666 for (int i = 0; i < controllers.size(); i++) { 667 int key = controllers.keyAt(i); 668 controllers.get(key).disableIms(slotId); 669 } 670 } 671 } 672 673 /** 674 * Returns the {@link IImsMmTelFeature} that corresponds to the given slot Id or {@link null} if 675 * the service is not available. If an IImsMMTelFeature is available, the 676 * {@link IImsServiceFeatureCallback} callback is registered as a listener for feature updates. 677 * @param slotId The SIM slot that we are requesting the {@link IImsMmTelFeature} for. 678 * @param callback Listener that will send updates to ImsManager when there are updates to 679 * the feature. 680 * @return {@link IImsMmTelFeature} interface or {@link null} if it is unavailable. 681 */ getMmTelFeatureAndListen(int slotId, IImsServiceFeatureCallback callback)682 public IImsMmTelFeature getMmTelFeatureAndListen(int slotId, 683 IImsServiceFeatureCallback callback) { 684 ImsServiceController controller = getImsServiceControllerAndListen(slotId, 685 ImsFeature.FEATURE_MMTEL, callback); 686 return (controller != null) ? controller.getMmTelFeature(slotId) : null; 687 } 688 689 /** 690 * Returns the {@link IImsRcsFeature} that corresponds to the given slot Id for emergency 691 * calling or {@link null} if the service is not available. If an IImsMMTelFeature is 692 * available, the {@link IImsServiceFeatureCallback} callback is registered as a listener for 693 * feature updates. 694 * @param slotId The SIM slot that we are requesting the {@link IImsRcsFeature} for. 695 * @param callback listener that will send updates to ImsManager when there are updates to 696 * the feature. 697 * @return {@link IImsRcsFeature} interface or {@link null} if it is unavailable. 698 */ getRcsFeatureAndListen(int slotId, IImsServiceFeatureCallback callback)699 public IImsRcsFeature getRcsFeatureAndListen(int slotId, IImsServiceFeatureCallback callback) { 700 ImsServiceController controller = getImsServiceControllerAndListen(slotId, 701 ImsFeature.FEATURE_RCS, callback); 702 return (controller != null) ? controller.getRcsFeature(slotId) : null; 703 } 704 705 /** 706 * Returns the ImsRegistration structure associated with the slotId and feature specified. 707 */ getImsRegistration(int slotId, int feature)708 public @Nullable IImsRegistration getImsRegistration(int slotId, int feature) 709 throws RemoteException { 710 ImsServiceController controller = getImsServiceController(slotId, feature); 711 if (controller != null) { 712 return controller.getRegistration(slotId); 713 } 714 return null; 715 } 716 717 /** 718 * Returns the ImsConfig structure associated with the slotId and feature specified. 719 */ getImsConfig(int slotId, int feature)720 public @Nullable IImsConfig getImsConfig(int slotId, int feature) 721 throws RemoteException { 722 ImsServiceController controller = getImsServiceController(slotId, feature); 723 if (controller != null) { 724 return controller.getConfig(slotId); 725 } 726 return null; 727 } 728 729 @VisibleForTesting getImsServiceController(int slotId, int feature)730 public ImsServiceController getImsServiceController(int slotId, int feature) { 731 if (slotId < 0 || slotId >= mNumSlots) { 732 return null; 733 } 734 ImsServiceController controller; 735 synchronized (mBoundServicesLock) { 736 SparseArray<ImsServiceController> services = mBoundImsServicesByFeature.get(slotId); 737 if (services == null) { 738 return null; 739 } 740 controller = services.get(feature); 741 } 742 return controller; 743 } 744 getImsServiceControllers(int slotId)745 private SparseArray<ImsServiceController> getImsServiceControllers(int slotId) { 746 if (slotId < 0 || slotId >= mNumSlots) { 747 return null; 748 } 749 synchronized (mBoundServicesLock) { 750 SparseArray<ImsServiceController> services = mBoundImsServicesByFeature.get(slotId); 751 if (services == null) { 752 return null; 753 } 754 return services; 755 } 756 } 757 758 @VisibleForTesting getImsServiceControllerAndListen(int slotId, int feature, IImsServiceFeatureCallback callback)759 public ImsServiceController getImsServiceControllerAndListen(int slotId, int feature, 760 IImsServiceFeatureCallback callback) { 761 ImsServiceController controller = getImsServiceController(slotId, feature); 762 763 if (controller != null) { 764 controller.addImsServiceFeatureCallback(callback); 765 return controller; 766 } 767 return null; 768 } 769 770 /** 771 * Unregister a previously registered IImsServiceFeatureCallback through 772 * {@link #getImsServiceControllerAndListen(int, int, IImsServiceFeatureCallback)} . 773 * @param slotId The slot id associated with the ImsFeature. 774 * @param feature The {@link ImsFeature.FeatureType} 775 * @param callback The callback to be unregistered. 776 */ unregisterImsFeatureCallback(int slotId, int feature, IImsServiceFeatureCallback callback)777 public void unregisterImsFeatureCallback(int slotId, int feature, 778 IImsServiceFeatureCallback callback) { 779 ImsServiceController controller = getImsServiceController(slotId, feature); 780 781 if (controller != null) { 782 controller.removeImsServiceFeatureCallback(callback); 783 } 784 } 785 786 // Used for testing only. overrideImsServiceConfiguration(int slotId, boolean isCarrierService, Map<Integer, String> featureConfig)787 public boolean overrideImsServiceConfiguration(int slotId, boolean isCarrierService, 788 Map<Integer, String> featureConfig) { 789 if (slotId < 0 || slotId >= mNumSlots) { 790 Log.w(TAG, "overrideImsServiceConfiguration: invalid slotId!"); 791 return false; 792 } 793 794 OverrideConfig overrideConfig = new OverrideConfig(slotId, isCarrierService, featureConfig); 795 Message.obtain(mHandler, HANDLER_OVERRIDE_IMS_SERVICE_CONFIG, overrideConfig) 796 .sendToTarget(); 797 return true; 798 } 799 800 // not synchronized, access through handler ONLY. getDeviceConfiguration(@msFeature.FeatureType int featureType)801 private String getDeviceConfiguration(@ImsFeature.FeatureType int featureType) { 802 return mDeviceServices.getOrDefault(featureType, ""); 803 } 804 805 // not synchronized, access in handler ONLY. setDeviceConfiguration(String name, @ImsFeature.FeatureType int featureType)806 private void setDeviceConfiguration(String name, @ImsFeature.FeatureType int featureType) { 807 mDeviceServices.put(featureType, name); 808 } 809 810 // not synchronized, access in handler ONLY. setCarrierConfiguredPackageName(@onNull String packageName, int slotId, @ImsFeature.FeatureType int featureType)811 private void setCarrierConfiguredPackageName(@NonNull String packageName, int slotId, 812 @ImsFeature.FeatureType int featureType) { 813 getCarrierConfiguredPackageNames(slotId).put(featureType, packageName); 814 } 815 816 // not synchronized, access in handler ONLY. getCarrierConfiguredPackageName(int slotId, @ImsFeature.FeatureType int featureType)817 private @NonNull String getCarrierConfiguredPackageName(int slotId, 818 @ImsFeature.FeatureType int featureType) { 819 return getCarrierConfiguredPackageNames(slotId).getOrDefault(featureType, ""); 820 } 821 822 // not synchronized, access in handler ONLY. getCarrierConfiguredPackageNames(int slotId)823 private @NonNull Map<Integer, String> getCarrierConfiguredPackageNames(int slotId) { 824 Map<Integer, String> carrierConfig = mCarrierServices.get(slotId); 825 if (carrierConfig == null) { 826 carrierConfig = new ArrayMap<>(); 827 mCarrierServices.put(slotId, carrierConfig); 828 } 829 return carrierConfig; 830 } 831 832 // not synchronized, access in handler ONLY. setOverridePackageName(@ullable String packageName, int slotId, @ImsFeature.FeatureType int featureType)833 private void setOverridePackageName(@Nullable String packageName, int slotId, 834 @ImsFeature.FeatureType int featureType) { 835 getOverridePackageName(slotId).put(featureType, packageName); 836 } 837 838 // not synchronized, access in handler ONLY. getOverridePackageName(int slotId, @ImsFeature.FeatureType int featureType)839 private @Nullable String getOverridePackageName(int slotId, 840 @ImsFeature.FeatureType int featureType) { 841 return getOverridePackageName(slotId).get(featureType); 842 } 843 844 // not synchronized, access in handler ONLY. getOverridePackageName(int slotId)845 private @NonNull SparseArray<String> getOverridePackageName(int slotId) { 846 SparseArray<String> carrierConfig = mOverrideServices.get(slotId); 847 if (carrierConfig == null) { 848 carrierConfig = new SparseArray<>(); 849 mOverrideServices.put(slotId, carrierConfig); 850 } 851 return carrierConfig; 852 } 853 854 /** 855 * @return true if there is a carrier configuration that exists for the slot & featureType pair 856 * and the cached carrier ImsService associated with the configuration also supports the 857 * requested ImsFeature type. 858 */ 859 // not synchronized, access in handler ONLY. doesCarrierConfigurationExist(int slotId, @ImsFeature.FeatureType int featureType)860 private boolean doesCarrierConfigurationExist(int slotId, 861 @ImsFeature.FeatureType int featureType) { 862 String carrierPackage = getCarrierConfiguredPackageName(slotId, featureType); 863 if (TextUtils.isEmpty(carrierPackage)) { 864 return false; 865 } 866 // Config exists, but the carrier ImsService also needs to support this feature 867 ImsServiceInfo info = getImsServiceInfoFromCache(carrierPackage); 868 return info != null && info.getSupportedFeatures().stream().anyMatch( 869 feature -> feature.slotId == slotId && feature.featureType == featureType); 870 } 871 872 /** 873 * @return the package name of the ImsService with the requested configuration. 874 */ 875 // used in shell commands queries during testing only. getImsServiceConfiguration(int slotId, boolean isCarrierService, @ImsFeature.FeatureType int featureType)876 public String getImsServiceConfiguration(int slotId, boolean isCarrierService, 877 @ImsFeature.FeatureType int featureType) { 878 if (slotId < 0 || slotId >= mNumSlots) { 879 Log.w(TAG, "getImsServiceConfiguration: invalid slotId!"); 880 return ""; 881 } 882 883 LinkedBlockingQueue<String> result = new LinkedBlockingQueue<>(1); 884 // access the configuration on the handler. 885 mHandler.post(() -> result.offer(isCarrierService 886 ? getCarrierConfiguredPackageName(slotId, featureType) : 887 getDeviceConfiguration(featureType))); 888 try { 889 return result.poll(GET_IMS_SERVICE_TIMEOUT_MS, TimeUnit.MILLISECONDS); 890 } catch (InterruptedException e) { 891 Log.w(TAG, "getImsServiceConfiguration: exception=" + e.getMessage()); 892 return null; 893 } 894 } 895 putImsController(int slotId, int feature, ImsServiceController controller)896 private void putImsController(int slotId, int feature, ImsServiceController controller) { 897 if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.FEATURE_INVALID 898 || feature >= ImsFeature.FEATURE_MAX) { 899 Log.w(TAG, "putImsController received invalid parameters - slot: " + slotId 900 + ", feature: " + feature); 901 return; 902 } 903 synchronized (mBoundServicesLock) { 904 SparseArray<ImsServiceController> services = mBoundImsServicesByFeature.get(slotId); 905 if (services == null) { 906 services = new SparseArray<>(); 907 mBoundImsServicesByFeature.put(slotId, services); 908 } 909 mEventLog.log("putImsController - [" + slotId + ", " 910 + ImsFeature.FEATURE_LOG_MAP.get(feature) + "] -> " + controller); 911 Log.i(TAG, "ImsServiceController added on slot: " + slotId + " with feature: " 912 + ImsFeature.FEATURE_LOG_MAP.get(feature) + " using package: " 913 + controller.getComponentName()); 914 services.put(feature, controller); 915 } 916 } 917 removeImsController(int slotId, int feature)918 private ImsServiceController removeImsController(int slotId, int feature) { 919 if (slotId < 0 || feature <= ImsFeature.FEATURE_INVALID 920 || feature >= ImsFeature.FEATURE_MAX) { 921 Log.w(TAG, "removeImsController received invalid parameters - slot: " + slotId 922 + ", feature: " + feature); 923 return null; 924 } 925 synchronized (mBoundServicesLock) { 926 SparseArray<ImsServiceController> services = mBoundImsServicesByFeature.get(slotId); 927 if (services == null) { 928 return null; 929 } 930 ImsServiceController c = services.get(feature, null); 931 if (c != null) { 932 mEventLog.log("removeImsController - [" + slotId + ", " 933 + ImsFeature.FEATURE_LOG_MAP.get(feature) + "] -> " + c); 934 Log.i(TAG, "ImsServiceController removed on slot: " + slotId + " with feature: " 935 + ImsFeature.FEATURE_LOG_MAP.get(feature) + " using package: " 936 + c.getComponentName()); 937 services.remove(feature); 938 } 939 return c; 940 } 941 } 942 943 // Update the current cache with the new ImsService(s) if it has been added or update the 944 // supported IMS features if they have changed. 945 // Called from the handler ONLY maybeAddedImsService(String packageName)946 private void maybeAddedImsService(String packageName) { 947 Log.d(TAG, "maybeAddedImsService, packageName: " + packageName); 948 List<ImsServiceInfo> infos = getImsServiceInfo(packageName); 949 // Wait until all ImsServiceInfo is cached before calling 950 // calculateFeatureConfigurationChange to reduce churn. 951 boolean requiresCalculation = false; 952 for (ImsServiceInfo info : infos) { 953 // Checking to see if the ComponentName is the same, so we can update the supported 954 // features. Will only be one (if it exists), since it is a set. 955 ImsServiceInfo match = getInfoByComponentName(mInstalledServicesCache, info.name); 956 if (match != null) { 957 // for dynamic query the new "info" will have no supported features yet. Don't wipe 958 // out the cache for the existing features or update yet. Instead start a query 959 // for features dynamically. 960 if (info.featureFromMetadata) { 961 mEventLog.log("maybeAddedImsService - updating features for " + info.name 962 + ": " + printFeatures(match.getSupportedFeatures()) + " -> " 963 + printFeatures(info.getSupportedFeatures())); 964 Log.i(TAG, "Updating features in cached ImsService: " + info.name); 965 Log.d(TAG, "Updating features - Old features: " + match + " new features: " 966 + info); 967 // update features in the cache 968 match.replaceFeatures(info.getSupportedFeatures()); 969 requiresCalculation = true; 970 } else { 971 mEventLog.log("maybeAddedImsService - scheduling query for " + info); 972 // start a query to get ImsService features 973 scheduleQueryForFeatures(info); 974 } 975 } else { 976 Log.i(TAG, "Adding newly added ImsService to cache: " + info.name); 977 mEventLog.log("maybeAddedImsService - adding new ImsService: " + info); 978 mInstalledServicesCache.put(info.name, info); 979 if (info.featureFromMetadata) { 980 requiresCalculation = true; 981 } else { 982 // newly added ImsServiceInfo that has not had features queried yet. Start async 983 // bind and query features. 984 scheduleQueryForFeatures(info); 985 } 986 } 987 } 988 if (requiresCalculation) calculateFeatureConfigurationChange(); 989 } 990 991 // Remove the ImsService from the cache. This may have been due to the ImsService being removed 992 // from the device or was returning permanent errors when bound. 993 // Called from the handler ONLY maybeRemovedImsService(String packageName)994 private boolean maybeRemovedImsService(String packageName) { 995 ImsServiceInfo match = getInfoByPackageName(mInstalledServicesCache, packageName); 996 if (match != null) { 997 mInstalledServicesCache.remove(match.name); 998 mEventLog.log("maybeRemovedImsService - removing ImsService: " + match); 999 Log.i(TAG, "Removing ImsService: " + match.name); 1000 unbindImsService(match); 1001 calculateFeatureConfigurationChange(); 1002 return true; 1003 } 1004 return false; 1005 } 1006 isDeviceService(ImsServiceInfo info)1007 private boolean isDeviceService(ImsServiceInfo info) { 1008 if (info == null) return false; 1009 return mDeviceServices.containsValue(info.name.getPackageName()); 1010 } 1011 getSlotsForActiveCarrierService(ImsServiceInfo info)1012 private List<Integer> getSlotsForActiveCarrierService(ImsServiceInfo info) { 1013 if (info == null) return Collections.emptyList(); 1014 List<Integer> slots = new ArrayList<>(mNumSlots); 1015 for (int i = 0; i < mNumSlots; i++) { 1016 if (!TextUtils.isEmpty(getCarrierConfiguredPackageNames(i).values().stream() 1017 .filter(e -> e.equals(info.name.getPackageName())).findAny().orElse(""))) { 1018 slots.add(i); 1019 } 1020 } 1021 return slots; 1022 } 1023 getControllerByServiceInfo( Map<ComponentName, ImsServiceController> searchMap, ImsServiceInfo matchValue)1024 private ImsServiceController getControllerByServiceInfo( 1025 Map<ComponentName, ImsServiceController> searchMap, ImsServiceInfo matchValue) { 1026 return searchMap.values().stream() 1027 .filter(c -> Objects.equals(c.getComponentName(), matchValue.name)) 1028 .findFirst().orElse(null); 1029 } 1030 getInfoByPackageName(Map<ComponentName, ImsServiceInfo> searchMap, String matchValue)1031 private ImsServiceInfo getInfoByPackageName(Map<ComponentName, ImsServiceInfo> searchMap, 1032 String matchValue) { 1033 return searchMap.values().stream() 1034 .filter((i) -> Objects.equals(i.name.getPackageName(), matchValue)) 1035 .findFirst().orElse(null); 1036 } 1037 getInfoByComponentName( Map<ComponentName, ImsServiceInfo> searchMap, ComponentName matchValue)1038 private ImsServiceInfo getInfoByComponentName( 1039 Map<ComponentName, ImsServiceInfo> searchMap, ComponentName matchValue) { 1040 return searchMap.get(matchValue); 1041 } 1042 bindImsServiceWithFeatures(ImsServiceInfo info, Set<ImsFeatureConfiguration.FeatureSlotPair> features)1043 private void bindImsServiceWithFeatures(ImsServiceInfo info, 1044 Set<ImsFeatureConfiguration.FeatureSlotPair> features) { 1045 // Only bind if there are features that will be created by the service. 1046 if (shouldFeaturesCauseBind(features)) { 1047 // Check to see if an active controller already exists 1048 ImsServiceController controller = getControllerByServiceInfo(mActiveControllers, info); 1049 if (controller != null) { 1050 Log.i(TAG, "ImsService connection exists for " + info.name + ", updating features " 1051 + features); 1052 try { 1053 controller.changeImsServiceFeatures(features); 1054 // Features have been set, there was an error adding/removing. When the 1055 // controller recovers, it will add/remove again. 1056 } catch (RemoteException e) { 1057 Log.w(TAG, "bindImsService: error=" + e.getMessage()); 1058 } 1059 } else { 1060 controller = info.controllerFactory.create(mContext, info.name, this); 1061 Log.i(TAG, "Binding ImsService: " + controller.getComponentName() 1062 + " with features: " + features); 1063 controller.bind(features); 1064 mEventLog.log("bindImsServiceWithFeatures - create new controller: " 1065 + controller); 1066 } 1067 mActiveControllers.put(info.name, controller); 1068 } 1069 } 1070 1071 // Clean up and unbind from an ImsService unbindImsService(ImsServiceInfo info)1072 private void unbindImsService(ImsServiceInfo info) { 1073 if (info == null) { 1074 return; 1075 } 1076 ImsServiceController controller = getControllerByServiceInfo(mActiveControllers, info); 1077 if (controller != null) { 1078 // Calls imsServiceFeatureRemoved on all features in the controller 1079 try { 1080 Log.i(TAG, "Unbinding ImsService: " + controller.getComponentName()); 1081 mEventLog.log("unbindImsService - unbinding and removing " + controller); 1082 controller.unbind(); 1083 } catch (RemoteException e) { 1084 Log.e(TAG, "unbindImsService: Remote Exception: " + e.getMessage()); 1085 } 1086 mActiveControllers.remove(info.name); 1087 } 1088 } 1089 1090 // Calculate which features an ImsServiceController will need. If it is the carrier specific 1091 // ImsServiceController, it will be granted all of the features it requests on the associated 1092 // slot. If it is the device ImsService, it will get all of the features not covered by the 1093 // carrier implementation. calculateFeaturesToCreate( ImsServiceInfo info)1094 private HashSet<ImsFeatureConfiguration.FeatureSlotPair> calculateFeaturesToCreate( 1095 ImsServiceInfo info) { 1096 HashSet<ImsFeatureConfiguration.FeatureSlotPair> imsFeaturesBySlot = new HashSet<>(); 1097 List<Integer> slots = getSlotsForActiveCarrierService(info); 1098 if (!slots.isEmpty()) { 1099 // There is an active carrier config associated with this. Return with the ImsService's 1100 // supported features that are also within the carrier configuration 1101 imsFeaturesBySlot.addAll(info.getSupportedFeatures().stream() 1102 .filter(feature -> info.name.getPackageName().equals( 1103 getCarrierConfiguredPackageName(feature.slotId, feature.featureType))) 1104 .collect(Collectors.toList())); 1105 return imsFeaturesBySlot; 1106 } 1107 if (isDeviceService(info)) { 1108 imsFeaturesBySlot.addAll(info.getSupportedFeatures().stream() 1109 // only allow supported features that are also set for this package as the 1110 // device configuration. 1111 .filter(feature -> info.name.getPackageName().equals( 1112 getDeviceConfiguration(feature.featureType))) 1113 // filter out any separate carrier configuration, since that feature is handled 1114 // by the carrier ImsService. 1115 .filter(feature -> !doesCarrierConfigurationExist(feature.slotId, 1116 feature.featureType)) 1117 .collect(Collectors.toList())); 1118 } 1119 return imsFeaturesBySlot; 1120 } 1121 1122 /** 1123 * Implementation of 1124 * {@link ImsServiceController.ImsServiceControllerCallbacks#imsServiceFeatureCreated}, which 1125 * removes the ImsServiceController from the mBoundImsServicesByFeature structure. 1126 */ imsServiceFeatureCreated(int slotId, int feature, ImsServiceController controller)1127 public void imsServiceFeatureCreated(int slotId, int feature, ImsServiceController controller) { 1128 putImsController(slotId, feature, controller); 1129 } 1130 1131 /** 1132 * Implementation of 1133 * {@link ImsServiceController.ImsServiceControllerCallbacks#imsServiceFeatureRemoved}, which 1134 * removes the ImsServiceController from the mBoundImsServicesByFeature structure. 1135 */ imsServiceFeatureRemoved(int slotId, int feature, ImsServiceController controller)1136 public void imsServiceFeatureRemoved(int slotId, int feature, ImsServiceController controller) { 1137 removeImsController(slotId, feature); 1138 } 1139 1140 /** 1141 * Implementation of 1142 * {@link ImsServiceController.ImsServiceControllerCallbacks#imsServiceFeaturesChanged, which 1143 * notify the ImsResolver of a change to the supported ImsFeatures of a connected ImsService. 1144 */ imsServiceFeaturesChanged(ImsFeatureConfiguration config, ImsServiceController controller)1145 public void imsServiceFeaturesChanged(ImsFeatureConfiguration config, 1146 ImsServiceController controller) { 1147 if (controller == null || config == null) { 1148 return; 1149 } 1150 Log.i(TAG, "imsServiceFeaturesChanged: config=" + config.getServiceFeatures() 1151 + ", ComponentName=" + controller.getComponentName()); 1152 mEventLog.log("imsServiceFeaturesChanged - for " + controller + ", new config " 1153 + config.getServiceFeatures()); 1154 handleFeaturesChanged(controller.getComponentName(), config.getServiceFeatures()); 1155 } 1156 1157 @Override imsServiceBindPermanentError(ComponentName name)1158 public void imsServiceBindPermanentError(ComponentName name) { 1159 if (name == null) { 1160 return; 1161 } 1162 Log.w(TAG, "imsServiceBindPermanentError: component=" + name); 1163 mEventLog.log("imsServiceBindPermanentError - for " + name); 1164 mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE, name.getPackageName()).sendToTarget(); 1165 } 1166 1167 /** 1168 * Determines if the features specified should cause a bind or keep a binding active to an 1169 * ImsService. 1170 * @return true if MMTEL or RCS features are present, false if they are not or only 1171 * EMERGENCY_MMTEL is specified. 1172 */ shouldFeaturesCauseBind(Set<ImsFeatureConfiguration.FeatureSlotPair> features)1173 private boolean shouldFeaturesCauseBind(Set<ImsFeatureConfiguration.FeatureSlotPair> features) { 1174 long bindableFeatures = features.stream() 1175 // remove all emergency features 1176 .filter(f -> f.featureType != ImsFeature.FEATURE_EMERGENCY_MMTEL).count(); 1177 return bindableFeatures > 0; 1178 } 1179 1180 // Possibly rebind to another ImsService for testing carrier ImsServices. 1181 // Called from the handler ONLY overrideCarrierService(int slotId, Map<Integer, String> featureMap)1182 private void overrideCarrierService(int slotId, Map<Integer, String> featureMap) { 1183 for (Integer featureType : featureMap.keySet()) { 1184 String overridePackageName = featureMap.get(featureType); 1185 mEventLog.log("overriding carrier ImsService to " + overridePackageName 1186 + " on slot " + slotId + " for feature " 1187 + ImsFeature.FEATURE_LOG_MAP.getOrDefault(featureType, "invalid")); 1188 setOverridePackageName(overridePackageName, slotId, featureType); 1189 } 1190 updateBoundServices(slotId, Collections.emptyMap()); 1191 } 1192 1193 // Possibly rebind to another ImsService for testing carrier ImsServices. 1194 // Called from the handler ONLY overrideDeviceService(Map<Integer, String> featureMap)1195 private void overrideDeviceService(Map<Integer, String> featureMap) { 1196 boolean requiresRecalc = false; 1197 for (Integer featureType : featureMap.keySet()) { 1198 String overridePackageName = featureMap.get(featureType); 1199 mEventLog.log("overriding device ImsService to " + overridePackageName + " for feature " 1200 + ImsFeature.FEATURE_LOG_MAP.getOrDefault(featureType, "invalid")); 1201 String oldPackageName = getDeviceConfiguration(featureType); 1202 if (!TextUtils.equals(oldPackageName, overridePackageName)) { 1203 Log.i(TAG, "overrideDeviceService - device package changed (override): " 1204 + oldPackageName + " -> " + overridePackageName); 1205 mEventLog.log("overrideDeviceService - device package changed (override): " 1206 + oldPackageName + " -> " + overridePackageName); 1207 setDeviceConfiguration(overridePackageName, featureType); 1208 ImsServiceInfo info = getImsServiceInfoFromCache(overridePackageName); 1209 if (info == null || info.featureFromMetadata) { 1210 requiresRecalc = true; 1211 } else { 1212 // Config will change when this query completes 1213 scheduleQueryForFeatures(info); 1214 } 1215 } 1216 } 1217 if (requiresRecalc) calculateFeatureConfigurationChange(); 1218 } 1219 1220 // Called from handler ONLY. carrierConfigChanged(int slotId)1221 private void carrierConfigChanged(int slotId) { 1222 updateBoundDeviceServices(); 1223 1224 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) { 1225 // not specified, update carrier override cache and possibly rebind on all slots. 1226 for (int i = 0; i < mNumSlots; i++) { 1227 updateBoundServices(i, getImsPackageOverrideConfig(i)); 1228 } 1229 } 1230 updateBoundServices(slotId, getImsPackageOverrideConfig(slotId)); 1231 } 1232 updateBoundDeviceServices()1233 private void updateBoundDeviceServices() { 1234 Log.d(TAG, "updateBoundDeviceServices: called"); 1235 ArrayMap<String, ImsServiceInfo> featureDynamicImsPackages = new ArrayMap<>(); 1236 for (int f = ImsFeature.FEATURE_EMERGENCY_MMTEL; f < ImsFeature.FEATURE_MAX; f++) { 1237 String packageName = getDeviceConfiguration(f); 1238 ImsServiceInfo serviceInfo = getImsServiceInfoFromCache(packageName); 1239 if (serviceInfo != null && !serviceInfo.featureFromMetadata 1240 && !featureDynamicImsPackages.containsKey(packageName)) { 1241 featureDynamicImsPackages.put(packageName, serviceInfo); 1242 1243 Log.d(TAG, "updateBoundDeviceServices: Schedule query for package=" + packageName); 1244 scheduleQueryForFeatures(featureDynamicImsPackages.get(packageName)); 1245 } 1246 } 1247 } 1248 updateBoundServices(int slotId, Map<Integer, String> featureMap)1249 private void updateBoundServices(int slotId, Map<Integer, String> featureMap) { 1250 if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlots) { 1251 return; 1252 } 1253 boolean hasConfigChanged = false; 1254 boolean didQuerySchedule = false; 1255 for (int f = ImsFeature.FEATURE_EMERGENCY_MMTEL; f < ImsFeature.FEATURE_MAX; f++) { 1256 String overridePackageName = getOverridePackageName(slotId, f); 1257 String oldPackageName = getCarrierConfiguredPackageName(slotId, f); 1258 String newPackageName = featureMap.getOrDefault(f, ""); 1259 if (!TextUtils.isEmpty(overridePackageName)) { 1260 // Do not allow carrier config changes to change the override package while it 1261 // is in effect. 1262 Log.i(TAG, String.format("updateBoundServices: overriding %s with %s for feature" 1263 + " %s on slot %d", 1264 TextUtils.isEmpty(newPackageName) ? "(none)" : newPackageName, 1265 overridePackageName, 1266 ImsFeature.FEATURE_LOG_MAP.getOrDefault(f, "invalid"), slotId)); 1267 newPackageName = overridePackageName; 1268 } 1269 1270 setCarrierConfiguredPackageName(newPackageName, slotId, f); 1271 // Carrier config may have not changed, but we still want to kick off a recalculation 1272 // in case there has been a change to the supported device features. 1273 ImsServiceInfo info = getImsServiceInfoFromCache(newPackageName); 1274 mEventLog.log("updateBoundServices - carrier package changed: " 1275 + oldPackageName + " -> " + newPackageName + " on slot " + slotId 1276 + ", hasConfigChanged=" + hasConfigChanged); 1277 if (info == null || info.featureFromMetadata) { 1278 hasConfigChanged = true; 1279 } else { 1280 // Config will change when this query completes 1281 scheduleQueryForFeatures(info); 1282 didQuerySchedule = true; 1283 } 1284 } 1285 if (hasConfigChanged) calculateFeatureConfigurationChange(); 1286 1287 if (hasConfigChanged && didQuerySchedule) { 1288 mEventLog.log("[warning] updateBoundServices - both hasConfigChange and query " 1289 + "scheduled on slot " + slotId); 1290 } 1291 } 1292 getImsPackageOverrideConfig(int slotId)1293 private @NonNull Map<Integer, String> getImsPackageOverrideConfig(int slotId) { 1294 int subId = mSubscriptionManagerProxy.getSubId(slotId); 1295 PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId); 1296 if (config == null) return Collections.emptyMap(); 1297 String packageNameMmTel = config.getString( 1298 CarrierConfigManager.KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, null); 1299 // Set the config equal for the deprecated key. 1300 String packageNameRcs = packageNameMmTel; 1301 packageNameMmTel = config.getString( 1302 CarrierConfigManager.KEY_CONFIG_IMS_MMTEL_PACKAGE_OVERRIDE_STRING, 1303 packageNameMmTel); 1304 packageNameRcs = config.getString( 1305 CarrierConfigManager.KEY_CONFIG_IMS_RCS_PACKAGE_OVERRIDE_STRING, packageNameRcs); 1306 Map<Integer, String> result = new ArrayMap<>(); 1307 if (!TextUtils.isEmpty(packageNameMmTel)) { 1308 result.put(ImsFeature.FEATURE_EMERGENCY_MMTEL, packageNameMmTel); 1309 result.put(ImsFeature.FEATURE_MMTEL, packageNameMmTel); 1310 } 1311 if (!TextUtils.isEmpty(packageNameRcs)) { 1312 result.put(ImsFeature.FEATURE_RCS, packageNameRcs); 1313 } 1314 return result; 1315 } 1316 1317 /** 1318 * Schedules a query for dynamic ImsService features. 1319 */ scheduleQueryForFeatures(ImsServiceInfo service, int delayMs)1320 private void scheduleQueryForFeatures(ImsServiceInfo service, int delayMs) { 1321 if (service == null) { 1322 return; 1323 } 1324 Message msg = Message.obtain(mHandler, HANDLER_START_DYNAMIC_FEATURE_QUERY, service); 1325 if (mHandler.hasMessages(HANDLER_START_DYNAMIC_FEATURE_QUERY, service)) { 1326 Log.d(TAG, "scheduleQueryForFeatures: dynamic query for " + service.name 1327 + " already scheduled"); 1328 return; 1329 } 1330 Log.d(TAG, "scheduleQueryForFeatures: starting dynamic query for " + service.name 1331 + " in " + delayMs + "ms."); 1332 mHandler.sendMessageDelayed(msg, delayMs); 1333 } 1334 scheduleQueryForFeatures(ComponentName name, int delayMs)1335 private void scheduleQueryForFeatures(ComponentName name, int delayMs) { 1336 ImsServiceInfo service = getImsServiceInfoFromCache(name.getPackageName()); 1337 if (service == null) { 1338 Log.w(TAG, "scheduleQueryForFeatures: Couldn't find cached info for name: " + name); 1339 return; 1340 } 1341 scheduleQueryForFeatures(service, delayMs); 1342 } 1343 scheduleQueryForFeatures(ImsServiceInfo service)1344 private void scheduleQueryForFeatures(ImsServiceInfo service) { 1345 scheduleQueryForFeatures(service, 0); 1346 } 1347 1348 /** 1349 * Schedules the processing of a completed query. 1350 */ handleFeaturesChanged(ComponentName name, Set<ImsFeatureConfiguration.FeatureSlotPair> features)1351 private void handleFeaturesChanged(ComponentName name, 1352 Set<ImsFeatureConfiguration.FeatureSlotPair> features) { 1353 SomeArgs args = SomeArgs.obtain(); 1354 args.arg1 = name; 1355 args.arg2 = features; 1356 mHandler.obtainMessage(HANDLER_DYNAMIC_FEATURE_CHANGE, args).sendToTarget(); 1357 } 1358 handleMsimConfigChange(Integer newNumSlots)1359 private void handleMsimConfigChange(Integer newNumSlots) { 1360 int oldLen = mNumSlots; 1361 if (oldLen == newNumSlots) { 1362 return; 1363 } 1364 mNumSlots = newNumSlots; 1365 Log.i(TAG, "handleMsimConfigChange: oldLen=" + oldLen + ", newLen=" + newNumSlots); 1366 mEventLog.log("MSIM config change: " + oldLen + " -> " + newNumSlots); 1367 if (newNumSlots < oldLen) { 1368 // we need to trim data structures that use slots, however mBoundImsServicesByFeature 1369 // will be updated by ImsServiceController changing to remove features on old slots. 1370 // start at the index of the new highest slot + 1. 1371 for (int oldSlot = newNumSlots; oldSlot < oldLen; oldSlot++) { 1372 // First clear old carrier configs 1373 Map<Integer, String> carrierConfigs = getCarrierConfiguredPackageNames(oldSlot); 1374 for (Integer feature : carrierConfigs.keySet()) { 1375 setCarrierConfiguredPackageName("", oldSlot, feature); 1376 } 1377 // next clear old overrides 1378 SparseArray<String> overrideConfigs = getOverridePackageName(oldSlot); 1379 for (int i = 0; i < overrideConfigs.size(); i++) { 1380 int feature = overrideConfigs.keyAt(i); 1381 setOverridePackageName("", oldSlot, feature); 1382 } 1383 } 1384 } 1385 // Get the new config for each ImsService. For manifest queries, this will update the 1386 // number of slots. 1387 // This will get all services with the correct intent filter from PackageManager 1388 List<ImsServiceInfo> infos = getImsServiceInfo(null); 1389 for (ImsServiceInfo info : infos) { 1390 ImsServiceInfo cachedInfo = mInstalledServicesCache.get(info.name); 1391 if (cachedInfo != null) { 1392 if (info.featureFromMetadata) { 1393 cachedInfo.replaceFeatures(info.getSupportedFeatures()); 1394 } else { 1395 // Remove features that are no longer supported by the device configuration. 1396 cachedInfo.getSupportedFeatures() 1397 .removeIf(filter -> filter.slotId >= newNumSlots); 1398 } 1399 } else { 1400 // This is unexpected, put the new service on the queue to be added 1401 mEventLog.log("handleMsimConfigChange: detected untracked service - " + info); 1402 Log.w(TAG, "handleMsimConfigChange: detected untracked package, queueing to add " 1403 + info); 1404 mHandler.obtainMessage(HANDLER_ADD_PACKAGE, 1405 info.name.getPackageName()).sendToTarget(); 1406 } 1407 } 1408 1409 if (newNumSlots < oldLen) { 1410 // A CarrierConfigChange will happen for the new slot, so only recalculate if there are 1411 // less new slots because we need to remove the old capabilities. 1412 calculateFeatureConfigurationChange(); 1413 } 1414 } 1415 1416 // Starts a dynamic query. Called from handler ONLY. startDynamicQuery(ImsServiceInfo service)1417 private void startDynamicQuery(ImsServiceInfo service) { 1418 // if not current device/carrier service, don't perform query. If this changes, this method 1419 // will be called again. 1420 if (!isDeviceService(service) && getSlotsForActiveCarrierService(service).isEmpty()) { 1421 Log.i(TAG, "scheduleQueryForFeatures: skipping query for ImsService that is not" 1422 + " set as carrier/device ImsService."); 1423 return; 1424 } 1425 mEventLog.log("startDynamicQuery - starting query for " + service); 1426 boolean queryStarted = mFeatureQueryManager.startQuery(service.name, 1427 service.controllerFactory.getServiceInterface()); 1428 if (!queryStarted) { 1429 Log.w(TAG, "startDynamicQuery: service could not connect. Retrying after delay."); 1430 mEventLog.log("startDynamicQuery - query failed. Retrying in " 1431 + DELAY_DYNAMIC_QUERY_MS + " mS"); 1432 scheduleQueryForFeatures(service, DELAY_DYNAMIC_QUERY_MS); 1433 } else { 1434 Log.d(TAG, "startDynamicQuery: Service queried, waiting for response."); 1435 } 1436 } 1437 1438 // process complete dynamic query. Called from handler ONLY. dynamicQueryComplete(ComponentName name, Set<ImsFeatureConfiguration.FeatureSlotPair> features)1439 private void dynamicQueryComplete(ComponentName name, 1440 Set<ImsFeatureConfiguration.FeatureSlotPair> features) { 1441 ImsServiceInfo service = getImsServiceInfoFromCache(name.getPackageName()); 1442 if (service == null) { 1443 Log.w(TAG, "dynamicQueryComplete: Couldn't find cached info for name: " 1444 + name); 1445 return; 1446 } 1447 mEventLog.log("dynamicQueryComplete: for package " + name + ", features: " 1448 + printFeatures(service.getSupportedFeatures()) + " -> " + printFeatures(features)); 1449 sanitizeFeatureConfig(features); 1450 // Add features to service 1451 service.replaceFeatures(features); 1452 // Wait until all queries have completed before changing the configuration to reduce churn. 1453 if (!mFeatureQueryManager.isQueryInProgress()) { 1454 if (mHandler.hasMessages(HANDLER_DYNAMIC_FEATURE_CHANGE)) { 1455 mEventLog.log("[warning] dynamicQueryComplete - HANDLER_DYNAMIC_FEATURE_CHANGE " 1456 + "pending with calculateFeatureConfigurationChange()"); 1457 } 1458 calculateFeatureConfigurationChange(); 1459 } 1460 } 1461 1462 /** 1463 * Ensure the feature includes MMTEL when it supports EMERGENCY_MMTEL, if not, remove. 1464 */ sanitizeFeatureConfig(Set<ImsFeatureConfiguration.FeatureSlotPair> features)1465 private void sanitizeFeatureConfig(Set<ImsFeatureConfiguration.FeatureSlotPair> features) { 1466 Set<ImsFeatureConfiguration.FeatureSlotPair> emergencyMmtelFeatures = features.stream() 1467 .filter(feature -> feature.featureType == ImsFeature.FEATURE_EMERGENCY_MMTEL) 1468 .collect(Collectors.toSet()); 1469 for (ImsFeatureConfiguration.FeatureSlotPair feature : emergencyMmtelFeatures) { 1470 if (!features.contains(new ImsFeatureConfiguration.FeatureSlotPair(feature.slotId, 1471 ImsFeature.FEATURE_MMTEL))) { 1472 features.remove(feature); 1473 } 1474 } 1475 } 1476 1477 // Calculate the new configuration for the bound ImsServices. 1478 // Should ONLY be called from the handler. calculateFeatureConfigurationChange()1479 private void calculateFeatureConfigurationChange() { 1480 for (ImsServiceInfo info : mInstalledServicesCache.values()) { 1481 Set<ImsFeatureConfiguration.FeatureSlotPair> features = calculateFeaturesToCreate(info); 1482 if (shouldFeaturesCauseBind(features)) { 1483 bindImsServiceWithFeatures(info, features); 1484 } else { 1485 unbindImsService(info); 1486 } 1487 } 1488 } 1489 printFeatures(Set<ImsFeatureConfiguration.FeatureSlotPair> features)1490 private static String printFeatures(Set<ImsFeatureConfiguration.FeatureSlotPair> features) { 1491 StringBuilder featureString = new StringBuilder(); 1492 featureString.append(" features: ["); 1493 if (features != null) { 1494 for (ImsFeatureConfiguration.FeatureSlotPair feature : features) { 1495 featureString.append("{"); 1496 featureString.append(feature.slotId); 1497 featureString.append(","); 1498 featureString.append(ImsFeature.FEATURE_LOG_MAP.get(feature.featureType)); 1499 featureString.append("}"); 1500 } 1501 featureString.append("]"); 1502 } 1503 return featureString.toString(); 1504 } 1505 1506 /** 1507 * Returns the ImsServiceInfo that matches the provided packageName. Visible for testing 1508 * the ImsService caching functionality. 1509 */ 1510 @VisibleForTesting getImsServiceInfoFromCache(String packageName)1511 public ImsServiceInfo getImsServiceInfoFromCache(String packageName) { 1512 if (TextUtils.isEmpty(packageName)) { 1513 return null; 1514 } 1515 ImsServiceInfo infoFilter = getInfoByPackageName(mInstalledServicesCache, packageName); 1516 if (infoFilter != null) { 1517 return infoFilter; 1518 } else { 1519 return null; 1520 } 1521 } 1522 1523 // Return the ImsServiceInfo specified for the package name. If the package name is null, 1524 // get all packages that support ImsServices. getImsServiceInfo(String packageName)1525 private List<ImsServiceInfo> getImsServiceInfo(String packageName) { 1526 List<ImsServiceInfo> infos = new ArrayList<>(); 1527 // Search for Current ImsService implementations 1528 infos.addAll(searchForImsServices(packageName, mImsServiceControllerFactory)); 1529 // Search for compat ImsService Implementations 1530 infos.addAll(searchForImsServices(packageName, mImsServiceControllerFactoryCompat)); 1531 return infos; 1532 } 1533 searchForImsServices(String packageName, ImsServiceControllerFactory controllerFactory)1534 private List<ImsServiceInfo> searchForImsServices(String packageName, 1535 ImsServiceControllerFactory controllerFactory) { 1536 List<ImsServiceInfo> infos = new ArrayList<>(); 1537 1538 Intent serviceIntent = new Intent(controllerFactory.getServiceInterface()); 1539 serviceIntent.setPackage(packageName); 1540 1541 PackageManager packageManager = mContext.getPackageManager(); 1542 for (ResolveInfo entry : packageManager.queryIntentServicesAsUser( 1543 serviceIntent, 1544 PackageManager.GET_META_DATA, 1545 UserHandle.getUserHandleForUid(UserHandle.myUserId()))) { 1546 ServiceInfo serviceInfo = entry.serviceInfo; 1547 1548 if (serviceInfo != null) { 1549 ImsServiceInfo info = new ImsServiceInfo(); 1550 info.name = new ComponentName(serviceInfo.packageName, serviceInfo.name); 1551 info.controllerFactory = controllerFactory; 1552 1553 // we will allow the manifest method of declaring manifest features in two cases: 1554 // 1) it is the device overlay "default" ImsService, where the features do not 1555 // change (the new method can still be used if the default does not define manifest 1556 // entries). 1557 // 2) using the "compat" ImsService, which only supports manifest query. 1558 if (isDeviceService(info) 1559 || mImsServiceControllerFactoryCompat == controllerFactory) { 1560 if (serviceInfo.metaData != null) { 1561 if (serviceInfo.metaData.getBoolean(METADATA_MMTEL_FEATURE, false)) { 1562 info.addFeatureForAllSlots(mNumSlots, ImsFeature.FEATURE_MMTEL); 1563 // only allow FEATURE_EMERGENCY_MMTEL if FEATURE_MMTEL is defined. 1564 if (serviceInfo.metaData.getBoolean(METADATA_EMERGENCY_MMTEL_FEATURE, 1565 false)) { 1566 info.addFeatureForAllSlots(mNumSlots, 1567 ImsFeature.FEATURE_EMERGENCY_MMTEL); 1568 } 1569 } 1570 if (serviceInfo.metaData.getBoolean(METADATA_RCS_FEATURE, false)) { 1571 info.addFeatureForAllSlots(mNumSlots, ImsFeature.FEATURE_RCS); 1572 } 1573 } 1574 // Only dynamic query if we are not a compat version of ImsService and the 1575 // default service. 1576 if (mImsServiceControllerFactoryCompat != controllerFactory 1577 && info.getSupportedFeatures().isEmpty()) { 1578 // metadata empty, try dynamic query instead 1579 info.featureFromMetadata = false; 1580 } 1581 } else { 1582 // We are a carrier service and not using the compat version of ImsService. 1583 info.featureFromMetadata = false; 1584 } 1585 Log.i(TAG, "service name: " + info.name + ", manifest query: " 1586 + info.featureFromMetadata); 1587 // Check manifest permission to be sure that the service declares the correct 1588 // permissions. Overridden if the METADATA_OVERRIDE_PERM_CHECK metadata is set to 1589 // true. 1590 // NOTE: METADATA_OVERRIDE_PERM_CHECK should only be set for testing. 1591 if (TextUtils.equals(serviceInfo.permission, Manifest.permission.BIND_IMS_SERVICE) 1592 || serviceInfo.metaData.getBoolean(METADATA_OVERRIDE_PERM_CHECK, false)) { 1593 infos.add(info); 1594 } else { 1595 Log.w(TAG, "ImsService is not protected with BIND_IMS_SERVICE permission: " 1596 + info.name); 1597 } 1598 } 1599 } 1600 return infos; 1601 } 1602 1603 // Dump is called on the main thread, since ImsResolver Handler is also handled on main thread, 1604 // we shouldn't need to worry about concurrent access of private params. dump(FileDescriptor fd, PrintWriter printWriter, String[] args)1605 public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) { 1606 IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); 1607 pw.println("ImsResolver:"); 1608 pw.increaseIndent(); 1609 pw.println("Configurations:"); 1610 pw.increaseIndent(); 1611 pw.println("Device:"); 1612 pw.increaseIndent(); 1613 for (Integer i : mDeviceServices.keySet()) { 1614 pw.println(ImsFeature.FEATURE_LOG_MAP.get(i) + " -> " + mDeviceServices.get(i)); 1615 } 1616 pw.decreaseIndent(); 1617 pw.println("Carrier: "); 1618 pw.increaseIndent(); 1619 for (int i = 0; i < mNumSlots; i++) { 1620 for (int j = 0; j < MmTelFeature.FEATURE_MAX; j++) { 1621 pw.print("slot="); 1622 pw.print(i); 1623 pw.print(", feature="); 1624 pw.print(ImsFeature.FEATURE_LOG_MAP.getOrDefault(j, "?")); 1625 pw.println(": "); 1626 pw.increaseIndent(); 1627 String name = getCarrierConfiguredPackageName(i, j); 1628 pw.println(TextUtils.isEmpty(name) ? "none" : name); 1629 pw.decreaseIndent(); 1630 } 1631 } 1632 pw.decreaseIndent(); 1633 pw.decreaseIndent(); 1634 pw.println("Bound Features:"); 1635 pw.increaseIndent(); 1636 for (int i = 0; i < mNumSlots; i++) { 1637 for (int j = 0; j < MmTelFeature.FEATURE_MAX; j++) { 1638 pw.print("slot="); 1639 pw.print(i); 1640 pw.print(", feature="); 1641 pw.print(ImsFeature.FEATURE_LOG_MAP.getOrDefault(j, "?")); 1642 pw.println(": "); 1643 pw.increaseIndent(); 1644 ImsServiceController c = getImsServiceController(i, j); 1645 pw.println(c == null ? "none" : c); 1646 pw.decreaseIndent(); 1647 } 1648 } 1649 pw.decreaseIndent(); 1650 pw.println("Cached ImsServices:"); 1651 pw.increaseIndent(); 1652 for (ImsServiceInfo i : mInstalledServicesCache.values()) { 1653 pw.println(i); 1654 } 1655 pw.decreaseIndent(); 1656 pw.println("Active controllers:"); 1657 pw.increaseIndent(); 1658 for (ImsServiceController c : mActiveControllers.values()) { 1659 pw.println(c); 1660 pw.increaseIndent(); 1661 c.dump(pw); 1662 pw.decreaseIndent(); 1663 } 1664 pw.decreaseIndent(); 1665 pw.println("Event Log:"); 1666 pw.increaseIndent(); 1667 mEventLog.dump(pw); 1668 pw.decreaseIndent(); 1669 } 1670 } 1671