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.FlaggedApi; 20 import android.annotation.IntDef; 21 import android.annotation.IntRange; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.annotation.SystemApi; 25 import android.net.Uri; 26 import android.os.RemoteException; 27 import android.telephony.ims.ImsReasonInfo; 28 import android.telephony.ims.ImsRegistrationAttributes; 29 import android.telephony.ims.RegistrationManager; 30 import android.telephony.ims.SipDetails; 31 import android.telephony.ims.aidl.IImsRegistration; 32 import android.telephony.ims.aidl.IImsRegistrationCallback; 33 import android.util.Log; 34 35 import com.android.internal.telephony.flags.Flags; 36 import com.android.internal.telephony.util.RemoteCallbackListExt; 37 import com.android.internal.telephony.util.TelephonyUtils; 38 import com.android.internal.util.ArrayUtils; 39 40 import java.lang.annotation.Retention; 41 import java.lang.annotation.RetentionPolicy; 42 import java.util.concurrent.CancellationException; 43 import java.util.concurrent.CompletableFuture; 44 import java.util.concurrent.CompletionException; 45 import java.util.concurrent.ExecutionException; 46 import java.util.concurrent.Executor; 47 import java.util.concurrent.atomic.AtomicReference; 48 import java.util.function.Consumer; 49 import java.util.function.Supplier; 50 51 /** 52 * Controls IMS registration for this ImsService and notifies the framework when the IMS 53 * registration for this ImsService has changed status. 54 * <p> 55 * Note: There is no guarantee on the thread that the calls from the framework will be called on. It 56 * is the implementors responsibility to handle moving the calls to a working thread if required. 57 */ 58 public class ImsRegistrationImplBase { 59 60 private static final String LOG_TAG = "ImsRegistrationImplBase"; 61 /** 62 * @hide 63 */ 64 // Defines the underlying radio technology type that we have registered for IMS over. 65 @IntDef(value = { 66 REGISTRATION_TECH_NONE, 67 REGISTRATION_TECH_LTE, 68 REGISTRATION_TECH_IWLAN, 69 REGISTRATION_TECH_CROSS_SIM, 70 REGISTRATION_TECH_NR, 71 REGISTRATION_TECH_3G 72 }) 73 @Retention(RetentionPolicy.SOURCE) 74 public @interface ImsRegistrationTech {} 75 /** 76 * No registration technology specified, used when we are not registered. 77 */ 78 public static final int REGISTRATION_TECH_NONE = -1; 79 /** 80 * This ImsService is registered to IMS via LTE. 81 */ 82 public static final int REGISTRATION_TECH_LTE = 0; 83 /** 84 * This ImsService is registered to IMS via IWLAN. 85 */ 86 public static final int REGISTRATION_TECH_IWLAN = 1; 87 88 /** 89 * This ImsService is registered to IMS via internet over second subscription. 90 */ 91 public static final int REGISTRATION_TECH_CROSS_SIM = 2; 92 93 /** 94 * This ImsService is registered to IMS via NR. 95 */ 96 public static final int REGISTRATION_TECH_NR = 3; 97 98 /** 99 * This ImsService is registered to IMS via 3G. 100 */ 101 public static final int REGISTRATION_TECH_3G = 4; 102 103 /** 104 * This is used to check the upper range of registration tech 105 * @hide 106 */ 107 public static final int REGISTRATION_TECH_MAX = REGISTRATION_TECH_3G + 1; 108 109 // Registration states, used to notify new ImsRegistrationImplBase#Callbacks of the current 110 // state. 111 // The unknown state is set as the initialization state. This is so that we do not call back 112 // with NOT_REGISTERED in the case where the ImsService has not updated the registration state 113 // yet. 114 private static final int REGISTRATION_STATE_UNKNOWN = -1; 115 116 /** @hide */ 117 @Retention(RetentionPolicy.SOURCE) 118 @IntDef( 119 prefix = {"REASON_"}, 120 value = { 121 REASON_UNKNOWN, 122 REASON_SIM_REMOVED, 123 REASON_SIM_REFRESH, 124 REASON_ALLOWED_NETWORK_TYPES_CHANGED, 125 REASON_NON_IMS_CAPABLE_NETWORK, 126 REASON_RADIO_POWER_OFF, 127 REASON_HANDOVER_FAILED, 128 REASON_VOPS_NOT_SUPPORTED, 129 }) 130 public @interface ImsDeregistrationReason{} 131 132 /** 133 * Unspecified reason. 134 * @hide 135 */ 136 public static final int REASON_UNKNOWN = 0; 137 138 /** 139 * Since SIM is removed, the credentials for IMS service is also removed. 140 * @hide 141 */ 142 public static final int REASON_SIM_REMOVED = 1; 143 144 /** 145 * Detach from the network shall be performed due to the SIM refresh. IMS service should be 146 * deregistered before that procedure. 147 * @hide 148 */ 149 public static final int REASON_SIM_REFRESH = 2; 150 151 /** 152 * The allowed network types have changed, resulting in a network type 153 * that does not support IMS. 154 * @hide 155 */ 156 public static final int REASON_ALLOWED_NETWORK_TYPES_CHANGED = 3; 157 158 /** 159 * The device camped on a network that does not support IMS. 160 * @hide 161 */ 162 public static final int REASON_NON_IMS_CAPABLE_NETWORK = 4; 163 164 /** 165 * IMS service should be deregistered from the network before turning off the radio. 166 * @hide 167 */ 168 public static final int REASON_RADIO_POWER_OFF = 5; 169 170 /** 171 * Since the handover is failed or not allowed, the data service for IMS shall be 172 * disconnected. 173 * @hide 174 */ 175 public static final int REASON_HANDOVER_FAILED = 6; 176 177 /** 178 * The network is changed to a network that does not support voice over IMS. 179 * @hide 180 */ 181 public static final int REASON_VOPS_NOT_SUPPORTED = 7; 182 183 private Executor mExecutor; 184 185 /** 186 * Create a new ImsRegistration. 187 * <p> 188 * Method stubs called from the framework will be called asynchronously. To specify the 189 * {@link Executor} that the methods stubs will be called, use 190 * {@link ImsRegistrationImplBase#ImsRegistrationImplBase(Executor)} instead. 191 * @hide This API is not part of the Android public SDK API 192 */ 193 @SystemApi ImsRegistrationImplBase()194 public ImsRegistrationImplBase() { 195 super(); 196 } 197 198 /** 199 * Create a ImsRegistration using the Executor specified for methods being called by the 200 * framework. 201 * @param executor The executor for the framework to use when executing the methods overridden 202 * by the implementation of ImsRegistration. 203 * @hide This API is not part of the Android public SDK API 204 */ 205 @SystemApi ImsRegistrationImplBase(@onNull Executor executor)206 public ImsRegistrationImplBase(@NonNull Executor executor) { 207 super(); 208 mExecutor = executor; 209 } 210 211 private final IImsRegistration mBinder = new IImsRegistration.Stub() { 212 213 @Override 214 public @ImsRegistrationTech int getRegistrationTechnology() throws RemoteException { 215 return executeMethodAsyncForResult(() -> (mRegistrationAttributes == null) 216 ? REGISTRATION_TECH_NONE : mRegistrationAttributes.getRegistrationTechnology(), 217 "getRegistrationTechnology"); 218 } 219 220 @Override 221 public void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { 222 AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); 223 executeMethodAsync(() -> { 224 try { 225 ImsRegistrationImplBase.this.addRegistrationCallback(c); 226 } catch (RemoteException e) { 227 exceptionRef.set(e); 228 } 229 }, "addRegistrationCallback"); 230 231 if (exceptionRef.get() != null) { 232 throw exceptionRef.get(); 233 } 234 } 235 236 @Override 237 public void addEmergencyRegistrationCallback(IImsRegistrationCallback c) 238 throws RemoteException { 239 AtomicReference<RemoteException> exceptionRef = new AtomicReference<>(); 240 executeMethodAsync(() -> { 241 try { 242 ImsRegistrationImplBase.this.addEmergencyRegistrationCallback(c); 243 } catch (RemoteException e) { 244 exceptionRef.set(e); 245 } 246 }, "addEmergencyRegistrationCallback"); 247 248 if (exceptionRef.get() != null) { 249 throw exceptionRef.get(); 250 } 251 } 252 253 @Override 254 public void removeEmergencyRegistrationCallback(IImsRegistrationCallback c) 255 throws RemoteException { 256 executeMethodAsync(() -> 257 ImsRegistrationImplBase.this.removeEmergencyRegistrationCallback(c), 258 "removeEmergencyRegistrationCallback"); 259 } 260 261 @Override 262 public void removeRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { 263 executeMethodAsync(() -> ImsRegistrationImplBase.this.removeRegistrationCallback(c), 264 "removeRegistrationCallback"); 265 } 266 267 @Override 268 public void triggerFullNetworkRegistration(int sipCode, String sipReason) { 269 executeMethodAsyncNoException(() -> ImsRegistrationImplBase.this 270 .triggerFullNetworkRegistration(sipCode, sipReason), 271 "triggerFullNetworkRegistration"); 272 } 273 274 @Override 275 public void triggerUpdateSipDelegateRegistration() { 276 executeMethodAsyncNoException(() -> ImsRegistrationImplBase.this 277 .updateSipDelegateRegistration(), "triggerUpdateSipDelegateRegistration"); 278 } 279 280 @Override 281 public void triggerSipDelegateDeregistration() { 282 executeMethodAsyncNoException(() -> ImsRegistrationImplBase.this 283 .triggerSipDelegateDeregistration(), "triggerSipDelegateDeregistration"); 284 } 285 286 @Override 287 public void triggerDeregistration(@ImsDeregistrationReason int reason) { 288 executeMethodAsyncNoException(() -> ImsRegistrationImplBase.this 289 .triggerDeregistration(reason), "triggerDeregistration"); 290 } 291 292 // Call the methods with a clean calling identity on the executor and wait indefinitely for 293 // the future to return. 294 private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException { 295 try { 296 CompletableFuture.runAsync( 297 () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join(); 298 } catch (CancellationException | CompletionException e) { 299 Log.w(LOG_TAG, "ImsRegistrationImplBase Binder - " + errorLogName + " exception: " 300 + e.getMessage()); 301 throw new RemoteException(e.getMessage()); 302 } 303 } 304 305 private void executeMethodAsyncNoException(Runnable r, String errorLogName) { 306 try { 307 CompletableFuture.runAsync( 308 () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join(); 309 } catch (CancellationException | CompletionException e) { 310 Log.w(LOG_TAG, "ImsRegistrationImplBase Binder - " + errorLogName + " exception: " 311 + e.getMessage()); 312 } 313 } 314 315 private <T> T executeMethodAsyncForResult(Supplier<T> r, 316 String errorLogName) throws RemoteException { 317 CompletableFuture<T> future = CompletableFuture.supplyAsync( 318 () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor); 319 try { 320 return future.get(); 321 } catch (ExecutionException | InterruptedException e) { 322 Log.w(LOG_TAG, "ImsRegistrationImplBase Binder - " + errorLogName + " exception: " 323 + e.getMessage()); 324 throw new RemoteException(e.getMessage()); 325 } 326 } 327 }; 328 329 private final RemoteCallbackListExt<IImsRegistrationCallback> mCallbacks = 330 new RemoteCallbackListExt<>(); 331 private final RemoteCallbackListExt<IImsRegistrationCallback> mEmergencyCallbacks = 332 new RemoteCallbackListExt<>(); 333 private final Object mLock = new Object(); 334 // Locked on mLock 335 private ImsRegistrationAttributes mRegistrationAttributes; 336 private ImsRegistrationAttributes mEmergencyRegistrationAttributes; 337 // Locked on mLock 338 private int mRegistrationState = REGISTRATION_STATE_UNKNOWN; 339 private int mEmergencyRegistrationState = REGISTRATION_STATE_UNKNOWN; 340 // Locked on mLock, create unspecified disconnect cause. 341 private ImsReasonInfo mLastDisconnectCause = new ImsReasonInfo(); 342 private ImsReasonInfo mEmergencyLastDisconnectCause = new ImsReasonInfo(); 343 // Locked on mLock 344 private int mLastDisconnectSuggestedAction = RegistrationManager.SUGGESTED_ACTION_NONE; 345 private int mEmergencyLastDisconnectSuggestedAction = RegistrationManager.SUGGESTED_ACTION_NONE; 346 private int mLastDisconnectRadioTech = REGISTRATION_TECH_NONE; 347 private int mEmergencyLastDisconnectRadioTech = REGISTRATION_TECH_NONE; 348 349 // We hold onto the uris each time they change so that we can send it to a callback when its 350 // first added. 351 private Uri[] mUris = new Uri[0]; 352 private boolean mUrisSet = false; 353 354 /** 355 * @hide 356 */ getBinder()357 public final IImsRegistration getBinder() { 358 return mBinder; 359 } 360 addRegistrationCallback(IImsRegistrationCallback c)361 private void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { 362 // This is purposefully not synchronized with broadcastToCallbacksLocked because the 363 // list of callbacks to notify is copied over from the original list modified here. I also 364 // do not want to risk introducing a deadlock by using the same mCallbacks Object to 365 // synchronize on outgoing and incoming operations. 366 mCallbacks.register(c); 367 updateNewCallbackWithState(c, false); 368 } 369 removeRegistrationCallback(IImsRegistrationCallback c)370 private void removeRegistrationCallback(IImsRegistrationCallback c) { 371 // This is purposefully not synchronized with broadcastToCallbacksLocked because the 372 // list of callbacks to notify is copied over from the original list modified here. I also 373 // do not want to risk introducing a deadlock by using the same mCallbacks Object to 374 // synchronize on outgoing and incoming operations. 375 mCallbacks.unregister(c); 376 } 377 addEmergencyRegistrationCallback(IImsRegistrationCallback c)378 private void addEmergencyRegistrationCallback(IImsRegistrationCallback c) 379 throws RemoteException { 380 mEmergencyCallbacks.register(c); 381 updateNewCallbackWithState(c, true); 382 } 383 removeEmergencyRegistrationCallback(IImsRegistrationCallback c)384 private void removeEmergencyRegistrationCallback(IImsRegistrationCallback c) { 385 mEmergencyCallbacks.unregister(c); 386 } 387 388 /** 389 * Called by the framework to request that the ImsService perform the network registration 390 * of all SIP delegates associated with this ImsService. 391 * <p> 392 * If the SIP delegate feature tag configuration has changed, then this method will be 393 * called in order to let the ImsService know that it can pick up these changes in the IMS 394 * registration. 395 * @hide This API is not part of the Android public SDK API 396 */ 397 @SystemApi updateSipDelegateRegistration()398 public void updateSipDelegateRegistration() { 399 // Stub implementation, ImsService should implement this 400 } 401 402 403 /** 404 * Called by the framework to request that the ImsService perform the network deregistration of 405 * all SIP delegates associated with this ImsService. 406 * <p> 407 * This is typically called in situations where the user has changed the configuration of the 408 * device (for example, the default messaging application) and the framework is reconfiguring 409 * the tags associated with each IMS application. 410 * <p> 411 * This should not affect the registration of features managed by the ImsService itself, such as 412 * feature tags related to MMTEL registration. 413 * @hide This API is not part of the Android public SDK API 414 */ 415 @SystemApi triggerSipDelegateDeregistration()416 public void triggerSipDelegateDeregistration() { 417 // Stub implementation, ImsService should implement this 418 } 419 420 /** 421 * Called by the framework to notify the ImsService that a SIP delegate connection has received 422 * a SIP message containing a permanent failure response (such as a 403) or an indication that a 423 * SIP response timer has timed out in response to an outgoing SIP message. This method will be 424 * called when this condition occurs to trigger the ImsService to tear down the full IMS 425 * registration and re-register again. 426 * 427 * @param sipCode The SIP error code that represents a permanent failure that was received in 428 * response to a request generated by the IMS application. See RFC3261 7.2 for the general 429 * classes of responses available here, however the codes that generate this condition may 430 * be carrier specific. 431 * @param sipReason The reason associated with the SIP error code. {@code null} if there was no 432 * reason associated with the error. 433 * @hide This API is not part of the Android public SDK API 434 */ 435 @SystemApi triggerFullNetworkRegistration(@ntRangefrom = 100, to = 699) int sipCode, @Nullable String sipReason)436 public void triggerFullNetworkRegistration(@IntRange(from = 100, to = 699) int sipCode, 437 @Nullable String sipReason) { 438 // Stub implementation, ImsService should implement this 439 } 440 441 /** 442 * Requests IMS stack to perform graceful IMS deregistration before radio performing 443 * network detach in the events of SIM remove, refresh or and so on. The radio waits for 444 * the IMS deregistration, which will be notified by telephony via 445 * {@link android.hardware.radio.ims.IRadioIms#updateImsRegistrationInfo()}, 446 * or a certain timeout interval to start the network detach procedure. 447 * 448 * @param reason the reason why the deregistration is triggered. 449 * @hide 450 */ triggerDeregistration(@msDeregistrationReason int reason)451 public void triggerDeregistration(@ImsDeregistrationReason int reason) { 452 // Stub Implementation, can be overridden by ImsService 453 } 454 455 /** 456 * Notify the framework that the device is connected to the IMS network. 457 * 458 * @param imsRadioTech the radio access technology. 459 * @hide This API is not part of the Android public SDK API 460 */ 461 @SystemApi onRegistered(@msRegistrationTech int imsRadioTech)462 public final void onRegistered(@ImsRegistrationTech int imsRadioTech) { 463 onRegistered(new ImsRegistrationAttributes.Builder(imsRadioTech).build()); 464 } 465 466 /** 467 * Notify the framework that the device is connected to the IMS network. 468 * 469 * @param attributes The attributes associated with the IMS registration. 470 * @hide This API is not part of the Android public SDK API 471 */ 472 @SystemApi onRegistered(@onNull ImsRegistrationAttributes attributes)473 public final void onRegistered(@NonNull ImsRegistrationAttributes attributes) { 474 boolean isEmergency = isEmergency(attributes); 475 if (isEmergency) { 476 updateToEmergencyState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERED); 477 } else { 478 updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERED); 479 } 480 broadcastToCallbacksLocked((c) -> { 481 try { 482 c.onRegistered(attributes); 483 } catch (RemoteException e) { 484 Log.w(LOG_TAG, e + "onRegistered(int, Set) - Skipping callback."); 485 } 486 }, isEmergency); 487 } 488 489 /** 490 * Notify the framework that the device is trying to connect the IMS network. 491 * 492 * @param imsRadioTech the radio access technology. 493 * @hide This API is not part of the Android public SDK API 494 */ 495 @SystemApi onRegistering(@msRegistrationTech int imsRadioTech)496 public final void onRegistering(@ImsRegistrationTech int imsRadioTech) { 497 onRegistering(new ImsRegistrationAttributes.Builder(imsRadioTech).build()); 498 } 499 500 /** 501 * Notify the framework that the device is trying to connect the IMS network. 502 * 503 * @param attributes The attributes associated with the IMS registration. 504 * @hide This API is not part of the Android public SDK API 505 */ 506 @SystemApi onRegistering(@onNull ImsRegistrationAttributes attributes)507 public final void onRegistering(@NonNull ImsRegistrationAttributes attributes) { 508 boolean isEmergency = isEmergency(attributes); 509 if (isEmergency) { 510 updateToEmergencyState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERING); 511 } else { 512 updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERING); 513 } 514 broadcastToCallbacksLocked((c) -> { 515 try { 516 c.onRegistering(attributes); 517 } catch (RemoteException e) { 518 Log.w(LOG_TAG, e + "onRegistering(int, Set) - Skipping callback."); 519 } 520 }, isEmergency); 521 } 522 523 /** 524 * Notify the framework that the device is disconnected from the IMS network. 525 * <p> 526 * Note: Prior to calling {@link #onDeregistered(ImsReasonInfo)}, you should ensure that any 527 * changes to {@link android.telephony.ims.feature.ImsFeature} capability availability is sent 528 * to the framework. For example, 529 * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO} 530 * and 531 * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE} 532 * may be set to unavailable to ensure the framework knows these services are no longer 533 * available due to de-registration. If you do not report capability changes impacted by 534 * de-registration, the framework will not know which features are no longer available as a 535 * result. 536 * 537 * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. 538 * @hide This API is not part of the Android public SDK API 539 */ 540 @SystemApi onDeregistered(ImsReasonInfo info)541 public final void onDeregistered(ImsReasonInfo info) { 542 // Default impl to keep backwards compatibility with old implementations 543 onDeregistered(info, RegistrationManager.SUGGESTED_ACTION_NONE, REGISTRATION_TECH_NONE); 544 } 545 546 /** 547 * Notify the framework that the device is disconnected from the IMS network. 548 * <p> 549 * Note: Prior to calling {@link #onDeregistered(ImsReasonInfo,int)}, you should ensure that any 550 * changes to {@link android.telephony.ims.feature.ImsFeature} capability availability is sent 551 * to the framework. For example, 552 * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO} 553 * and 554 * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE} 555 * may be set to unavailable to ensure the framework knows these services are no longer 556 * available due to de-registration. If you do not report capability changes impacted by 557 * de-registration, the framework will not know which features are no longer available as a 558 * result. 559 * 560 * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. 561 * @param suggestedAction the expected behavior of radio protocol stack. 562 * @param imsRadioTech the network type on which IMS registration has failed. 563 * @hide This API is not part of the Android public SDK API 564 */ 565 @SystemApi onDeregistered(@ullable ImsReasonInfo info, @RegistrationManager.SuggestedAction int suggestedAction, @ImsRegistrationTech int imsRadioTech)566 public final void onDeregistered(@Nullable ImsReasonInfo info, 567 @RegistrationManager.SuggestedAction int suggestedAction, 568 @ImsRegistrationTech int imsRadioTech) { 569 // Impl to keep backwards compatibility with old implementations 570 ImsRegistrationAttributes attributes = mRegistrationAttributes != null 571 ? new ImsRegistrationAttributes(imsRadioTech, 572 mRegistrationAttributes.getTransportType(), 573 mRegistrationAttributes.getAttributeFlags(), 574 mRegistrationAttributes.getFeatureTags()) : 575 new ImsRegistrationAttributes.Builder(imsRadioTech).build(); 576 onDeregistered(info, suggestedAction, attributes); 577 } 578 579 /** 580 * Notify the framework that the device is disconnected from the IMS network. 581 * <p> 582 * Note: Prior to calling {@link #onDeregistered(ImsReasonInfo,int)}, you should ensure that any 583 * changes to {@link android.telephony.ims.feature.ImsFeature} capability availability is sent 584 * to the framework. For example, 585 * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO} 586 * and 587 * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE} 588 * may be set to unavailable to ensure the framework knows these services are no longer 589 * available due to de-registration. If you do not report capability changes impacted by 590 * de-registration, the framework will not know which features are no longer available as a 591 * result. 592 * 593 * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. 594 * @param suggestedAction the expected behavior of radio protocol stack. 595 * @param attributes The attributes associated with the IMS registration 596 * @hide This API is not part of the Android public SDK API 597 */ 598 @SystemApi 599 @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE) onDeregistered(@ullable ImsReasonInfo info, @RegistrationManager.SuggestedAction int suggestedAction, @NonNull ImsRegistrationAttributes attributes)600 public final void onDeregistered(@Nullable ImsReasonInfo info, 601 @RegistrationManager.SuggestedAction int suggestedAction, 602 @NonNull ImsRegistrationAttributes attributes) { 603 boolean isEmergency = isEmergency(attributes); 604 int imsRadioTech = attributes.getRegistrationTechnology(); 605 if (isEmergency) { 606 updateToDisconnectedEmergencyState(info, suggestedAction, imsRadioTech); 607 } else { 608 updateToDisconnectedState(info, suggestedAction, imsRadioTech); 609 } 610 // ImsReasonInfo should never be null. 611 final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo(); 612 613 broadcastToCallbacksLocked((c) -> { 614 try { 615 c.onDeregistered(reasonInfo, suggestedAction, imsRadioTech); 616 } catch (RemoteException e) { 617 Log.w(LOG_TAG, e + "onDeregistered() - Skipping callback."); 618 } 619 }, isEmergency); 620 } 621 622 /** 623 * Notify the framework that the device is disconnected from the IMS network. 624 * <p> 625 * Note: Before calling {@link #onDeregistered(ImsReasonInfo, SipDetails)}, ImsService should 626 * ensure that any changes to {@link android.telephony.ims.feature.ImsFeature} capability 627 * availability is sent to the framework. 628 * For example, 629 * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO} 630 * and 631 * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE} 632 * may be set to unavailable to ensure the framework knows these services are no longer 633 * available due to de-registration. If ImsService do not report capability changes impacted 634 * by de-registration, the framework will not know which features are no longer available as a 635 * result. 636 * 637 * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. 638 * @param details the {@link SipDetails} related to disconnected Ims registration 639 * @hide This API is not part of the Android public SDK API 640 */ 641 @SystemApi onDeregistered(@ullable ImsReasonInfo info, @NonNull SipDetails details)642 public final void onDeregistered(@Nullable ImsReasonInfo info, 643 @NonNull SipDetails details) { 644 onDeregistered(info, RegistrationManager.SUGGESTED_ACTION_NONE, REGISTRATION_TECH_NONE, 645 details); 646 } 647 648 /** 649 * Notify the framework that the device is disconnected from the IMS network. 650 * <p> 651 * Note: Before calling {@link #onDeregistered(ImsReasonInfo, SipDetails)}, ImsService should 652 * ensure that any changes to {@link android.telephony.ims.feature.ImsFeature} capability 653 * availability is sent to the framework. 654 * For example, 655 * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO} 656 * and 657 * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE} 658 * may be set to unavailable to ensure the framework knows these services are no longer 659 * available due to de-registration. If ImsService do not report capability changes impacted 660 * by de-registration, the framework will not know which features are no longer available as a 661 * result. 662 * 663 * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. 664 * @param suggestedAction the expected behavior of radio protocol stack. 665 * @param imsRadioTech the network type on which IMS registration has failed. 666 * @param details the {@link SipDetails} related to disconnected Ims registration 667 * @hide This API is not part of the Android public SDK API 668 */ 669 @SystemApi onDeregistered(@ullable ImsReasonInfo info, @RegistrationManager.SuggestedAction int suggestedAction, @ImsRegistrationTech int imsRadioTech, @NonNull SipDetails details)670 public final void onDeregistered(@Nullable ImsReasonInfo info, 671 @RegistrationManager.SuggestedAction int suggestedAction, 672 @ImsRegistrationTech int imsRadioTech, @NonNull SipDetails details) { 673 updateToDisconnectedState(info, suggestedAction, imsRadioTech); 674 // ImsReasonInfo should never be null. 675 final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo(); 676 broadcastToCallbacksLocked((c) -> { 677 try { 678 c.onDeregisteredWithDetails(reasonInfo, suggestedAction, imsRadioTech, details); 679 } catch (RemoteException e) { 680 Log.w(LOG_TAG, e + "onDeregistered() - Skipping callback."); 681 } 682 }, false); 683 } 684 685 /** 686 * Notify the framework that the handover from the current radio technology to the technology 687 * defined in {@code imsRadioTech} has failed. 688 * @param imsRadioTech The technology that has failed to be changed. Valid values are 689 * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and 690 * {@link #REGISTRATION_TECH_CROSS_SIM}. 691 * @param info The {@link ImsReasonInfo} for the failure to change technology. 692 * @hide This API is not part of the Android public SDK API 693 */ 694 @SystemApi onTechnologyChangeFailed(@msRegistrationTech int imsRadioTech, ImsReasonInfo info)695 public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech, 696 ImsReasonInfo info) { 697 ImsRegistrationAttributes attributes = mRegistrationAttributes != null 698 ? new ImsRegistrationAttributes(imsRadioTech, 699 mRegistrationAttributes.getTransportType(), 700 mRegistrationAttributes.getAttributeFlags(), 701 mRegistrationAttributes.getFeatureTags()) : 702 new ImsRegistrationAttributes.Builder(imsRadioTech).build(); 703 onTechnologyChangeFailed(info, attributes); 704 } 705 706 /** 707 * Notify the framework that the handover from the current radio technology to the technology 708 * defined in {@code imsRadioTech} has failed. 709 * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and 710 * {@link #REGISTRATION_TECH_CROSS_SIM}. 711 * @param info The {@link ImsReasonInfo} for the failure to change technology. 712 * @param attributes The attributes associated with the IMS registration 713 * @hide This API is not part of the Android public SDK API 714 */ 715 @SystemApi 716 @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE) onTechnologyChangeFailed(@ullable ImsReasonInfo info, @NonNull ImsRegistrationAttributes attributes)717 public final void onTechnologyChangeFailed(@Nullable ImsReasonInfo info, 718 @NonNull ImsRegistrationAttributes attributes) { 719 boolean isEmergency = isEmergency(attributes); 720 int imsRadioTech = attributes.getRegistrationTechnology(); 721 final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo(); 722 broadcastToCallbacksLocked(c -> { 723 try { 724 c.onTechnologyChangeFailed(imsRadioTech, reasonInfo); 725 } catch (RemoteException e) { 726 Log.w(LOG_TAG, e + "onTechnologyChangeFailed() - Skipping callback."); 727 } 728 }, isEmergency); 729 } 730 731 /** 732 * Invoked when the {@link Uri}s associated to this device's subscriber have changed. 733 * These {@link Uri}s' are filtered out during conference calls. 734 * 735 * The {@link Uri}s are not guaranteed to be different between subsequent calls. 736 * @param uris changed uris 737 * @hide This API is not part of the Android public SDK API 738 */ 739 @SystemApi onSubscriberAssociatedUriChanged(Uri[] uris)740 public final void onSubscriberAssociatedUriChanged(Uri[] uris) { 741 synchronized (mLock) { 742 mUris = ArrayUtils.cloneOrNull(uris); 743 mUrisSet = true; 744 } 745 broadcastToCallbacksLocked((c) -> onSubscriberAssociatedUriChanged(c, uris), false); 746 } 747 isEmergency(ImsRegistrationAttributes attributes)748 private boolean isEmergency(ImsRegistrationAttributes attributes) { 749 if (attributes == null) { 750 return false; 751 } else { 752 return (attributes.getAttributeFlags() 753 & ImsRegistrationAttributes.ATTR_REGISTRATION_TYPE_EMERGENCY) != 0; 754 } 755 } 756 757 /** 758 * Broadcast the specified operation in ta synchronized manner so that multiple threads do not 759 * try to call broadcast at the same time, which will generate an error. 760 * @param c The Consumer lambda method containing the callback to call. 761 */ broadcastToCallbacksLocked(Consumer<IImsRegistrationCallback> c, boolean isEmergency)762 private void broadcastToCallbacksLocked(Consumer<IImsRegistrationCallback> c, 763 boolean isEmergency) { 764 // One broadcast can happen at a time, so synchronize threads so only one 765 // beginBroadcast/endBroadcast happens at a time. 766 if (isEmergency) { 767 synchronized (mEmergencyCallbacks) { 768 mEmergencyCallbacks.broadcastAction(c); 769 } 770 } else { 771 synchronized (mCallbacks) { 772 mCallbacks.broadcastAction(c); 773 } 774 } 775 } 776 onSubscriberAssociatedUriChanged(IImsRegistrationCallback callback, Uri[] uris)777 private void onSubscriberAssociatedUriChanged(IImsRegistrationCallback callback, Uri[] uris) { 778 try { 779 callback.onSubscriberAssociatedUriChanged(uris); 780 } catch (RemoteException e) { 781 Log.w(LOG_TAG, e + "onSubscriberAssociatedUriChanged() - Skipping callback."); 782 } 783 } 784 updateToState(ImsRegistrationAttributes attributes, int newState)785 private void updateToState(ImsRegistrationAttributes attributes, int newState) { 786 synchronized (mLock) { 787 mRegistrationAttributes = attributes; 788 mRegistrationState = newState; 789 mLastDisconnectCause = null; 790 mLastDisconnectSuggestedAction = RegistrationManager.SUGGESTED_ACTION_NONE; 791 mLastDisconnectRadioTech = REGISTRATION_TECH_NONE; 792 } 793 } 794 updateToEmergencyState(ImsRegistrationAttributes attributes, int newState)795 private void updateToEmergencyState(ImsRegistrationAttributes attributes, int newState) { 796 synchronized (mLock) { 797 mEmergencyRegistrationAttributes = attributes; 798 mEmergencyRegistrationState = newState; 799 mEmergencyLastDisconnectCause = null; 800 mEmergencyLastDisconnectSuggestedAction = RegistrationManager.SUGGESTED_ACTION_NONE; 801 mEmergencyLastDisconnectRadioTech = REGISTRATION_TECH_NONE; 802 } 803 } 804 updateToDisconnectedState(ImsReasonInfo info, @RegistrationManager.SuggestedAction int suggestedAction, @ImsRegistrationTech int imsRadioTech)805 private void updateToDisconnectedState(ImsReasonInfo info, 806 @RegistrationManager.SuggestedAction int suggestedAction, 807 @ImsRegistrationTech int imsRadioTech) { 808 synchronized (mLock) { 809 //We don't want to send this info over if we are disconnected 810 mUrisSet = false; 811 mUris = null; 812 813 updateToState(new ImsRegistrationAttributes.Builder(REGISTRATION_TECH_NONE).build(), 814 RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED); 815 if (info != null) { 816 mLastDisconnectCause = info; 817 mLastDisconnectSuggestedAction = suggestedAction; 818 mLastDisconnectRadioTech = imsRadioTech; 819 } else { 820 Log.w(LOG_TAG, "updateToDisconnectedState: no ImsReasonInfo provided."); 821 mLastDisconnectCause = new ImsReasonInfo(); 822 } 823 } 824 } 825 updateToDisconnectedEmergencyState(ImsReasonInfo info, @RegistrationManager.SuggestedAction int suggestedAction, @ImsRegistrationTech int imsRadioTech)826 private void updateToDisconnectedEmergencyState(ImsReasonInfo info, 827 @RegistrationManager.SuggestedAction int suggestedAction, 828 @ImsRegistrationTech int imsRadioTech) { 829 synchronized (mLock) { 830 //We don't want to send this info over if we are disconnected 831 mUrisSet = false; 832 mUris = null; 833 834 updateToEmergencyState(new ImsRegistrationAttributes.Builder(REGISTRATION_TECH_NONE) 835 .build(), 836 RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED); 837 if (info != null) { 838 mEmergencyLastDisconnectCause = info; 839 mEmergencyLastDisconnectSuggestedAction = suggestedAction; 840 mEmergencyLastDisconnectRadioTech = imsRadioTech; 841 } else { 842 Log.w(LOG_TAG, "updateToDisconnectedState: no ImsReasonInfo provided."); 843 mEmergencyLastDisconnectCause = new ImsReasonInfo(); 844 } 845 } 846 } 847 848 /** 849 * @param c the newly registered callback that will be updated with the current registration 850 * state. 851 */ updateNewCallbackWithState(IImsRegistrationCallback c, boolean isEmergencyCallback)852 private void updateNewCallbackWithState(IImsRegistrationCallback c, boolean isEmergencyCallback) 853 throws RemoteException { 854 int state; 855 ImsRegistrationAttributes attributes; 856 ImsReasonInfo disconnectInfo; 857 int suggestedAction; 858 int imsDisconnectRadioTech; 859 boolean urisSet; 860 Uri[] uris; 861 synchronized (mLock) { 862 state = isEmergencyCallback ? mEmergencyRegistrationState : mRegistrationState; 863 attributes = isEmergencyCallback ? mEmergencyRegistrationAttributes : 864 mRegistrationAttributes; 865 disconnectInfo = isEmergencyCallback ? mEmergencyLastDisconnectCause : 866 mLastDisconnectCause; 867 suggestedAction = isEmergencyCallback ? mEmergencyLastDisconnectSuggestedAction : 868 mLastDisconnectSuggestedAction; 869 imsDisconnectRadioTech = isEmergencyCallback ? mEmergencyLastDisconnectRadioTech : 870 mLastDisconnectRadioTech; 871 urisSet = mUrisSet; 872 uris = mUris; 873 } 874 switch (state) { 875 case RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED: { 876 c.onDeregistered(disconnectInfo, suggestedAction, imsDisconnectRadioTech); 877 break; 878 } 879 case RegistrationManager.REGISTRATION_STATE_REGISTERING: { 880 c.onRegistering(attributes); 881 break; 882 } 883 case RegistrationManager.REGISTRATION_STATE_REGISTERED: { 884 c.onRegistered(attributes); 885 break; 886 } 887 case REGISTRATION_STATE_UNKNOWN: { 888 // Do not callback if the state has not been updated yet by the ImsService. 889 break; 890 } 891 } 892 if (urisSet) { 893 onSubscriberAssociatedUriChanged(c, uris); 894 } 895 } 896 897 /** 898 * Set default Executor from ImsService. 899 * @param executor The default executor for the framework to use when executing the methods 900 * overridden by the implementation of Registration. 901 * @hide 902 */ setDefaultExecutor(@onNull Executor executor)903 public final void setDefaultExecutor(@NonNull Executor executor) { 904 if (mExecutor == null) { 905 mExecutor = executor; 906 } 907 } 908 909 /** 910 * Clear the cached data when the subscription is no longer valid 911 * such as when a sim is removed. 912 * @hide 913 */ clearRegistrationCache()914 public final void clearRegistrationCache() { 915 synchronized (mLock) { 916 mUris = null; 917 mUrisSet = false; 918 } 919 } 920 } 921