1 /* 2 * Copyright (C) 2018 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.feature; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.SystemApi; 22 import android.annotation.TestApi; 23 import android.content.Context; 24 import android.os.IInterface; 25 import android.os.RemoteException; 26 import android.telephony.SubscriptionManager; 27 import android.telephony.ims.aidl.IImsCapabilityCallback; 28 import android.telephony.ims.stub.ImsRegistrationImplBase; 29 import android.util.Log; 30 31 import com.android.ims.internal.IImsFeatureStatusCallback; 32 import com.android.internal.annotations.VisibleForTesting; 33 import com.android.internal.telephony.util.RemoteCallbackListExt; 34 35 import java.lang.annotation.Retention; 36 import java.lang.annotation.RetentionPolicy; 37 import java.util.HashMap; 38 import java.util.Map; 39 40 /** 41 * Base class for all IMS features that are supported by the framework. Use a concrete subclass 42 * of {@link ImsFeature}, such as {@link MmTelFeature} or {@link RcsFeature}. 43 * 44 * @hide 45 */ 46 @SystemApi 47 public abstract class ImsFeature { 48 49 private static final String LOG_TAG = "ImsFeature"; 50 51 /** 52 * Invalid feature value 53 * @hide 54 */ 55 public static final int FEATURE_INVALID = -1; 56 // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously 57 // defined values in ImsServiceClass for compatibility purposes. 58 /** 59 * This feature supports emergency calling over MMTEL. If defined, the framework will try to 60 * place an emergency call over IMS first. If it is not defined, the framework will only use 61 * CSFB for emergency calling. 62 * @hide 63 */ 64 @SystemApi 65 public static final int FEATURE_EMERGENCY_MMTEL = 0; 66 /** 67 * This feature supports the MMTEL feature. 68 * @hide 69 */ 70 @SystemApi 71 public static final int FEATURE_MMTEL = 1; 72 /** 73 * This feature supports the RCS feature. 74 * @hide 75 */ 76 @SystemApi 77 public static final int FEATURE_RCS = 2; 78 /** 79 * Total number of features defined 80 * @hide 81 */ 82 public static final int FEATURE_MAX = 3; 83 84 /** 85 * Used for logging purposes. 86 * @hide 87 */ 88 public static final Map<Integer, String> FEATURE_LOG_MAP = new HashMap<Integer, String>() {{ 89 put(FEATURE_EMERGENCY_MMTEL, "EMERGENCY_MMTEL"); 90 put(FEATURE_MMTEL, "MMTEL"); 91 put(FEATURE_RCS, "RCS"); 92 }}; 93 94 /** 95 * Integer values defining IMS features that are supported in ImsFeature. 96 * @hide 97 */ 98 @IntDef(flag = true, 99 value = { 100 FEATURE_EMERGENCY_MMTEL, 101 FEATURE_MMTEL, 102 FEATURE_RCS 103 }) 104 @Retention(RetentionPolicy.SOURCE) 105 public @interface FeatureType {} 106 107 /** 108 * Integer values defining the state of the ImsFeature at any time. 109 * @hide 110 */ 111 @IntDef(flag = true, 112 value = { 113 STATE_UNAVAILABLE, 114 STATE_INITIALIZING, 115 STATE_READY, 116 }) 117 @Retention(RetentionPolicy.SOURCE) 118 public @interface ImsState {} 119 120 /** 121 * This {@link ImsFeature}'s state is unavailable and should not be communicated with. This will 122 * remove all bindings back to the framework. Any attempt to communicate with the framework 123 * during this time will result in an {@link IllegalStateException}. 124 * @hide 125 */ 126 @SystemApi 127 public static final int STATE_UNAVAILABLE = 0; 128 /** 129 * This {@link ImsFeature} state is initializing and should not be communicated with. This will 130 * remove all bindings back to the framework. Any attempt to communicate with the framework 131 * during this time will result in an {@link IllegalStateException}. 132 * @hide 133 */ 134 @SystemApi 135 public static final int STATE_INITIALIZING = 1; 136 /** 137 * This {@link ImsFeature} is ready for communication. Do not attempt to call framework methods 138 * until {@see #onFeatureReady()} is called. 139 * @hide 140 */ 141 @SystemApi 142 public static final int STATE_READY = 2; 143 144 /** 145 * Used for logging purposes. 146 * @hide 147 */ 148 public static final Map<Integer, String> STATE_LOG_MAP = new HashMap<Integer, String>() {{ 149 put(STATE_UNAVAILABLE, "UNAVAILABLE"); 150 put(STATE_INITIALIZING, "INITIALIZING"); 151 put(STATE_READY, "READY"); 152 }}; 153 154 /** 155 * Integer values defining the result codes that should be returned from 156 * {@link #changeEnabledCapabilities} when the framework tries to set a feature's capability. 157 * @hide 158 */ 159 @IntDef(flag = true, 160 value = { 161 CAPABILITY_ERROR_GENERIC, 162 CAPABILITY_SUCCESS 163 }) 164 @Retention(RetentionPolicy.SOURCE) 165 public @interface ImsCapabilityError {} 166 167 /** 168 * The capability was unable to be changed. 169 * @hide 170 */ 171 @SystemApi 172 public static final int CAPABILITY_ERROR_GENERIC = -1; 173 /** 174 * The capability was able to be changed. 175 * @hide 176 */ 177 @SystemApi 178 public static final int CAPABILITY_SUCCESS = 0; 179 180 /** 181 * Used by the ImsFeature to call back to the CapabilityCallback that the framework has 182 * provided. 183 */ 184 protected static class CapabilityCallbackProxy { 185 private final IImsCapabilityCallback mCallback; 186 187 /** @hide */ CapabilityCallbackProxy(IImsCapabilityCallback c)188 public CapabilityCallbackProxy(IImsCapabilityCallback c) { 189 mCallback = c; 190 } 191 192 /** 193 * This method notifies the provided framework callback that the request to change the 194 * indicated capability has failed and has not changed. 195 * 196 * @param capability The Capability that will be notified to the framework, defined as 197 * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}, 198 * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO}, 199 * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or 200 * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}. 201 * @param radioTech The radio tech that this capability failed for, defined as 202 * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE}, 203 * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} or 204 * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}. 205 * @param reason The reason this capability was unable to be changed, defined as 206 * {@link #CAPABILITY_ERROR_GENERIC} or {@link #CAPABILITY_SUCCESS}. 207 */ onChangeCapabilityConfigurationError(int capability, int radioTech, @ImsCapabilityError int reason)208 public void onChangeCapabilityConfigurationError(int capability, int radioTech, 209 @ImsCapabilityError int reason) { 210 if (mCallback == null) { 211 return; 212 } 213 try { 214 mCallback.onChangeCapabilityConfigurationError(capability, radioTech, reason); 215 } catch (RemoteException e) { 216 Log.e(LOG_TAG, "onChangeCapabilityConfigurationError called on dead binder."); 217 } 218 } 219 } 220 221 /** 222 * Contains the IMS capabilities defined and supported by an ImsFeature in the form of a 223 * bit-mask. 224 * 225 * @deprecated This class is not used directly, but rather extended in subclasses of 226 * {@link ImsFeature} to provide service specific capabilities. 227 * @see MmTelFeature.MmTelCapabilities 228 * @hide 229 */ 230 // Not Actually deprecated, but we need to remove it from the @SystemApi surface. 231 @Deprecated 232 @SystemApi // SystemApi only because it was leaked through type usage in a previous release. 233 @TestApi 234 public static class Capabilities { 235 /** @deprecated Use getters and accessors instead. */ 236 // Not actually deprecated, but we need to remove it from the @SystemApi surface eventually. 237 protected int mCapabilities = 0; 238 239 /** 240 * @hide 241 */ Capabilities()242 public Capabilities() { 243 } 244 245 /** 246 * @hide 247 */ Capabilities(int capabilities)248 protected Capabilities(int capabilities) { 249 mCapabilities = capabilities; 250 } 251 252 /** 253 * @param capabilities Capabilities to be added to the configuration in the form of a 254 * bit mask. 255 * @hide 256 */ addCapabilities(int capabilities)257 public void addCapabilities(int capabilities) { 258 mCapabilities |= capabilities; 259 } 260 261 /** 262 * @param capabilities Capabilities to be removed to the configuration in the form of a 263 * bit mask. 264 * @hide 265 */ removeCapabilities(int capabilities)266 public void removeCapabilities(int capabilities) { 267 mCapabilities &= ~capabilities; 268 } 269 270 /** 271 * @return true if all of the capabilities specified are capable. 272 * @hide 273 */ isCapable(int capabilities)274 public boolean isCapable(int capabilities) { 275 return (mCapabilities & capabilities) == capabilities; 276 } 277 278 /** 279 * @return a deep copy of the Capabilites. 280 * @hide 281 */ copy()282 public Capabilities copy() { 283 return new Capabilities(mCapabilities); 284 } 285 286 /** 287 * @return a bitmask containing the capability flags directly. 288 * @hide 289 */ getMask()290 public int getMask() { 291 return mCapabilities; 292 } 293 294 /** 295 * @hide 296 */ 297 @Override equals(Object o)298 public boolean equals(Object o) { 299 if (this == o) return true; 300 if (!(o instanceof Capabilities)) return false; 301 302 Capabilities that = (Capabilities) o; 303 304 return mCapabilities == that.mCapabilities; 305 } 306 307 /** 308 * @hide 309 */ 310 @Override hashCode()311 public int hashCode() { 312 return mCapabilities; 313 } 314 315 /** 316 * @hide 317 */ 318 @Override toString()319 public String toString() { 320 return "Capabilities: " + Integer.toBinaryString(mCapabilities); 321 } 322 } 323 324 /** @hide */ 325 protected Context mContext; 326 /** @hide */ 327 protected final Object mLock = new Object(); 328 329 private final RemoteCallbackListExt<IImsFeatureStatusCallback> mStatusCallbacks = 330 new RemoteCallbackListExt<>(); 331 private @ImsState int mState = STATE_UNAVAILABLE; 332 private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX; 333 private final RemoteCallbackListExt<IImsCapabilityCallback> mCapabilityCallbacks = 334 new RemoteCallbackListExt<>(); 335 private Capabilities mCapabilityStatus = new Capabilities(); 336 337 /** 338 * @hide 339 */ initialize(Context context, int slotId)340 public void initialize(Context context, int slotId) { 341 mContext = context; 342 mSlotId = slotId; 343 } 344 345 /** 346 * @return The SIM slot index associated with this ImsFeature. 347 * 348 * @see SubscriptionManager#getSubscriptionIds(int) for more information on getting the 349 * subscription IDs associated with this slot. 350 * @hide 351 */ 352 @SystemApi getSlotIndex()353 public final int getSlotIndex() { 354 return mSlotId; 355 } 356 357 /** 358 * @return The current state of the ImsFeature, set previously by {@link #setFeatureState(int)} 359 * or {@link #STATE_UNAVAILABLE} if it has not been updated yet. 360 * @hide 361 */ 362 @SystemApi getFeatureState()363 public @ImsState int getFeatureState() { 364 synchronized (mLock) { 365 return mState; 366 } 367 } 368 369 /** 370 * Set the state of the ImsFeature. The state is used as a signal to the framework to start or 371 * stop communication, depending on the state sent. 372 * @param state The ImsFeature's state, defined as {@link #STATE_UNAVAILABLE}, 373 * {@link #STATE_INITIALIZING}, or {@link #STATE_READY}. 374 * @hide 375 */ 376 @SystemApi setFeatureState(@msState int state)377 public final void setFeatureState(@ImsState int state) { 378 boolean isNotify = false; 379 synchronized (mLock) { 380 if (mState != state) { 381 mState = state; 382 isNotify = true; 383 } 384 } 385 if (isNotify) { 386 notifyFeatureState(state); 387 } 388 } 389 390 /** 391 * Not final for testing, but shouldn't be extended! 392 * @hide 393 */ 394 @VisibleForTesting addImsFeatureStatusCallback(@onNull IImsFeatureStatusCallback c)395 public void addImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) { 396 try { 397 // If we have just connected, send queued status. 398 c.notifyImsFeatureStatus(getFeatureState()); 399 // Add the callback if the callback completes successfully without a RemoteException. 400 mStatusCallbacks.register(c); 401 } catch (RemoteException e) { 402 Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage()); 403 } 404 } 405 406 /** 407 * Not final for testing, but shouldn't be extended! 408 * @hide 409 */ 410 @VisibleForTesting removeImsFeatureStatusCallback(@onNull IImsFeatureStatusCallback c)411 public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) { 412 mStatusCallbacks.unregister(c); 413 } 414 415 /** 416 * Internal method called by ImsFeature when setFeatureState has changed. 417 */ notifyFeatureState(@msState int state)418 private void notifyFeatureState(@ImsState int state) { 419 synchronized (mStatusCallbacks) { 420 mStatusCallbacks.broadcastAction((c) -> { 421 try { 422 c.notifyImsFeatureStatus(state); 423 } catch (RemoteException e) { 424 Log.w(LOG_TAG, e + " notifyFeatureState() - Skipping " 425 + "callback."); 426 } 427 }); 428 } 429 } 430 431 /** 432 * @hide 433 */ addCapabilityCallback(IImsCapabilityCallback c)434 public final void addCapabilityCallback(IImsCapabilityCallback c) { 435 mCapabilityCallbacks.register(c); 436 try { 437 // Notify the Capability callback that was just registered of the current capabilities. 438 c.onCapabilitiesStatusChanged(queryCapabilityStatus().mCapabilities); 439 } catch (RemoteException e) { 440 Log.w(LOG_TAG, "addCapabilityCallback: error accessing callback: " + e.getMessage()); 441 } 442 } 443 444 /** 445 * @hide 446 */ removeCapabilityCallback(IImsCapabilityCallback c)447 final void removeCapabilityCallback(IImsCapabilityCallback c) { 448 mCapabilityCallbacks.unregister(c); 449 } 450 451 /**@hide*/ queryCapabilityConfigurationInternal(int capability, int radioTech, IImsCapabilityCallback c)452 final void queryCapabilityConfigurationInternal(int capability, int radioTech, 453 IImsCapabilityCallback c) { 454 boolean enabled = queryCapabilityConfiguration(capability, radioTech); 455 try { 456 if (c != null) { 457 c.onQueryCapabilityConfiguration(capability, radioTech, enabled); 458 } 459 } catch (RemoteException e) { 460 Log.e(LOG_TAG, "queryCapabilityConfigurationInternal called on dead binder!"); 461 } 462 } 463 464 /** 465 * @return the cached capabilities status for this feature. 466 * @hide 467 */ 468 @VisibleForTesting queryCapabilityStatus()469 public Capabilities queryCapabilityStatus() { 470 synchronized (mLock) { 471 return mCapabilityStatus.copy(); 472 } 473 } 474 475 /** 476 * Called internally to request the change of enabled capabilities. 477 * @hide 478 */ 479 @VisibleForTesting requestChangeEnabledCapabilities(CapabilityChangeRequest request, IImsCapabilityCallback c)480 public final void requestChangeEnabledCapabilities(CapabilityChangeRequest request, 481 IImsCapabilityCallback c) { 482 if (request == null) { 483 throw new IllegalArgumentException( 484 "ImsFeature#requestChangeEnabledCapabilities called with invalid params."); 485 } 486 changeEnabledCapabilities(request, new CapabilityCallbackProxy(c)); 487 } 488 489 /** 490 * Called by the ImsFeature when the capabilities status has changed. 491 * 492 * @param caps the new {@link Capabilities} status of the {@link ImsFeature}. 493 * 494 * @hide 495 */ notifyCapabilitiesStatusChanged(Capabilities caps)496 protected final void notifyCapabilitiesStatusChanged(Capabilities caps) { 497 synchronized (mLock) { 498 mCapabilityStatus = caps.copy(); 499 } 500 501 synchronized (mCapabilityCallbacks) { 502 mCapabilityCallbacks.broadcastAction((callback) -> { 503 try { 504 Log.d(LOG_TAG, "ImsFeature notifyCapabilitiesStatusChanged Capabilities = " 505 + caps.mCapabilities); 506 callback.onCapabilitiesStatusChanged(caps.mCapabilities); 507 } catch (RemoteException e) { 508 Log.w(LOG_TAG, e + " notifyCapabilitiesStatusChanged() - Skipping " 509 + "callback."); 510 } 511 }); 512 } 513 } 514 515 /** 516 * Provides the ImsFeature with the ability to return the framework Capability Configuration 517 * for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and 518 * includes a capability A to enable or disable, this method should return the correct enabled 519 * status for capability A. 520 * @param capability The capability that we are querying the configuration for. 521 * @return true if the capability is enabled, false otherwise. 522 * @hide 523 */ 524 @SuppressWarnings("HiddenAbstractMethod") queryCapabilityConfiguration(int capability, int radioTech)525 public abstract boolean queryCapabilityConfiguration(int capability, int radioTech); 526 527 /** 528 * Features should override this method to receive Capability preference change requests from 529 * the framework using the provided {@link CapabilityChangeRequest}. If any of the capabilities 530 * in the {@link CapabilityChangeRequest} are not able to be completed due to an error, 531 * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} should be called for 532 * each failed capability. 533 * 534 * @param request A {@link CapabilityChangeRequest} containing requested capabilities to 535 * enable/disable. 536 * @param c A {@link CapabilityCallbackProxy}, which will be used to call back to the framework 537 * setting a subset of these capabilities fail, using 538 * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError}. 539 */ changeEnabledCapabilities(CapabilityChangeRequest request, CapabilityCallbackProxy c)540 public abstract void changeEnabledCapabilities(CapabilityChangeRequest request, 541 CapabilityCallbackProxy c); 542 543 /** 544 * Called when the framework is removing this feature and it needs to be cleaned up. 545 */ onFeatureRemoved()546 public abstract void onFeatureRemoved(); 547 548 /** 549 * Called after this ImsFeature has been initialized and has been set to the 550 * {@link ImsState#STATE_READY} state. 551 * <p> 552 * Any attempt by this feature to access the framework before this method is called will return 553 * with an {@link IllegalStateException}. 554 * The IMS provider should use this method to trigger registration for this feature on the IMS 555 * network, if needed. 556 */ onFeatureReady()557 public abstract void onFeatureReady(); 558 559 /** 560 * @return Binder instance that the framework will use to communicate with this feature. 561 * @hide 562 */ 563 @SuppressWarnings("HiddenAbstractMethod") getBinder()564 protected abstract IInterface getBinder(); 565 } 566