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 android.telephony.ims; 18 19 import android.annotation.FlaggedApi; 20 import android.annotation.LongDef; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.SuppressLint; 24 import android.annotation.SystemApi; 25 import android.app.Service; 26 import android.content.Intent; 27 import android.os.IBinder; 28 import android.os.RemoteException; 29 import android.telephony.CarrierConfigManager; 30 import android.telephony.ims.aidl.IImsConfig; 31 import android.telephony.ims.aidl.IImsMmTelFeature; 32 import android.telephony.ims.aidl.IImsRcsFeature; 33 import android.telephony.ims.aidl.IImsRegistration; 34 import android.telephony.ims.aidl.IImsServiceController; 35 import android.telephony.ims.aidl.IImsServiceControllerListener; 36 import android.telephony.ims.aidl.ISipTransport; 37 import android.telephony.ims.feature.ImsFeature; 38 import android.telephony.ims.feature.MmTelFeature; 39 import android.telephony.ims.feature.RcsFeature; 40 import android.telephony.ims.stub.ImsConfigImplBase; 41 import android.telephony.ims.stub.ImsFeatureConfiguration; 42 import android.telephony.ims.stub.ImsRegistrationImplBase; 43 import android.telephony.ims.stub.SipTransportImplBase; 44 import android.util.Log; 45 import android.util.SparseArray; 46 import android.util.SparseBooleanArray; 47 48 import com.android.ims.internal.IImsFeatureStatusCallback; 49 import com.android.internal.annotations.VisibleForTesting; 50 import com.android.internal.telephony.flags.Flags; 51 import com.android.internal.telephony.util.TelephonyUtils; 52 53 import java.lang.annotation.Retention; 54 import java.lang.annotation.RetentionPolicy; 55 import java.util.Map; 56 import java.util.NoSuchElementException; 57 import java.util.concurrent.CancellationException; 58 import java.util.concurrent.CompletableFuture; 59 import java.util.concurrent.CompletionException; 60 import java.util.concurrent.ExecutionException; 61 import java.util.concurrent.Executor; 62 import java.util.function.Supplier; 63 64 /** 65 * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend 66 * ImsService must register the service in their AndroidManifest to be detected by the framework. 67 * First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE" 68 * permission. Then, the ImsService definition in the manifest must follow the following format: 69 * 70 * ... 71 * <service android:name=".EgImsService" 72 * android:permission="android.permission.BIND_IMS_SERVICE" > 73 * ... 74 * <intent-filter> 75 * <action android:name="android.telephony.ims.ImsService" /> 76 * </intent-filter> 77 * </service> 78 * ... 79 * 80 * The telephony framework will then bind to the ImsService you have defined in your manifest 81 * if you are either: 82 * 1) Defined as the default ImsService for the device in the device overlay using 83 * "config_ims_mmtel_package" or "config_ims_rcs_package". 84 * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using 85 * {@link CarrierConfigManager#KEY_CONFIG_IMS_MMTEL_PACKAGE_OVERRIDE_STRING} or 86 * {@link CarrierConfigManager#KEY_CONFIG_IMS_RCS_PACKAGE_OVERRIDE_STRING}. 87 * 88 * There are two ways to define to the platform which {@link ImsFeature}s this {@link ImsService} 89 * supports, dynamic or static definitions. 90 * 91 * In the static definition, the {@link ImsFeature}s that are supported are defined in the service 92 * definition of the AndroidManifest.xml file as metadata: 93 * <!-- Apps must declare which features they support as metadata. The different categories are 94 * defined below. In this example, the MMTEL_FEATURE feature is supported. --> 95 * <meta-data android:name="android.telephony.ims.MMTEL_FEATURE" android:value="true" /> 96 * 97 * The features that are currently supported in an ImsService are: 98 * - RCS_FEATURE: This ImsService implements the RcsFeature class. 99 * - MMTEL_FEATURE: This ImsService implements the MmTelFeature class. 100 * - EMERGENCY_MMTEL_FEATURE: This ImsService supports Emergency Calling for MMTEL, must be 101 * declared along with the MMTEL_FEATURE. If this is not specified, the framework will use 102 * circuit switch for emergency calling. 103 * 104 * In the dynamic definition, the supported features are not specified in the service definition 105 * of the AndroidManifest. Instead, the framework binds to this service and calls 106 * {@link #querySupportedImsFeatures()}. The {@link ImsService} then returns an 107 * {@link ImsFeatureConfiguration}, which the framework uses to initialize the supported 108 * {@link ImsFeature}s. If at any time, the list of supported {@link ImsFeature}s changes, 109 * {@link #onUpdateSupportedImsFeatures(ImsFeatureConfiguration)} can be called to tell the 110 * framework of the changes. 111 * 112 * @hide 113 */ 114 @SystemApi 115 public class ImsService extends Service { 116 117 private static final String LOG_TAG = "ImsService"; 118 119 /** 120 * This ImsService supports the capability to place emergency calls over MMTEL. 121 * <p> 122 * Note: This should never be set by {@link #getImsServiceCapabilities()}, as whether it is 123 * there or not depends on whether or not {@link ImsFeature#FEATURE_EMERGENCY_MMTEL} is defined 124 * for this ImsService. If it is set, it will be removed during sanitization before the final 125 * capabilities bitfield is sent back to the framework. 126 * @hide This is encoded into the {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, but we will be 127 * adding other capabilities in a central location, so track this capability here as well. 128 */ 129 public static final long CAPABILITY_EMERGENCY_OVER_MMTEL = 1 << 0; 130 131 /** 132 * This ImsService supports the capability to create SIP delegates for other IMS applications 133 * to use to proxy SIP messaging traffic through it. 134 * <p> 135 * In order for the framework to report SipDelegate creation as being available for this 136 * ImsService implementation, this ImsService must report this capability flag in 137 * {@link #getImsServiceCapabilities()}, {@link #getSipTransport(int)} must not return null, and 138 * this ImsService MUST report the ability to create both {@link ImsFeature#FEATURE_MMTEL} and 139 * {@link ImsFeature#FEATURE_RCS} features. 140 */ 141 public static final long CAPABILITY_SIP_DELEGATE_CREATION = 1 << 1; 142 143 /** 144 * This ImsService supports the terminal based call waiting service. 145 * <p> 146 * In order for the IMS service to support the service, IMS service shall 147 * override {@link MmTelFeature#setTerminalBasedCallWaitingStatus}. 148 * If ImsService has this capability, Android platform will handle the synchronization 149 * between the network based call waiting service over circuit-switched networks and the 150 * terminal based call waiting service of IMS service, and will handle the received 151 * circuit-switched waiting calls. Otherwise, this functionality of Android platform shall 152 * be disabled. 153 */ 154 public static final long CAPABILITY_TERMINAL_BASED_CALL_WAITING = 1 << 2; 155 156 /** 157 * This ImsService supports the capability to manage calls on multiple subscriptions at the same 158 * time. 159 * <p> 160 * When set, this ImsService supports managing calls on multiple subscriptions at the same time 161 * for all WLAN network configurations. Telephony will allow new outgoing/incoming IMS calls to 162 * be set up on other subscriptions while there is an ongoing call. The ImsService must also 163 * support managing calls on WWAN + WWAN configurations whenever the modem also reports 164 * simultaneous calling availability, which can be listened to using the 165 * {@link android.telephony.TelephonyCallback.SimultaneousCellularCallingSupportListener} API. 166 * Telephony will only allow additional ongoing/incoming IMS calls on another subscription to be 167 * set up on WWAN + WWAN configurations when the modem reports that simultaneous cellular 168 * calling is allowed at the current time on both subscriptions where there are ongoing calls. 169 * <p> 170 * When unset (default), this ImsService can not support calls on multiple subscriptions at the 171 * same time for any WLAN or WWAN configurations, so pending outgoing call placed on another 172 * cellular subscription while there is an ongoing call will be cancelled by Telephony. 173 * Similarly, any incoming call notification on another cellular subscription while there is an 174 * ongoing call will be rejected. 175 * @hide TODO: move this to system API when we have a backing implementation + CTS testing 176 */ 177 @FlaggedApi(Flags.FLAG_SIMULTANEOUS_CALLING_INDICATIONS) 178 public static final long CAPABILITY_SUPPORTS_SIMULTANEOUS_CALLING = 1 << 3; 179 180 /** 181 * Used for internal correctness checks of capabilities set by the ImsService implementation and 182 * tracks the index of the largest defined flag in the capabilities long. 183 * @hide 184 */ 185 public static final long CAPABILITY_MAX_INDEX = 186 Long.numberOfTrailingZeros(CAPABILITY_SUPPORTS_SIMULTANEOUS_CALLING); 187 188 /** 189 * @hide 190 */ 191 @LongDef(flag = true, 192 prefix = "CAPABILITY_", 193 value = { 194 // CAPABILITY_EMERGENCY_OVER_MMTEL is not included here because it is managed by 195 // whether or not ImsFeature.FEATURE_EMERGENCY_MMTEL feature is set and should 196 // not be set by users of ImsService. 197 CAPABILITY_SIP_DELEGATE_CREATION, 198 CAPABILITY_TERMINAL_BASED_CALL_WAITING, 199 CAPABILITY_SUPPORTS_SIMULTANEOUS_CALLING 200 }) 201 @Retention(RetentionPolicy.SOURCE) 202 public @interface ImsServiceCapability {} 203 204 /** 205 * Used for logging purposes, see {@link #getCapabilitiesString(long)} 206 * @hide 207 */ 208 private static final Map<Long, String> CAPABILITIES_LOG_MAP = Map.of( 209 CAPABILITY_EMERGENCY_OVER_MMTEL, "EMERGENCY_OVER_MMTEL", 210 CAPABILITY_SIP_DELEGATE_CREATION, "SIP_DELEGATE_CREATION", 211 CAPABILITY_TERMINAL_BASED_CALL_WAITING, "TERMINAL_BASED_CALL_WAITING", 212 CAPABILITY_SUPPORTS_SIMULTANEOUS_CALLING, "SIMULTANEOUS_CALLING"); 213 214 /** 215 * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService. 216 * @hide 217 */ 218 public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService"; 219 220 // A map of slot Id -> map of features (indexed by ImsFeature feature id) corresponding to that 221 // slot. 222 // We keep track of this to facilitate cleanup of the IImsFeatureStatusCallback and 223 // call ImsFeature#onFeatureRemoved. 224 private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>(); 225 226 // A map of slot id -> boolean array, where each entry in the boolean array corresponds to an 227 // ImsFeature that was created for a slot id and not a sub id for backwards compatibility 228 // purposes. 229 private final SparseArray<SparseBooleanArray> mCreateImsFeatureWithSlotIdFlagMap = 230 new SparseArray<>(); 231 232 private IImsServiceControllerListener mListener; 233 private final Object mListenerLock = new Object(); 234 private final Object mExecutorLock = new Object(); 235 private Executor mExecutor; 236 237 /** 238 * Create a new ImsService. 239 * <p> 240 * Method stubs called from the framework will be called asynchronously. Vendor specifies the 241 * {@link Executor} that the methods stubs will be called. If mExecutor is set to null by 242 * vendor use Runnable::run. 243 */ ImsService()244 public ImsService() { 245 } 246 247 /** 248 * Listener that notifies the framework of ImsService changes. 249 * @hide 250 */ 251 public static class Listener extends IImsServiceControllerListener.Stub { 252 /** 253 * The IMS features that this ImsService supports has changed. 254 * @param c a new {@link ImsFeatureConfiguration} containing {@link ImsFeature.FeatureType}s 255 * that this ImsService supports. This may trigger the addition/removal of feature 256 * in this service. 257 */ onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)258 public void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) { 259 } 260 } 261 262 /** 263 * @hide 264 */ 265 protected final IBinder mImsServiceController = new IImsServiceController.Stub() { 266 @Override 267 public void setListener(IImsServiceControllerListener l) { 268 synchronized (mListenerLock) { 269 if (mListener != null && mListener.asBinder().isBinderAlive()) { 270 try { 271 mListener.asBinder().unlinkToDeath(mDeathRecipient, 0); 272 } catch (NoSuchElementException e) { 273 Log.w(LOG_TAG, "IImsServiceControllerListener does not exist"); 274 } 275 } 276 277 mListener = l; 278 if (mListener == null) { 279 executeMethodAsync(() -> releaseResource(), "releaseResource"); 280 return; 281 } 282 283 try { 284 mListener.asBinder().linkToDeath(mDeathRecipient, 0); 285 Log.i(LOG_TAG, "setListener: register linkToDeath"); 286 } catch (RemoteException e) { 287 // RemoteException means target binder process was crashed 288 // release resource 289 executeMethodAsync(() -> releaseResource(), "releaseResource"); 290 } 291 } 292 } 293 294 @Override 295 public IImsMmTelFeature createMmTelFeature(int slotId, int subId) { 296 MmTelFeature f = (MmTelFeature) getImsFeature(slotId, ImsFeature.FEATURE_MMTEL); 297 if (f == null) { 298 return executeMethodAsyncForResult(() -> createMmTelFeatureInternal(slotId, subId), 299 "createMmTelFeature"); 300 } else { 301 return f.getBinder(); 302 } 303 } 304 305 @Override 306 public IImsMmTelFeature createEmergencyOnlyMmTelFeature(int slotId) { 307 MmTelFeature f = (MmTelFeature) getImsFeature(slotId, ImsFeature.FEATURE_MMTEL); 308 if (f == null) { 309 return executeMethodAsyncForResult(() -> createEmergencyOnlyMmTelFeatureInternal( 310 slotId), "createEmergencyOnlyMmTelFeature"); 311 } else { 312 return f.getBinder(); 313 } 314 } 315 316 @Override 317 public IImsRcsFeature createRcsFeature(int slotId, int subId) { 318 RcsFeature f = (RcsFeature) getImsFeature(slotId, ImsFeature.FEATURE_RCS); 319 if (f == null) { 320 return executeMethodAsyncForResult(() -> 321 createRcsFeatureInternal(slotId, subId), "createRcsFeature"); 322 } else { 323 return f.getBinder(); 324 } 325 } 326 327 @Override 328 public void addFeatureStatusCallback(int slotId, int featureType, 329 IImsFeatureStatusCallback c) { 330 executeMethodAsync(() -> ImsService.this.addImsFeatureStatusCallback( 331 slotId, featureType, c), "addFeatureStatusCallback"); 332 } 333 334 @Override 335 public void removeFeatureStatusCallback(int slotId, int featureType, 336 IImsFeatureStatusCallback c) { 337 executeMethodAsync(() -> ImsService.this.removeImsFeatureStatusCallback( 338 slotId, featureType, c), "removeFeatureStatusCallback"); 339 } 340 341 @Override 342 public void removeImsFeature(int slotId, int featureType, boolean changeSubId) { 343 if (changeSubId && isImsFeatureCreatedForSlot(slotId, featureType)) { 344 Log.w(LOG_TAG, "Do not remove Ims feature for compatibility"); 345 return; 346 } 347 executeMethodAsync(() -> ImsService.this.removeImsFeature(slotId, featureType), 348 "removeImsFeature"); 349 setImsFeatureCreatedForSlot(slotId, featureType, false); 350 } 351 352 @Override 353 public ImsFeatureConfiguration querySupportedImsFeatures() { 354 return executeMethodAsyncForResult(() -> ImsService.this.querySupportedImsFeatures(), 355 "ImsFeatureConfiguration"); 356 } 357 358 @Override 359 public long getImsServiceCapabilities() { 360 return executeMethodAsyncForResult(() -> { 361 long caps = ImsService.this.getImsServiceCapabilities(); 362 long sanitizedCaps = sanitizeCapabilities(caps); 363 if (caps != sanitizedCaps) { 364 Log.w(LOG_TAG, "removing invalid bits from field: 0x" 365 + Long.toHexString(caps ^ sanitizedCaps)); 366 } 367 return sanitizedCaps; 368 }, "getImsServiceCapabilities"); 369 } 370 371 @Override 372 public void notifyImsServiceReadyForFeatureCreation() { 373 executeMethodAsync(() -> ImsService.this.readyForFeatureCreation(), 374 "notifyImsServiceReadyForFeatureCreation"); 375 } 376 377 @Override 378 public IImsConfig getConfig(int slotId, int subId) { 379 return executeMethodAsyncForResult(() -> { 380 ImsConfigImplBase c = 381 ImsService.this.getConfigForSubscription(slotId, subId); 382 if (c != null) { 383 c.setDefaultExecutor(getCachedExecutor()); 384 return c.getIImsConfig(); 385 } else { 386 return null; 387 } 388 }, "getConfig"); 389 } 390 391 @Override 392 public IImsRegistration getRegistration(int slotId, int subId) { 393 return executeMethodAsyncForResult(() -> { 394 ImsRegistrationImplBase r = 395 ImsService.this.getRegistrationForSubscription(slotId, subId); 396 if (r != null) { 397 r.setDefaultExecutor(getCachedExecutor()); 398 return r.getBinder(); 399 } else { 400 return null; 401 } 402 }, "getRegistration"); 403 } 404 405 @Override 406 public ISipTransport getSipTransport(int slotId) { 407 return executeMethodAsyncForResult(() -> { 408 SipTransportImplBase s = ImsService.this.getSipTransport(slotId); 409 if (s != null) { 410 s.setDefaultExecutor(getCachedExecutor()); 411 return s.getBinder(); 412 } else { 413 return null; 414 } 415 }, "getSipTransport"); 416 } 417 418 @Override 419 public void enableIms(int slotId, int subId) { 420 executeMethodAsync(() -> 421 ImsService.this.enableImsForSubscription(slotId, subId), "enableIms"); 422 } 423 424 @Override 425 public void disableIms(int slotId, int subId) { 426 executeMethodAsync(() -> 427 ImsService.this.disableImsForSubscription(slotId, subId), "disableIms"); 428 } 429 430 @Override 431 public void resetIms(int slotId, int subId) { 432 executeMethodAsync(() -> 433 ImsService.this.resetImsInternal(slotId, subId), "resetIms"); 434 } 435 }; 436 437 private final IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { 438 @Override 439 public void binderDied() { 440 Log.w(LOG_TAG, 441 "IImsServiceControllerListener binder to framework has died. Cleaning up"); 442 executeMethodAsync(() -> releaseResource(), "releaseResource"); 443 } 444 }; 445 446 /** 447 * @hide 448 */ 449 @Override onBind(Intent intent)450 public IBinder onBind(Intent intent) { 451 if(SERVICE_INTERFACE.equals(intent.getAction())) { 452 Log.i(LOG_TAG, "ImsService Bound."); 453 return mImsServiceController; 454 } 455 return null; 456 } 457 getCachedExecutor()458 private Executor getCachedExecutor() { 459 synchronized (mExecutorLock) { 460 if (mExecutor == null) { 461 Executor e = ImsService.this.getExecutor(); 462 mExecutor = (e != null) ? e : Runnable::run; 463 } 464 return mExecutor; 465 } 466 } 467 createMmTelFeatureInternal(int slotId, int subscriptionId)468 private IImsMmTelFeature createMmTelFeatureInternal(int slotId, int subscriptionId) { 469 MmTelFeature f = createMmTelFeatureForSubscription(slotId, subscriptionId); 470 if (f != null) { 471 setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL); 472 f.setDefaultExecutor(getCachedExecutor()); 473 return f.getBinder(); 474 } else { 475 Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned."); 476 return null; 477 } 478 } 479 createEmergencyOnlyMmTelFeatureInternal(int slotId)480 private IImsMmTelFeature createEmergencyOnlyMmTelFeatureInternal(int slotId) { 481 MmTelFeature f = createEmergencyOnlyMmTelFeature(slotId); 482 if (f != null) { 483 setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL); 484 f.setDefaultExecutor(getCachedExecutor()); 485 return f.getBinder(); 486 } else { 487 Log.e(LOG_TAG, "createEmergencyOnlyMmTelFeatureInternal: null feature returned."); 488 return null; 489 } 490 } 491 createRcsFeatureInternal(int slotId, int subId)492 private IImsRcsFeature createRcsFeatureInternal(int slotId, int subId) { 493 RcsFeature f = createRcsFeatureForSubscription(slotId, subId); 494 if (f != null) { 495 f.setDefaultExecutor(getCachedExecutor()); 496 setupFeature(f, slotId, ImsFeature.FEATURE_RCS); 497 return f.getBinder(); 498 } else { 499 Log.e(LOG_TAG, "createRcsFeatureInternal: null feature returned."); 500 return null; 501 } 502 } 503 setupFeature(ImsFeature f, int slotId, int featureType)504 private void setupFeature(ImsFeature f, int slotId, int featureType) { 505 f.initialize(this, slotId); 506 addImsFeature(slotId, featureType, f); 507 } 508 addImsFeatureStatusCallback(int slotId, int featureType, IImsFeatureStatusCallback c)509 private void addImsFeatureStatusCallback(int slotId, int featureType, 510 IImsFeatureStatusCallback c) { 511 synchronized (mFeaturesBySlot) { 512 // get ImsFeature associated with the slot/feature 513 SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); 514 if (features == null) { 515 Log.w(LOG_TAG, "Can not add ImsFeatureStatusCallback - no features on slot " 516 + slotId); 517 return; 518 } 519 ImsFeature f = features.get(featureType); 520 if (f != null) { 521 f.addImsFeatureStatusCallback(c); 522 } 523 } 524 } 525 removeImsFeatureStatusCallback(int slotId, int featureType, IImsFeatureStatusCallback c)526 private void removeImsFeatureStatusCallback(int slotId, int featureType, 527 IImsFeatureStatusCallback c) { 528 synchronized (mFeaturesBySlot) { 529 // get ImsFeature associated with the slot/feature 530 SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); 531 if (features == null) { 532 Log.w(LOG_TAG, "Can not remove ImsFeatureStatusCallback - no features on slot " 533 + slotId); 534 return; 535 } 536 ImsFeature f = features.get(featureType); 537 if (f != null) { 538 f.removeImsFeatureStatusCallback(c); 539 } 540 } 541 } 542 addImsFeature(int slotId, int featureType, ImsFeature f)543 private void addImsFeature(int slotId, int featureType, ImsFeature f) { 544 synchronized (mFeaturesBySlot) { 545 // Get SparseArray for Features, by querying slot Id 546 SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); 547 if (features == null) { 548 // Populate new SparseArray of features if it doesn't exist for this slot yet. 549 features = new SparseArray<>(); 550 mFeaturesBySlot.put(slotId, features); 551 } 552 features.put(featureType, f); 553 } 554 } 555 removeImsFeature(int slotId, int featureType)556 private void removeImsFeature(int slotId, int featureType) { 557 // clear cached data 558 notifySubscriptionRemoved(slotId); 559 560 synchronized (mFeaturesBySlot) { 561 // get ImsFeature associated with the slot/feature 562 SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); 563 if (features == null) { 564 Log.w(LOG_TAG, "Can not remove ImsFeature. No ImsFeatures exist on slot " 565 + slotId); 566 return; 567 } 568 ImsFeature f = features.get(featureType); 569 if (f == null) { 570 Log.w(LOG_TAG, "Can not remove ImsFeature. No feature with type " 571 + featureType + " exists on slot " + slotId); 572 return; 573 } 574 f.onFeatureRemoved(); 575 features.remove(featureType); 576 } 577 } 578 579 /** 580 * @hide 581 */ 582 @VisibleForTesting getImsFeature(int slotId, int featureType)583 public ImsFeature getImsFeature(int slotId, int featureType) { 584 synchronized (mFeaturesBySlot) { 585 // Get SparseArray for Features, by querying slot Id 586 SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); 587 if (features == null) { 588 return null; 589 } 590 return features.get(featureType); 591 } 592 } 593 setImsFeatureCreatedForSlot(int slotId, @ImsFeature.FeatureType int featureType, boolean createdForSlot)594 private void setImsFeatureCreatedForSlot(int slotId, 595 @ImsFeature.FeatureType int featureType, boolean createdForSlot) { 596 synchronized (mCreateImsFeatureWithSlotIdFlagMap) { 597 getImsFeatureCreatedForSlot(slotId).put(featureType, createdForSlot); 598 } 599 } 600 601 /** 602 * @hide 603 */ 604 @VisibleForTesting isImsFeatureCreatedForSlot(int slotId, @ImsFeature.FeatureType int featureType)605 public boolean isImsFeatureCreatedForSlot(int slotId, 606 @ImsFeature.FeatureType int featureType) { 607 synchronized (mCreateImsFeatureWithSlotIdFlagMap) { 608 return getImsFeatureCreatedForSlot(slotId).get(featureType); 609 } 610 } 611 getImsFeatureCreatedForSlot(int slotId)612 private SparseBooleanArray getImsFeatureCreatedForSlot(int slotId) { 613 SparseBooleanArray createFlag = mCreateImsFeatureWithSlotIdFlagMap.get(slotId); 614 if (createFlag == null) { 615 createFlag = new SparseBooleanArray(); 616 mCreateImsFeatureWithSlotIdFlagMap.put(slotId, createFlag); 617 } 618 return createFlag; 619 } 620 releaseResource()621 private void releaseResource() { 622 Log.w(LOG_TAG, "cleaning up features"); 623 synchronized (mFeaturesBySlot) { 624 SparseArray<ImsFeature> features; 625 ImsFeature imsFeature; 626 627 for (int i = 0; i < mFeaturesBySlot.size(); i++) { 628 features = mFeaturesBySlot.valueAt(i); 629 if (features == null) { 630 continue; 631 } 632 633 for (int index = 0; index < features.size(); index++) { 634 imsFeature = features.valueAt(index); 635 if (imsFeature != null) { 636 imsFeature.onFeatureRemoved(); 637 } 638 } 639 features.clear(); 640 } 641 mFeaturesBySlot.clear(); 642 } 643 } 644 645 // Call the methods with a clean calling identity on the executor and wait indefinitely for 646 // the future to return. executeMethodAsync(Runnable r, String errorLogName)647 private void executeMethodAsync(Runnable r, String errorLogName) { 648 try { 649 CompletableFuture.runAsync( 650 () -> TelephonyUtils.runWithCleanCallingIdentity(r), 651 getCachedExecutor()).join(); 652 } catch (CancellationException | CompletionException e) { 653 Log.w(LOG_TAG, "ImsService Binder - " + errorLogName + " exception: " 654 + e.getMessage()); 655 } 656 } 657 executeMethodAsyncForResult(Supplier<T> r, String errorLogName)658 private <T> T executeMethodAsyncForResult(Supplier<T> r, String errorLogName) { 659 CompletableFuture<T> future = CompletableFuture.supplyAsync( 660 () -> TelephonyUtils.runWithCleanCallingIdentity(r), getCachedExecutor()); 661 try { 662 return future.get(); 663 } catch (ExecutionException | InterruptedException e) { 664 Log.w(LOG_TAG, "ImsService Binder - " + errorLogName + " exception: " 665 + e.getMessage()); 666 return null; 667 } 668 } 669 resetImsInternal(int slotId, int subId)670 private void resetImsInternal(int slotId, int subId) { 671 try { 672 resetIms(slotId); 673 } catch (UnsupportedOperationException e) { 674 disableImsForSubscription(slotId, subId); 675 } 676 } 677 678 /** 679 * When called, provide the {@link ImsFeatureConfiguration} that this {@link ImsService} 680 * currently supports. This will trigger the framework to set up the {@link ImsFeature}s that 681 * correspond to the {@link ImsFeature}s configured here. 682 * 683 * Use {@link #onUpdateSupportedImsFeatures(ImsFeatureConfiguration)} to change the supported 684 * {@link ImsFeature}s. 685 * 686 * @return an {@link ImsFeatureConfiguration} containing Features this ImsService supports. 687 */ querySupportedImsFeatures()688 public ImsFeatureConfiguration querySupportedImsFeatures() { 689 // Return empty for base implementation 690 return new ImsFeatureConfiguration(); 691 } 692 693 /** 694 * Updates the framework with a new {@link ImsFeatureConfiguration} containing the updated 695 * features, that this {@link ImsService} supports. This may trigger the framework to add/remove 696 * new ImsFeatures, depending on the configuration. 697 */ onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)698 public final void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) 699 throws RemoteException { 700 IImsServiceControllerListener l; 701 synchronized (mListenerLock) { 702 if (mListener == null) { 703 throw new IllegalStateException("Framework is not ready"); 704 } 705 l = mListener; 706 } 707 l.onUpdateSupportedImsFeatures(c); 708 } 709 710 /** 711 * The optional capabilities that this ImsService supports. 712 * <p> 713 * This should be a static configuration and should not change at runtime. 714 * @return The optional static capabilities of this ImsService implementation. 715 */ 716 // ImsService follows a different convention, since it is a stub class. The on* methods are 717 // final and call back into the framework with a state update. 718 @SuppressLint("OnNameExpected") getImsServiceCapabilities()719 public @ImsServiceCapability long getImsServiceCapabilities() { 720 // Stub implementation to be implemented by ImsService. 721 return 0L; 722 } 723 724 /** 725 * The ImsService has been bound and is ready for ImsFeature creation based on the Features that 726 * the ImsService has registered for with the framework, either in the manifest or via 727 * {@link #querySupportedImsFeatures()}. 728 * 729 * The ImsService should use this signal instead of onCreate/onBind or similar to perform 730 * feature initialization because the framework may bind to this service multiple times to 731 * query the ImsService's {@link ImsFeatureConfiguration} via 732 * {@link #querySupportedImsFeatures()}before creating features. 733 */ readyForFeatureCreation()734 public void readyForFeatureCreation() { 735 } 736 737 /** 738 * The framework has enabled IMS for the subscription specified, the ImsService should register 739 * for IMS and perform all appropriate initialization to bring up all ImsFeatures. 740 * 741 * @param slotId The slot ID that IMS will be enabled for. 742 * @param subscriptionId The subscription ID that IMS will be enabled for. 743 */ enableImsForSubscription(int slotId, int subscriptionId)744 public void enableImsForSubscription(int slotId, int subscriptionId) { 745 enableIms(slotId); 746 } 747 748 /** 749 * The framework has disabled IMS for the subscription specified. The ImsService must deregister 750 * for IMS and set capability status to false for all ImsFeatures. 751 * @param slotId The slot ID that IMS will be disabled for. 752 * @param subscriptionId The subscription ID that IMS will be disabled for. 753 */ disableImsForSubscription(int slotId, int subscriptionId)754 public void disableImsForSubscription(int slotId, int subscriptionId) { 755 disableIms(slotId); 756 } 757 758 /** 759 * The subscription has removed. The ImsService should notify ImsRegistrationImplBase and 760 * ImsConfigImplBase the SIM state was changed. 761 * @param slotId The slot ID which has removed. 762 */ notifySubscriptionRemoved(int slotId)763 private void notifySubscriptionRemoved(int slotId) { 764 ImsRegistrationImplBase registrationImplBase = 765 getRegistration(slotId); 766 if (registrationImplBase != null) { 767 registrationImplBase.clearRegistrationCache(); 768 } 769 770 ImsConfigImplBase imsConfigImplBase = getConfig(slotId); 771 if (imsConfigImplBase != null) { 772 imsConfigImplBase.clearConfigurationCache(); 773 } 774 } 775 776 /** 777 * The framework has enabled IMS for the slot specified, the ImsService should register for IMS 778 * and perform all appropriate initialization to bring up all ImsFeatures. 779 * @deprecated Use {@link #enableImsForSubscription} instead. 780 */ 781 @Deprecated enableIms(int slotId)782 public void enableIms(int slotId) { 783 } 784 785 /** 786 * The framework has disabled IMS for the slot specified. The ImsService must deregister for IMS 787 * and set capability status to false for all ImsFeatures. 788 * @deprecated Use {@link #disableImsForSubscription} instead. 789 */ 790 @Deprecated disableIms(int slotId)791 public void disableIms(int slotId) { 792 } 793 794 /** 795 * The framework has reset IMS for the slot specified. The ImsService must deregister 796 * and release all resources for IMS. After resetIms is called, either 797 * {@link #enableImsForSubscription(int, int)} or {@link #disableImsForSubscription(int, int)} 798 * will be called for the same slotId. 799 * 800 * @param slotId The slot ID that IMS will be reset for. 801 * @hide 802 */ resetIms(int slotId)803 public void resetIms(int slotId) { 804 throw new UnsupportedOperationException(); 805 } 806 807 /** 808 * When called, the framework is requesting that a new {@link MmTelFeature} is created for the 809 * specified subscription. 810 * 811 * @param subscriptionId The subscription ID that the MMTEL Feature is being created for. 812 * @return The newly created {@link MmTelFeature} associated with the subscription or null if 813 * the feature is not supported. 814 */ createMmTelFeatureForSubscription(int slotId, int subscriptionId)815 public @Nullable MmTelFeature createMmTelFeatureForSubscription(int slotId, 816 int subscriptionId) { 817 setImsFeatureCreatedForSlot(slotId, ImsFeature.FEATURE_MMTEL, true); 818 return createMmTelFeature(slotId); 819 } 820 821 /** 822 * When called, the framework is requesting that a new {@link RcsFeature} is created for the 823 * specified subscription. 824 * 825 * @param subscriptionId The subscription ID that the RCS Feature is being created for. 826 * @return The newly created {@link RcsFeature} associated with the subscription or null if the 827 * feature is not supported. 828 */ createRcsFeatureForSubscription(int slotId, int subscriptionId)829 public @Nullable RcsFeature createRcsFeatureForSubscription(int slotId, int subscriptionId) { 830 setImsFeatureCreatedForSlot(slotId, ImsFeature.FEATURE_RCS, true); 831 return createRcsFeature(slotId); 832 } 833 834 /** 835 * When called, the framework is requesting that a new emergency-only {@link MmTelFeature} is 836 * created for the specified slot. For emergency calls, there is no known Subscription Id. 837 * 838 * @param slotId The slot ID that the MMTEL Feature is being created for. 839 * @return An MmTelFeature instance to be used for the slot ID when there is not 840 * subscription inserted. Only requested when there is no subscription active on 841 * the specified slot. 842 */ createEmergencyOnlyMmTelFeature(int slotId)843 public @Nullable MmTelFeature createEmergencyOnlyMmTelFeature(int slotId) { 844 setImsFeatureCreatedForSlot(slotId, ImsFeature.FEATURE_MMTEL, true); 845 return createMmTelFeature(slotId); 846 } 847 848 /** 849 * When called, the framework is requesting that a new {@link MmTelFeature} is created for the 850 * specified slot. 851 * @deprecated Use {@link #createMmTelFeatureForSubscription} instead 852 * 853 * @param slotId The slot ID that the MMTEL Feature is being created for. 854 * @return The newly created {@link MmTelFeature} associated with the slot or null if the 855 * feature is not supported. 856 */ 857 @Deprecated createMmTelFeature(int slotId)858 public MmTelFeature createMmTelFeature(int slotId) { 859 return null; 860 } 861 862 /** 863 * When called, the framework is requesting that a new {@link RcsFeature} is created for the 864 * specified slot. 865 * @deprecated Use {@link #createRcsFeatureForSubscription} instead 866 * 867 * @param slotId The slot ID that the RCS Feature is being created for. 868 * @return The newly created {@link RcsFeature} associated with the slot or null if the feature 869 * is not supported. 870 */ 871 @Deprecated createRcsFeature(int slotId)872 public RcsFeature createRcsFeature(int slotId) { 873 return null; 874 } 875 876 /** 877 * Return the {@link ImsConfigImplBase} implementation associated with the provided 878 * subscription. This will be used by the platform to get/set specific IMS related 879 * configurations. 880 * 881 * @param subscriptionId The subscription ID that the IMS configuration is associated with. 882 * @return ImsConfig implementation that is associated with the specified subscription. 883 */ getConfigForSubscription(int slotId, int subscriptionId)884 public @NonNull ImsConfigImplBase getConfigForSubscription(int slotId, int subscriptionId) { 885 return getConfig(slotId); 886 } 887 888 /** 889 * Return the {@link ImsRegistrationImplBase} implementation associated with the provided 890 * subscription. 891 * 892 * @param subscriptionId The subscription ID that is associated with the IMS Registration. 893 * @return the ImsRegistration implementation associated with the subscription. 894 */ getRegistrationForSubscription(int slotId, int subscriptionId)895 public @NonNull ImsRegistrationImplBase getRegistrationForSubscription(int slotId, 896 int subscriptionId) { 897 return getRegistration(slotId); 898 } 899 900 /** 901 * Return the {@link ImsConfigImplBase} implementation associated with the provided slot. This 902 * will be used by the platform to get/set specific IMS related configurations. 903 * @deprecated use {@link #getConfigForSubscription} instead. 904 * 905 * @param slotId The slot that the IMS configuration is associated with. 906 * @return ImsConfig implementation that is associated with the specified slot. 907 */ 908 @Deprecated getConfig(int slotId)909 public ImsConfigImplBase getConfig(int slotId) { 910 return new ImsConfigImplBase(); 911 } 912 913 /** 914 * Return the {@link ImsRegistrationImplBase} implementation associated with the provided slot. 915 * @deprecated use {@link #getRegistrationForSubscription} instead. 916 * 917 * @param slotId The slot that is associated with the IMS Registration. 918 * @return the ImsRegistration implementation associated with the slot. 919 */ 920 @Deprecated getRegistration(int slotId)921 public ImsRegistrationImplBase getRegistration(int slotId) { 922 return new ImsRegistrationImplBase(); 923 } 924 925 /** 926 * Return the {@link SipTransportImplBase} implementation associated with the provided slot. 927 * <p> 928 * This is an optional interface used for devices that must support IMS single registration and 929 * proxy SIP traffic to remote IMS applications. If this is not supported for this IMS service, 930 * this method should return {@code null}. If this feature is supported, then this method must 931 * never be {@code null} and the optional ImsService capability flag 932 * {@link #CAPABILITY_SIP_DELEGATE_CREATION} must be set in 933 * {@link #getImsServiceCapabilities()}. Otherwise the framework will assume this feature is not 934 * supported for this ImsService. 935 * @param slotId The slot that is associated with the SipTransport implementation. 936 * @return the SipTransport implementation for the specified slot. 937 */ 938 // ImsService follows a different convention, since it is a stub class. The on* methods are 939 // final and call back into the framework with a state update. 940 @SuppressLint("OnNameExpected") getSipTransport(int slotId)941 public @Nullable SipTransportImplBase getSipTransport(int slotId) { 942 // Stub implementation for ImsServices that do not support SipTransport. 943 return null; 944 } 945 sanitizeCapabilities(@msServiceCapability long caps)946 private static long sanitizeCapabilities(@ImsServiceCapability long caps) { 947 long filter = 0xFFFFFFFFFFFFFFFFL; 948 // pad the "allowed" set with zeros 949 filter <<= CAPABILITY_MAX_INDEX + 1; 950 // remove values above the allowed set. 951 caps &= ~filter; 952 // CAPABILITY_EMERGENCY_OVER_MMTEL should also not be set here, will be set by telephony 953 // internally. 954 caps &= ~CAPABILITY_EMERGENCY_OVER_MMTEL; 955 return caps; 956 } 957 958 /** 959 * @return A string representation of the ImsService capabilities for logging. 960 * @hide 961 */ getCapabilitiesString(@msServiceCapability long caps)962 public static String getCapabilitiesString(@ImsServiceCapability long caps) { 963 StringBuffer result = new StringBuffer(); 964 result.append("capabilities={ "); 965 // filter incrementally fills 0s from left to right. This is used to keep filtering out 966 // more bits in the long until the remaining leftmost bits are all zero. 967 long filter = 0xFFFFFFFFFFFFFFFFL; 968 // position of iterator to potentially print capability. 969 long i = 0; 970 while ((caps & filter) != 0 && i <= 63) { 971 long bitToCheck = (1L << i); 972 if ((caps & bitToCheck) != 0) { 973 result.append(CAPABILITIES_LOG_MAP.getOrDefault(bitToCheck, bitToCheck + "?")); 974 result.append(" "); 975 } 976 // shift left by one and fill in another 1 on the leftmost bit. 977 filter <<= 1; 978 i++; 979 } 980 result.append("}"); 981 return result.toString(); 982 } 983 984 /** 985 * The ImsService will now be able to define an Executor that the ImsService can be used to 986 * execute the methods. By default all ImsService level method calls will use this Executor. 987 * The ImsService has set the default executor as Runnable::run, 988 * Should be override or default executor will be used. 989 * @return an Executor used to execute methods called remotely by the framework. 990 */ getExecutor()991 public @NonNull Executor getExecutor() { 992 return Runnable::run; 993 } 994 } 995