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 @TestApi 48 public abstract class ImsFeature { 49 50 private static final String LOG_TAG = "ImsFeature"; 51 52 /** 53 * Invalid feature value 54 * @hide 55 */ 56 public static final int FEATURE_INVALID = -1; 57 // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously 58 // defined values in ImsServiceClass for compatibility purposes. 59 /** 60 * This feature supports emergency calling over MMTEL. If defined, the framework will try to 61 * place an emergency call over IMS first. If it is not defined, the framework will only use 62 * CSFB for emergency calling. 63 * @hide 64 */ 65 @SystemApi @TestApi 66 public static final int FEATURE_EMERGENCY_MMTEL = 0; 67 /** 68 * This feature supports the MMTEL feature. 69 * @hide 70 */ 71 @SystemApi @TestApi 72 public static final int FEATURE_MMTEL = 1; 73 /** 74 * This feature supports the RCS feature. 75 * @hide 76 */ 77 @SystemApi @TestApi 78 public static final int FEATURE_RCS = 2; 79 /** 80 * Total number of features defined 81 * @hide 82 */ 83 public static final int FEATURE_MAX = 3; 84 85 /** 86 * Used for logging purposes. 87 * @hide 88 */ 89 public static final Map<Integer, String> FEATURE_LOG_MAP = new HashMap<Integer, String>() {{ 90 put(FEATURE_EMERGENCY_MMTEL, "EMERGENCY_MMTEL"); 91 put(FEATURE_MMTEL, "MMTEL"); 92 put(FEATURE_RCS, "RCS"); 93 }}; 94 95 /** 96 * Integer values defining IMS features that are supported in ImsFeature. 97 * @hide 98 */ 99 @IntDef(flag = true, 100 value = { 101 FEATURE_EMERGENCY_MMTEL, 102 FEATURE_MMTEL, 103 FEATURE_RCS 104 }) 105 @Retention(RetentionPolicy.SOURCE) 106 public @interface FeatureType {} 107 108 /** 109 * Integer values defining the state of the ImsFeature at any time. 110 * @hide 111 */ 112 @IntDef(flag = true, 113 value = { 114 STATE_UNAVAILABLE, 115 STATE_INITIALIZING, 116 STATE_READY, 117 }) 118 @Retention(RetentionPolicy.SOURCE) 119 public @interface ImsState {} 120 121 /** 122 * This {@link ImsFeature}'s state is unavailable and should not be communicated with. This will 123 * remove all bindings back to the framework. Any attempt to communicate with the framework 124 * during this time will result in an {@link IllegalStateException}. 125 * @hide 126 */ 127 @SystemApi @TestApi 128 public static final int STATE_UNAVAILABLE = 0; 129 /** 130 * This {@link ImsFeature} state is initializing and should not be communicated with. This will 131 * remove all bindings back to the framework. Any attempt to communicate with the framework 132 * during this time will result in an {@link IllegalStateException}. 133 * @hide 134 */ 135 @SystemApi @TestApi 136 public static final int STATE_INITIALIZING = 1; 137 /** 138 * This {@link ImsFeature} is ready for communication. Do not attempt to call framework methods 139 * until {@see #onFeatureReady()} is called. 140 * @hide 141 */ 142 @SystemApi @TestApi 143 public static final int STATE_READY = 2; 144 145 /** 146 * Used for logging purposes. 147 * @hide 148 */ 149 public static final Map<Integer, String> STATE_LOG_MAP = new HashMap<Integer, String>() {{ 150 put(STATE_UNAVAILABLE, "UNAVAILABLE"); 151 put(STATE_INITIALIZING, "INITIALIZING"); 152 put(STATE_READY, "READY"); 153 }}; 154 155 /** 156 * Integer values defining the result codes that should be returned from 157 * {@link #changeEnabledCapabilities} when the framework tries to set a feature's capability. 158 * @hide 159 */ 160 @IntDef(flag = true, 161 value = { 162 CAPABILITY_ERROR_GENERIC, 163 CAPABILITY_SUCCESS 164 }) 165 @Retention(RetentionPolicy.SOURCE) 166 public @interface ImsCapabilityError {} 167 168 /** 169 * The capability was unable to be changed. 170 * @hide 171 */ 172 @SystemApi @TestApi 173 public static final int CAPABILITY_ERROR_GENERIC = -1; 174 /** 175 * The capability was able to be changed. 176 * @hide 177 */ 178 @SystemApi @TestApi 179 public static final int CAPABILITY_SUCCESS = 0; 180 181 /** 182 * Used by the ImsFeature to call back to the CapabilityCallback that the framework has 183 * provided. 184 */ 185 protected static class CapabilityCallbackProxy { 186 private final IImsCapabilityCallback mCallback; 187 188 /** @hide */ CapabilityCallbackProxy(IImsCapabilityCallback c)189 public CapabilityCallbackProxy(IImsCapabilityCallback c) { 190 mCallback = c; 191 } 192 193 /** 194 * This method notifies the provided framework callback that the request to change the 195 * indicated capability has failed and has not changed. 196 * 197 * @param capability The Capability that will be notified to the framework, defined as 198 * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}, 199 * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO}, 200 * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or 201 * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}. 202 * @param radioTech The radio tech that this capability failed for, defined as 203 * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or 204 * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}. 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 final 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 @TestApi 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 @TestApi 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 @TestApi 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 */ queryCapabilityConfiguration(int capability, int radioTech)513 public abstract boolean queryCapabilityConfiguration(int capability, int radioTech); 514 515 /** 516 * Features should override this method to receive Capability preference change requests from 517 * the framework using the provided {@link CapabilityChangeRequest}. If any of the capabilities 518 * in the {@link CapabilityChangeRequest} are not able to be completed due to an error, 519 * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} should be called for 520 * each failed capability. 521 * 522 * @param request A {@link CapabilityChangeRequest} containing requested capabilities to 523 * enable/disable. 524 * @param c A {@link CapabilityCallbackProxy}, which will be used to call back to the framework 525 * setting a subset of these capabilities fail, using 526 * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError}. 527 */ changeEnabledCapabilities(CapabilityChangeRequest request, CapabilityCallbackProxy c)528 public abstract void changeEnabledCapabilities(CapabilityChangeRequest request, 529 CapabilityCallbackProxy c); 530 531 /** 532 * Called when the framework is removing this feature and it needs to be cleaned up. 533 */ onFeatureRemoved()534 public abstract void onFeatureRemoved(); 535 536 /** 537 * Called after this ImsFeature has been initialized and has been set to the 538 * {@link ImsState#STATE_READY} state. 539 * <p> 540 * Any attempt by this feature to access the framework before this method is called will return 541 * with an {@link IllegalStateException}. 542 * The IMS provider should use this method to trigger registration for this feature on the IMS 543 * network, if needed. 544 */ onFeatureReady()545 public abstract void onFeatureReady(); 546 547 /** 548 * @return Binder instance that the framework will use to communicate with this feature. 549 * @hide 550 */ getBinder()551 protected abstract IInterface getBinder(); 552 } 553