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