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 synchronized (mLock) { 379 if (mState != state) { 380 mState = state; 381 notifyFeatureState(state); 382 } 383 } 384 } 385 386 /** 387 * Not final for testing, but shouldn't be extended! 388 * @hide 389 */ 390 @VisibleForTesting addImsFeatureStatusCallback(@onNull IImsFeatureStatusCallback c)391 public void addImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) { 392 try { 393 // If we have just connected, send queued status. 394 c.notifyImsFeatureStatus(getFeatureState()); 395 // Add the callback if the callback completes successfully without a RemoteException. 396 mStatusCallbacks.register(c); 397 } catch (RemoteException e) { 398 Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage()); 399 } 400 } 401 402 /** 403 * Not final for testing, but shouldn't be extended! 404 * @hide 405 */ 406 @VisibleForTesting removeImsFeatureStatusCallback(@onNull IImsFeatureStatusCallback c)407 public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) { 408 mStatusCallbacks.unregister(c); 409 } 410 411 /** 412 * Internal method called by ImsFeature when setFeatureState has changed. 413 */ notifyFeatureState(@msState int state)414 private void notifyFeatureState(@ImsState int state) { 415 mStatusCallbacks.broadcastAction((c) -> { 416 try { 417 c.notifyImsFeatureStatus(state); 418 } catch (RemoteException e) { 419 Log.w(LOG_TAG, e + " notifyFeatureState() - Skipping " 420 + "callback."); 421 } 422 }); 423 } 424 425 /** 426 * @hide 427 */ addCapabilityCallback(IImsCapabilityCallback c)428 public final void addCapabilityCallback(IImsCapabilityCallback c) { 429 mCapabilityCallbacks.register(c); 430 try { 431 // Notify the Capability callback that was just registered of the current capabilities. 432 c.onCapabilitiesStatusChanged(queryCapabilityStatus().mCapabilities); 433 } catch (RemoteException e) { 434 Log.w(LOG_TAG, "addCapabilityCallback: error accessing callback: " + e.getMessage()); 435 } 436 } 437 438 /** 439 * @hide 440 */ removeCapabilityCallback(IImsCapabilityCallback c)441 final void removeCapabilityCallback(IImsCapabilityCallback c) { 442 mCapabilityCallbacks.unregister(c); 443 } 444 445 /**@hide*/ queryCapabilityConfigurationInternal(int capability, int radioTech, IImsCapabilityCallback c)446 final void queryCapabilityConfigurationInternal(int capability, int radioTech, 447 IImsCapabilityCallback c) { 448 boolean enabled = queryCapabilityConfiguration(capability, radioTech); 449 try { 450 if (c != null) { 451 c.onQueryCapabilityConfiguration(capability, radioTech, enabled); 452 } 453 } catch (RemoteException e) { 454 Log.e(LOG_TAG, "queryCapabilityConfigurationInternal called on dead binder!"); 455 } 456 } 457 458 /** 459 * @return the cached capabilities status for this feature. 460 * @hide 461 */ 462 @VisibleForTesting queryCapabilityStatus()463 public Capabilities queryCapabilityStatus() { 464 synchronized (mLock) { 465 return mCapabilityStatus.copy(); 466 } 467 } 468 469 /** 470 * Called internally to request the change of enabled capabilities. 471 * @hide 472 */ 473 @VisibleForTesting requestChangeEnabledCapabilities(CapabilityChangeRequest request, IImsCapabilityCallback c)474 public final void requestChangeEnabledCapabilities(CapabilityChangeRequest request, 475 IImsCapabilityCallback c) { 476 if (request == null) { 477 throw new IllegalArgumentException( 478 "ImsFeature#requestChangeEnabledCapabilities called with invalid params."); 479 } 480 changeEnabledCapabilities(request, new CapabilityCallbackProxy(c)); 481 } 482 483 /** 484 * Called by the ImsFeature when the capabilities status has changed. 485 * 486 * @param caps the new {@link Capabilities} status of the {@link ImsFeature}. 487 * 488 * @hide 489 */ notifyCapabilitiesStatusChanged(Capabilities caps)490 protected final void notifyCapabilitiesStatusChanged(Capabilities caps) { 491 synchronized (mLock) { 492 mCapabilityStatus = caps.copy(); 493 } 494 mCapabilityCallbacks.broadcastAction((callback) -> { 495 try { 496 callback.onCapabilitiesStatusChanged(caps.mCapabilities); 497 } catch (RemoteException e) { 498 Log.w(LOG_TAG, e + " notifyCapabilitiesStatusChanged() - Skipping " 499 + "callback."); 500 } 501 }); 502 } 503 504 /** 505 * Provides the ImsFeature with the ability to return the framework Capability Configuration 506 * for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and 507 * includes a capability A to enable or disable, this method should return the correct enabled 508 * status for capability A. 509 * @param capability The capability that we are querying the configuration for. 510 * @return true if the capability is enabled, false otherwise. 511 * @hide 512 */ 513 @SuppressWarnings("HiddenAbstractMethod") queryCapabilityConfiguration(int capability, int radioTech)514 public abstract boolean queryCapabilityConfiguration(int capability, int radioTech); 515 516 /** 517 * Features should override this method to receive Capability preference change requests from 518 * the framework using the provided {@link CapabilityChangeRequest}. If any of the capabilities 519 * in the {@link CapabilityChangeRequest} are not able to be completed due to an error, 520 * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} should be called for 521 * each failed capability. 522 * 523 * @param request A {@link CapabilityChangeRequest} containing requested capabilities to 524 * enable/disable. 525 * @param c A {@link CapabilityCallbackProxy}, which will be used to call back to the framework 526 * setting a subset of these capabilities fail, using 527 * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError}. 528 */ changeEnabledCapabilities(CapabilityChangeRequest request, CapabilityCallbackProxy c)529 public abstract void changeEnabledCapabilities(CapabilityChangeRequest request, 530 CapabilityCallbackProxy c); 531 532 /** 533 * Called when the framework is removing this feature and it needs to be cleaned up. 534 */ onFeatureRemoved()535 public abstract void onFeatureRemoved(); 536 537 /** 538 * Called after this ImsFeature has been initialized and has been set to the 539 * {@link ImsState#STATE_READY} state. 540 * <p> 541 * Any attempt by this feature to access the framework before this method is called will return 542 * with an {@link IllegalStateException}. 543 * The IMS provider should use this method to trigger registration for this feature on the IMS 544 * network, if needed. 545 */ onFeatureReady()546 public abstract void onFeatureReady(); 547 548 /** 549 * @return Binder instance that the framework will use to communicate with this feature. 550 * @hide 551 */ 552 @SuppressWarnings("HiddenAbstractMethod") getBinder()553 protected abstract IInterface getBinder(); 554 } 555