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