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