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