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.stub; 18 19 import android.annotation.IntDef; 20 import android.annotation.IntRange; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.SystemApi; 24 import android.net.Uri; 25 import android.os.RemoteException; 26 import android.telephony.ims.ImsReasonInfo; 27 import android.telephony.ims.ImsRegistrationAttributes; 28 import android.telephony.ims.RegistrationManager; 29 import android.telephony.ims.aidl.IImsRegistration; 30 import android.telephony.ims.aidl.IImsRegistrationCallback; 31 import android.util.Log; 32 33 import com.android.internal.telephony.util.RemoteCallbackListExt; 34 import com.android.internal.telephony.util.TelephonyUtils; 35 import com.android.internal.util.ArrayUtils; 36 37 import java.lang.annotation.Retention; 38 import java.lang.annotation.RetentionPolicy; 39 import java.util.concurrent.CancellationException; 40 import java.util.concurrent.CompletableFuture; 41 import java.util.concurrent.CompletionException; 42 import java.util.concurrent.ExecutionException; 43 import java.util.concurrent.Executor; 44 import java.util.concurrent.atomic.AtomicReference; 45 import java.util.function.Supplier; 46 47 /** 48 * Controls IMS registration for this ImsService and notifies the framework when the IMS 49 * registration for this ImsService has changed status. 50 * <p> 51 * Note: There is no guarantee on the thread that the calls from the framework will be called on. It 52 * is the implementors responsibility to handle moving the calls to a working thread if required. 53 */ 54 public class ImsRegistrationImplBase { 55 56 private static final String LOG_TAG = "ImsRegistrationImplBase"; 57 58 /** 59 * @hide 60 */ 61 // Defines the underlying radio technology type that we have registered for IMS over. 62 @IntDef(value = { 63 REGISTRATION_TECH_NONE, 64 REGISTRATION_TECH_LTE, 65 REGISTRATION_TECH_IWLAN, 66 REGISTRATION_TECH_CROSS_SIM, 67 REGISTRATION_TECH_NR 68 }) 69 @Retention(RetentionPolicy.SOURCE) 70 public @interface ImsRegistrationTech {} 71 /** 72 * No registration technology specified, used when we are not registered. 73 */ 74 public static final int REGISTRATION_TECH_NONE = -1; 75 /** 76 * This ImsService is registered to IMS via LTE. 77 */ 78 public static final int REGISTRATION_TECH_LTE = 0; 79 /** 80 * This ImsService is registered to IMS via IWLAN. 81 */ 82 public static final int REGISTRATION_TECH_IWLAN = 1; 83 84 /** 85 * This ImsService is registered to IMS via internet over second subscription. 86 */ 87 public static final int REGISTRATION_TECH_CROSS_SIM = 2; 88 89 /** 90 * This ImsService is registered to IMS via NR. 91 */ 92 public static final int REGISTRATION_TECH_NR = 3; 93 94 /** 95 * This is used to check the upper range of registration tech 96 * @hide 97 */ 98 public static final int REGISTRATION_TECH_MAX = REGISTRATION_TECH_NR + 1; 99 100 // Registration states, used to notify new ImsRegistrationImplBase#Callbacks of the current 101 // state. 102 // The unknown state is set as the initialization state. This is so that we do not call back 103 // with NOT_REGISTERED in the case where the ImsService has not updated the registration state 104 // yet. 105 private static final int REGISTRATION_STATE_UNKNOWN = -1; 106 107 private Executor mExecutor; 108 109 /** 110 * Create a new ImsRegistration. 111 * <p> 112 * Method stubs called from the framework will be called asynchronously. To specify the 113 * {@link Executor} that the methods stubs will be called, use 114 * {@link ImsRegistrationImplBase#ImsRegistrationImplBase(Executor)} instead. 115 * @hide This API is not part of the Android public SDK API 116 */ 117 @SystemApi ImsRegistrationImplBase()118 public ImsRegistrationImplBase() { 119 super(); 120 } 121 122 /** 123 * Create a ImsRegistration using the Executor specified for methods being called by the 124 * framework. 125 * @param executor The executor for the framework to use when executing the methods overridden 126 * by the implementation of ImsRegistration. 127 * @hide This API is not part of the Android public SDK API 128 */ 129 @SystemApi ImsRegistrationImplBase(@onNull Executor executor)130 public ImsRegistrationImplBase(@NonNull Executor executor) { 131 super(); 132 mExecutor = executor; 133 } 134 135 private final IImsRegistration mBinder = new IImsRegistration.Stub() { 136 137 @Override 138 public @ImsRegistrationTech int getRegistrationTechnology() throws RemoteException { 139 return executeMethodAsyncForResult(() -> (mRegistrationAttributes == null) 140 ? REGISTRATION_TECH_NONE : mRegistrationAttributes.getRegistrationTechnology(), 141 "getRegistrationTechnology"); 142 } 143 144 @Override 145 public void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { 146 AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); 147 executeMethodAsync(() -> { 148 try { 149 ImsRegistrationImplBase.this.addRegistrationCallback(c); 150 } catch (RemoteException e) { 151 exceptionRef.set(e); 152 } 153 }, "addRegistrationCallback"); 154 155 if (exceptionRef.get() != null) { 156 throw exceptionRef.get(); 157 } 158 } 159 160 @Override 161 public void removeRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { 162 executeMethodAsync(() -> ImsRegistrationImplBase.this.removeRegistrationCallback(c), 163 "removeRegistrationCallback"); 164 } 165 166 @Override 167 public void triggerFullNetworkRegistration(int sipCode, String sipReason) { 168 executeMethodAsyncNoException(() -> ImsRegistrationImplBase.this 169 .triggerFullNetworkRegistration(sipCode, sipReason), 170 "triggerFullNetworkRegistration"); 171 } 172 173 @Override 174 public void triggerUpdateSipDelegateRegistration() { 175 executeMethodAsyncNoException(() -> ImsRegistrationImplBase.this 176 .updateSipDelegateRegistration(), "triggerUpdateSipDelegateRegistration"); 177 } 178 179 @Override 180 public void triggerSipDelegateDeregistration() { 181 executeMethodAsyncNoException(() -> ImsRegistrationImplBase.this 182 .triggerSipDelegateDeregistration(), "triggerSipDelegateDeregistration"); 183 } 184 185 // Call the methods with a clean calling identity on the executor and wait indefinitely for 186 // the future to return. 187 private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException { 188 try { 189 CompletableFuture.runAsync( 190 () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join(); 191 } catch (CancellationException | CompletionException e) { 192 Log.w(LOG_TAG, "ImsRegistrationImplBase Binder - " + errorLogName + " exception: " 193 + e.getMessage()); 194 throw new RemoteException(e.getMessage()); 195 } 196 } 197 198 private void executeMethodAsyncNoException(Runnable r, String errorLogName) { 199 try { 200 CompletableFuture.runAsync( 201 () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join(); 202 } catch (CancellationException | CompletionException e) { 203 Log.w(LOG_TAG, "ImsRegistrationImplBase Binder - " + errorLogName + " exception: " 204 + e.getMessage()); 205 } 206 } 207 208 private <T> T executeMethodAsyncForResult(Supplier<T> r, 209 String errorLogName) throws RemoteException { 210 CompletableFuture<T> future = CompletableFuture.supplyAsync( 211 () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor); 212 try { 213 return future.get(); 214 } catch (ExecutionException | InterruptedException e) { 215 Log.w(LOG_TAG, "ImsRegistrationImplBase Binder - " + errorLogName + " exception: " 216 + e.getMessage()); 217 throw new RemoteException(e.getMessage()); 218 } 219 } 220 }; 221 222 private final RemoteCallbackListExt<IImsRegistrationCallback> mCallbacks = 223 new RemoteCallbackListExt<>(); 224 private final Object mLock = new Object(); 225 // Locked on mLock 226 private ImsRegistrationAttributes mRegistrationAttributes; 227 // Locked on mLock 228 private int mRegistrationState = REGISTRATION_STATE_UNKNOWN; 229 // Locked on mLock, create unspecified disconnect cause. 230 private ImsReasonInfo mLastDisconnectCause = new ImsReasonInfo(); 231 232 // We hold onto the uris each time they change so that we can send it to a callback when its 233 // first added. 234 private Uri[] mUris = new Uri[0]; 235 private boolean mUrisSet = false; 236 237 /** 238 * @hide 239 */ getBinder()240 public final IImsRegistration getBinder() { 241 return mBinder; 242 } 243 addRegistrationCallback(IImsRegistrationCallback c)244 private void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { 245 mCallbacks.register(c); 246 updateNewCallbackWithState(c); 247 } 248 removeRegistrationCallback(IImsRegistrationCallback c)249 private void removeRegistrationCallback(IImsRegistrationCallback c) { 250 mCallbacks.unregister(c); 251 } 252 253 /** 254 * Called by the framework to request that the ImsService perform the network registration 255 * of all SIP delegates associated with this ImsService. 256 * <p> 257 * If the SIP delegate feature tag configuration has changed, then this method will be 258 * called in order to let the ImsService know that it can pick up these changes in the IMS 259 * registration. 260 * @hide This API is not part of the Android public SDK API 261 */ 262 @SystemApi updateSipDelegateRegistration()263 public void updateSipDelegateRegistration() { 264 // Stub implementation, ImsService should implement this 265 } 266 267 268 /** 269 * Called by the framework to request that the ImsService perform the network deregistration of 270 * all SIP delegates associated with this ImsService. 271 * <p> 272 * This is typically called in situations where the user has changed the configuration of the 273 * device (for example, the default messaging application) and the framework is reconfiguring 274 * the tags associated with each IMS application. 275 * <p> 276 * This should not affect the registration of features managed by the ImsService itself, such as 277 * feature tags related to MMTEL registration. 278 * @hide This API is not part of the Android public SDK API 279 */ 280 @SystemApi triggerSipDelegateDeregistration()281 public void triggerSipDelegateDeregistration() { 282 // Stub implementation, ImsService should implement this 283 } 284 285 /** 286 * Called by the framework to notify the ImsService that a SIP delegate connection has received 287 * a SIP message containing a permanent failure response (such as a 403) or an indication that a 288 * SIP response timer has timed out in response to an outgoing SIP message. This method will be 289 * called when this condition occurs to trigger the ImsService to tear down the full IMS 290 * registration and re-register again. 291 * 292 * @param sipCode The SIP error code that represents a permanent failure that was received in 293 * response to a request generated by the IMS application. See RFC3261 7.2 for the general 294 * classes of responses available here, however the codes that generate this condition may 295 * be carrier specific. 296 * @param sipReason The reason associated with the SIP error code. {@code null} if there was no 297 * reason associated with the error. 298 * @hide This API is not part of the Android public SDK API 299 */ 300 @SystemApi triggerFullNetworkRegistration(@ntRangefrom = 100, to = 699) int sipCode, @Nullable String sipReason)301 public void triggerFullNetworkRegistration(@IntRange(from = 100, to = 699) int sipCode, 302 @Nullable String sipReason) { 303 // Stub implementation, ImsService should implement this 304 } 305 306 307 /** 308 * Notify the framework that the device is connected to the IMS network. 309 * 310 * @param imsRadioTech the radio access technology. 311 * @hide This API is not part of the Android public SDK API 312 */ 313 @SystemApi onRegistered(@msRegistrationTech int imsRadioTech)314 public final void onRegistered(@ImsRegistrationTech int imsRadioTech) { 315 onRegistered(new ImsRegistrationAttributes.Builder(imsRadioTech).build()); 316 } 317 318 /** 319 * Notify the framework that the device is connected to the IMS network. 320 * 321 * @param attributes The attributes associated with the IMS registration. 322 * @hide This API is not part of the Android public SDK API 323 */ 324 @SystemApi onRegistered(@onNull ImsRegistrationAttributes attributes)325 public final void onRegistered(@NonNull ImsRegistrationAttributes attributes) { 326 updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERED); 327 mCallbacks.broadcastAction((c) -> { 328 try { 329 c.onRegistered(attributes); 330 } catch (RemoteException e) { 331 Log.w(LOG_TAG, e + "onRegistered(int, Set) - Skipping callback."); 332 } 333 }); 334 } 335 336 /** 337 * Notify the framework that the device is trying to connect the IMS network. 338 * 339 * @param imsRadioTech the radio access technology. 340 * @hide This API is not part of the Android public SDK API 341 */ 342 @SystemApi onRegistering(@msRegistrationTech int imsRadioTech)343 public final void onRegistering(@ImsRegistrationTech int imsRadioTech) { 344 onRegistering(new ImsRegistrationAttributes.Builder(imsRadioTech).build()); 345 } 346 347 /** 348 * Notify the framework that the device is trying to connect the IMS network. 349 * 350 * @param attributes The attributes associated with the IMS registration. 351 * @hide This API is not part of the Android public SDK API 352 */ 353 @SystemApi onRegistering(@onNull ImsRegistrationAttributes attributes)354 public final void onRegistering(@NonNull ImsRegistrationAttributes attributes) { 355 updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERING); 356 mCallbacks.broadcastAction((c) -> { 357 try { 358 c.onRegistering(attributes); 359 } catch (RemoteException e) { 360 Log.w(LOG_TAG, e + "onRegistering(int, Set) - Skipping callback."); 361 } 362 }); 363 } 364 365 /** 366 * Notify the framework that the device is disconnected from the IMS network. 367 * <p> 368 * Note: Prior to calling {@link #onDeregistered(ImsReasonInfo)}, you should ensure that any 369 * changes to {@link android.telephony.ims.feature.ImsFeature} capability availability is sent 370 * to the framework. For example, 371 * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO} 372 * and 373 * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE} 374 * may be set to unavailable to ensure the framework knows these services are no longer 375 * available due to de-registration. If you do not report capability changes impacted by 376 * de-registration, the framework will not know which features are no longer available as a 377 * result. 378 * 379 * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. 380 * @hide This API is not part of the Android public SDK API 381 */ 382 @SystemApi onDeregistered(ImsReasonInfo info)383 public final void onDeregistered(ImsReasonInfo info) { 384 updateToDisconnectedState(info); 385 // ImsReasonInfo should never be null. 386 final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo(); 387 mCallbacks.broadcastAction((c) -> { 388 try { 389 c.onDeregistered(reasonInfo); 390 } catch (RemoteException e) { 391 Log.w(LOG_TAG, e + "onDeregistered() - Skipping callback."); 392 } 393 }); 394 } 395 396 /** 397 * Notify the framework that the handover from the current radio technology to the technology 398 * defined in {@code imsRadioTech} has failed. 399 * @param imsRadioTech The technology that has failed to be changed. Valid values are 400 * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and 401 * {@link #REGISTRATION_TECH_CROSS_SIM}. 402 * @param info The {@link ImsReasonInfo} for the failure to change technology. 403 * @hide This API is not part of the Android public SDK API 404 */ 405 @SystemApi onTechnologyChangeFailed(@msRegistrationTech int imsRadioTech, ImsReasonInfo info)406 public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech, 407 ImsReasonInfo info) { 408 final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo(); 409 mCallbacks.broadcastAction((c) -> { 410 try { 411 c.onTechnologyChangeFailed(imsRadioTech, reasonInfo); 412 } catch (RemoteException e) { 413 Log.w(LOG_TAG, e + "onTechnologyChangeFailed() - Skipping callback."); 414 } 415 }); 416 } 417 418 /** 419 * Invoked when the {@link Uri}s associated to this device's subscriber have changed. 420 * These {@link Uri}s' are filtered out during conference calls. 421 * 422 * The {@link Uri}s are not guaranteed to be different between subsequent calls. 423 * @param uris changed uris 424 * @hide This API is not part of the Android public SDK API 425 */ 426 @SystemApi onSubscriberAssociatedUriChanged(Uri[] uris)427 public final void onSubscriberAssociatedUriChanged(Uri[] uris) { 428 synchronized (mLock) { 429 mUris = ArrayUtils.cloneOrNull(uris); 430 mUrisSet = true; 431 } 432 mCallbacks.broadcastAction((c) -> onSubscriberAssociatedUriChanged(c, uris)); 433 } 434 onSubscriberAssociatedUriChanged(IImsRegistrationCallback callback, Uri[] uris)435 private void onSubscriberAssociatedUriChanged(IImsRegistrationCallback callback, Uri[] uris) { 436 try { 437 callback.onSubscriberAssociatedUriChanged(uris); 438 } catch (RemoteException e) { 439 Log.w(LOG_TAG, e + "onSubscriberAssociatedUriChanged() - Skipping callback."); 440 } 441 } 442 updateToState(ImsRegistrationAttributes attributes, int newState)443 private void updateToState(ImsRegistrationAttributes attributes, int newState) { 444 synchronized (mLock) { 445 mRegistrationAttributes = attributes; 446 mRegistrationState = newState; 447 mLastDisconnectCause = null; 448 } 449 } 450 updateToDisconnectedState(ImsReasonInfo info)451 private void updateToDisconnectedState(ImsReasonInfo info) { 452 synchronized (mLock) { 453 //We don't want to send this info over if we are disconnected 454 mUrisSet = false; 455 mUris = null; 456 457 updateToState(new ImsRegistrationAttributes.Builder(REGISTRATION_TECH_NONE).build(), 458 RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED); 459 if (info != null) { 460 mLastDisconnectCause = info; 461 } else { 462 Log.w(LOG_TAG, "updateToDisconnectedState: no ImsReasonInfo provided."); 463 mLastDisconnectCause = new ImsReasonInfo(); 464 } 465 } 466 } 467 468 /** 469 * @param c the newly registered callback that will be updated with the current registration 470 * state. 471 */ updateNewCallbackWithState(IImsRegistrationCallback c)472 private void updateNewCallbackWithState(IImsRegistrationCallback c) 473 throws RemoteException { 474 int state; 475 ImsRegistrationAttributes attributes; 476 ImsReasonInfo disconnectInfo; 477 boolean urisSet; 478 Uri[] uris; 479 synchronized (mLock) { 480 state = mRegistrationState; 481 attributes = mRegistrationAttributes; 482 disconnectInfo = mLastDisconnectCause; 483 urisSet = mUrisSet; 484 uris = mUris; 485 } 486 switch (state) { 487 case RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED: { 488 c.onDeregistered(disconnectInfo); 489 break; 490 } 491 case RegistrationManager.REGISTRATION_STATE_REGISTERING: { 492 c.onRegistering(attributes); 493 break; 494 } 495 case RegistrationManager.REGISTRATION_STATE_REGISTERED: { 496 c.onRegistered(attributes); 497 break; 498 } 499 case REGISTRATION_STATE_UNKNOWN: { 500 // Do not callback if the state has not been updated yet by the ImsService. 501 break; 502 } 503 } 504 if (urisSet) { 505 onSubscriberAssociatedUriChanged(c, uris); 506 } 507 } 508 509 /** 510 * Set default Executor from ImsService. 511 * @param executor The default executor for the framework to use when executing the methods 512 * overridden by the implementation of Registration. 513 * @hide 514 */ setDefaultExecutor(@onNull Executor executor)515 public final void setDefaultExecutor(@NonNull Executor executor) { 516 if (mExecutor == null) { 517 mExecutor = executor; 518 } 519 } 520 } 521