1 /* 2 * Copyright (C) 2020 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 com.android.services.telephony.rcs; 18 19 import android.app.role.OnRoleHoldersChangedListener; 20 import android.app.role.RoleManager; 21 import android.content.Context; 22 import android.os.PersistableBundle; 23 import android.os.RemoteCallbackList; 24 import android.os.RemoteException; 25 import android.os.UserHandle; 26 import android.telephony.CarrierConfigManager; 27 import android.telephony.ims.DelegateRequest; 28 import android.telephony.ims.FeatureTagState; 29 import android.telephony.ims.ImsException; 30 import android.telephony.ims.ImsService; 31 import android.telephony.ims.SipDelegateManager; 32 import android.telephony.ims.SipDialogState; 33 import android.telephony.ims.aidl.IImsRegistration; 34 import android.telephony.ims.aidl.ISipDelegate; 35 import android.telephony.ims.aidl.ISipDelegateConnectionStateCallback; 36 import android.telephony.ims.aidl.ISipDelegateMessageCallback; 37 import android.telephony.ims.aidl.ISipTransport; 38 import android.telephony.ims.stub.DelegateConnectionMessageCallback; 39 import android.telephony.ims.stub.DelegateConnectionStateCallback; 40 import android.telephony.ims.stub.SipDelegate; 41 import android.text.TextUtils; 42 import android.util.ArraySet; 43 import android.util.LocalLog; 44 import android.util.Log; 45 46 import androidx.annotation.NonNull; 47 48 import com.android.ims.RcsFeatureManager; 49 import com.android.internal.annotations.VisibleForTesting; 50 import com.android.internal.telephony.ISipDialogStateCallback; 51 import com.android.internal.telephony.util.RemoteCallbackListExt; 52 import com.android.internal.util.IndentingPrintWriter; 53 import com.android.phone.RcsProvisioningMonitor; 54 55 import com.google.common.base.Objects; 56 57 import java.io.PrintWriter; 58 import java.util.ArrayList; 59 import java.util.Collections; 60 import java.util.HashMap; 61 import java.util.Iterator; 62 import java.util.List; 63 import java.util.Locale; 64 import java.util.Map; 65 import java.util.Set; 66 import java.util.concurrent.Callable; 67 import java.util.concurrent.CompletableFuture; 68 import java.util.concurrent.ExecutionException; 69 import java.util.concurrent.Executor; 70 import java.util.concurrent.Executors; 71 import java.util.concurrent.Future; 72 import java.util.concurrent.ScheduledExecutorService; 73 import java.util.concurrent.ScheduledFuture; 74 import java.util.concurrent.TimeUnit; 75 import java.util.function.Consumer; 76 import java.util.stream.Collectors; 77 78 /** 79 * Manages the creation and destruction of SipDelegates in response to an IMS application requesting 80 * a SipDelegateConnection registered to one or more IMS feature tags. 81 * <p> 82 * This allows an IMS application to forward traffic related to those feature tags over the existing 83 * IMS registration managed by the {@link ImsService} associated with this cellular subscription 84 * instead of requiring that the IMS application manage its own IMS registration over-the-top. This 85 * is required for some cellular carriers, which mandate that all IMS SIP traffic must be sent 86 * through a single IMS registration managed by the system IMS service. 87 * 88 * //TODO: Support other roles besides SMS 89 * //TODO: Bring in carrier provisioning to influence features that can be created. 90 * //TODO: Generate registration change events. 91 */ 92 public class SipTransportController implements RcsFeatureController.Feature, 93 OnRoleHoldersChangedListener { 94 public static final String LOG_TAG = "SipTransportC"; 95 static final int LOG_SIZE = 50; 96 97 /**See {@link TimerAdapter#getReevaluateThrottleTimerMilliseconds()}.*/ 98 private static final int REEVALUATE_THROTTLE_DEFAULT_MS = 1000; 99 /**See {@link TimerAdapter#getUpdateRegistrationDelayMilliseconds()}.*/ 100 private static final int TRIGGER_UPDATE_REGISTRATION_DELAY_DEFAULT_MS = 1000; 101 102 /** 103 * {@link RoleManager} is final so we have to wrap the implementation for testing. 104 */ 105 @VisibleForTesting 106 public interface RoleManagerAdapter { 107 /** See {@link RoleManager#getRoleHolders(String)} */ getRoleHolders(String roleName)108 List<String> getRoleHolders(String roleName); 109 /** See {@link RoleManager#addOnRoleHoldersChangedListenerAsUser} */ addOnRoleHoldersChangedListenerAsUser(Executor executor, OnRoleHoldersChangedListener listener, UserHandle user)110 void addOnRoleHoldersChangedListenerAsUser(Executor executor, 111 OnRoleHoldersChangedListener listener, UserHandle user); 112 /** See {@link RoleManager#removeOnRoleHoldersChangedListenerAsUser} */ removeOnRoleHoldersChangedListenerAsUser(OnRoleHoldersChangedListener listener, UserHandle user)113 void removeOnRoleHoldersChangedListenerAsUser(OnRoleHoldersChangedListener listener, 114 UserHandle user); 115 } 116 117 /** 118 * Adapter for timers related to this class so they can be modified during testing. 119 */ 120 @VisibleForTesting 121 public interface TimerAdapter { 122 /** 123 * Time we will delay after a {@link #createSipDelegate} or {@link #destroySipDelegate} 124 * command to re-evaluate and apply any changes to the list of tracked 125 * SipDelegateControllers. 126 * <p> 127 * Another create/destroy request sent during this time will not postpone re-evaluation 128 * again. 129 */ getReevaluateThrottleTimerMilliseconds()130 int getReevaluateThrottleTimerMilliseconds(); 131 132 /** 133 * Time after re-evaluate we will wait to trigger the update of IMS registration. 134 * <p> 135 * Another re-evaluate while waiting to trigger a registration update will cause this 136 * controller to cancel and reschedule the event again, further delaying the trigger to send 137 * a registration update. 138 */ getUpdateRegistrationDelayMilliseconds()139 int getUpdateRegistrationDelayMilliseconds(); 140 } 141 142 private static class TimerAdapterImpl implements TimerAdapter { 143 144 @Override getReevaluateThrottleTimerMilliseconds()145 public int getReevaluateThrottleTimerMilliseconds() { 146 return REEVALUATE_THROTTLE_DEFAULT_MS; 147 } 148 149 @Override getUpdateRegistrationDelayMilliseconds()150 public int getUpdateRegistrationDelayMilliseconds() { 151 return TRIGGER_UPDATE_REGISTRATION_DELAY_DEFAULT_MS; 152 } 153 } 154 155 private static class RoleManagerAdapterImpl implements RoleManagerAdapter { 156 157 private final RoleManager mRoleManager; 158 RoleManagerAdapterImpl(Context context)159 private RoleManagerAdapterImpl(Context context) { 160 mRoleManager = context.getSystemService(RoleManager.class); 161 } 162 163 @Override getRoleHolders(String roleName)164 public List<String> getRoleHolders(String roleName) { 165 return mRoleManager.getRoleHolders(roleName); 166 } 167 168 @Override addOnRoleHoldersChangedListenerAsUser(Executor executor, OnRoleHoldersChangedListener listener, UserHandle user)169 public void addOnRoleHoldersChangedListenerAsUser(Executor executor, 170 OnRoleHoldersChangedListener listener, UserHandle user) { 171 mRoleManager.addOnRoleHoldersChangedListenerAsUser(executor, listener, user); 172 } 173 174 @Override removeOnRoleHoldersChangedListenerAsUser(OnRoleHoldersChangedListener listener, UserHandle user)175 public void removeOnRoleHoldersChangedListenerAsUser(OnRoleHoldersChangedListener listener, 176 UserHandle user) { 177 mRoleManager.removeOnRoleHoldersChangedListenerAsUser(listener, user); 178 } 179 } 180 181 /** 182 * Extends RemoteCallbackList to track callbacks to the IMS applications with 183 * SipDelegateConnections and cleans them up if they die. 184 */ 185 private class TrackedAppBinders extends RemoteCallbackList<ISipDelegateMessageCallback> { 186 @Override onCallbackDied(ISipDelegateMessageCallback callback, Object cookie)187 public void onCallbackDied(ISipDelegateMessageCallback callback, Object cookie) { 188 mExecutorService.execute(() -> { 189 if (cookie instanceof SipDelegateController) { 190 SipDelegateController c = (SipDelegateController) cookie; 191 logi("onCallbackDied - Cleaning up delegates associated with package: " 192 + c.getPackageName()); 193 boolean isNewDestroyQueued = addPendingDestroy(c, 194 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD); 195 if (isNewDestroyQueued) { 196 CompletableFuture<Void> f = new CompletableFuture<>(); 197 scheduleReevaluateNow(f); 198 f.thenRun(() -> logi("onCallbackDied - clean up complete for package: " 199 + c.getPackageName())); 200 } 201 } else { 202 logw("onCallbackDied: unexpected - cookie is not an instance of " 203 + "SipDelegateController"); 204 } 205 }); 206 } 207 } 208 209 /** 210 * Used in {@link #destroySipDelegate(int, ISipDelegate, int)} to store pending destroy 211 * requests. 212 */ 213 private static final class DestroyRequest { 214 public final SipDelegateController controller; 215 public final int reason; 216 DestroyRequest(SipDelegateController c, int r)217 DestroyRequest(SipDelegateController c, int r) { 218 controller = c; 219 reason = r; 220 } 221 222 223 @Override equals(Object o)224 public boolean equals(Object o) { 225 if (this == o) return true; 226 if (o == null || getClass() != o.getClass()) return false; 227 DestroyRequest that = (DestroyRequest) o; 228 // Only use controller for comparison, as we want to only have one DestroyRequest active 229 // per controller. 230 return controller.equals(that.controller); 231 } 232 233 @Override hashCode()234 public int hashCode() { 235 // Only use controller for hash, as we want to only have one DestroyRequest active per 236 // controller. 237 return java.util.Objects.hash(controller); 238 } 239 240 @Override toString()241 public String toString() { 242 return "DestroyRequest{" + "controller=" + controller + ", reason=" + reason + '}'; 243 } 244 } 245 246 /** 247 * This is to handle with dialogs of all available delegates that have dialogs. 248 */ 249 private final class SipDialogsStateHandle implements SipDialogsStateListener { 250 251 private Executor mExecutor = Runnable::run; 252 Map<String, List<SipDialogState>> mMapDialogState = new HashMap<>(); 253 254 /** 255 * This will be called using the {@link SipDialogsStateListener} 256 * @param key This is the ID of the SipSessionTracker for handling the dialogs of 257 * each created delegates. 258 * @param dialogStates This is a list of dialog states tracked in SipSessionTracker. 259 */ 260 @Override reMappingSipDelegateState(String key, List<SipDialogState> dialogStates)261 public void reMappingSipDelegateState(String key, 262 List<SipDialogState> dialogStates) { 263 mExecutor.execute(()->processReMappingSipDelegateState(key, dialogStates)); 264 } 265 266 /** 267 * Notify SipDialogState information with 268 * {@link com.android.internal.telephony.ISipDialogStateCallback} 269 */ 270 @Override notifySipDialogState()271 public void notifySipDialogState() { 272 mExecutor.execute(()->processNotifySipDialogState()); 273 } 274 processReMappingSipDelegateState(String key, List<SipDialogState> dialogStates)275 private void processReMappingSipDelegateState(String key, 276 List<SipDialogState> dialogStates) { 277 if (dialogStates.isEmpty()) { 278 mMapDialogState.remove(key); 279 } else { 280 mMapDialogState.put(key, dialogStates); 281 } 282 notifySipDialogState(); 283 } 284 processNotifySipDialogState()285 private void processNotifySipDialogState() { 286 List<SipDialogState> finalDialogStates = new ArrayList<>(); 287 for (List<SipDialogState> d : mMapDialogState.values()) { 288 finalDialogStates.addAll(d); 289 } 290 291 if (mSipDialogStateCallbacks.getRegisteredCallbackCount() == 0) { 292 return; 293 } 294 mSipDialogStateCallbacks.broadcastAction((c) -> { 295 try { 296 c.onActiveSipDialogsChanged(finalDialogStates); 297 } catch (RemoteException e) { 298 Log.e(LOG_TAG, 299 "onActiveSipDialogsChanged() - Skipping callback." + e); 300 } 301 }); 302 } 303 } 304 305 /** 306 * Allow the ability for tests to easily mock out the SipDelegateController for testing. 307 */ 308 @VisibleForTesting 309 public interface SipDelegateControllerFactory { 310 /** See {@link SipDelegateController} */ create(int subId, int uid, DelegateRequest initialRequest, String packageName, ISipTransport sipTransportImpl, IImsRegistration registrationImpl, ScheduledExecutorService executorService, ISipDelegateConnectionStateCallback stateCallback, ISipDelegateMessageCallback messageCallback)311 SipDelegateController create(int subId, int uid, DelegateRequest initialRequest, 312 String packageName, ISipTransport sipTransportImpl, 313 IImsRegistration registrationImpl, ScheduledExecutorService executorService, 314 ISipDelegateConnectionStateCallback stateCallback, 315 ISipDelegateMessageCallback messageCallback); 316 } 317 318 private SipDelegateControllerFactory mDelegateControllerFactory = SipDelegateController::new; 319 private final int mSlotId; 320 private final ScheduledExecutorService mExecutorService; 321 private final RoleManagerAdapter mRoleManagerAdapter; 322 private final TimerAdapter mTimerAdapter; 323 private final LocalLog mLocalLog = new LocalLog(LOG_SIZE); 324 325 // A priority queue of active SipDelegateControllers, where the oldest SipDelegate gets 326 // access to the feature tag if multiple apps are allowed to request the same feature tag. 327 private final List<SipDelegateController> mDelegatePriorityQueue = new ArrayList<>(); 328 // SipDelegateControllers who have been created and are pending to be added to the priority 329 // queue. Will be added into the queue in the same order as they were added here. 330 private final List<SipDelegateController> mDelegatePendingCreate = new ArrayList<>(); 331 // SipDelegateControllers that are pending to be destroyed. 332 private final List<DestroyRequest> mDelegatePendingDestroy = new ArrayList<>(); 333 // SipDialogStateCallback that are adding to use callback. 334 private final RemoteCallbackListExt<ISipDialogStateCallback> mSipDialogStateCallbacks = 335 new RemoteCallbackListExt<>(); 336 // To listen the state information if the dialog status is changed from the SipSessionTracker. 337 private final SipDialogsStateListener mSipDialogsListener = new SipDialogsStateHandle(); 338 339 // Cache of Binders to remote IMS applications for tracking their potential death 340 private final TrackedAppBinders mActiveAppBinders = new TrackedAppBinders(); 341 342 // Future scheduled for operations that require the list of SipDelegateControllers to 343 // be evaluated. When the timer expires and triggers the reevaluate method, this controller 344 // will iterate through mDelegatePriorityQueue and assign Feature Tags based on role+priority. 345 private ScheduledFuture<?> mScheduledEvaluateFuture; 346 // mPendingEvaluateFTFuture creates this CompletableFuture, exposed in order to stop other 347 // evaluates from occurring while another is waiting for a result on other threads. 348 private CompletableFuture<Void> mEvaluateCompleteFuture; 349 // Future scheduled that will trigger the ImsService to update the IMS registration for the 350 // SipDelegate configuration. Will be scheduled TRIGGER_UPDATE_REGISTRATION_DELAY_MS 351 // milliseconds after a pending evaluate completes. 352 private ScheduledFuture<?> mPendingUpdateRegistrationFuture; 353 // Subscription id will change as new subscriptions are loaded on the slot. 354 private int mSubId; 355 // Will go up/down as the ImsService associated with this slotId goes up/down. 356 private RcsFeatureManager mRcsManager; 357 // Cached package name of the app that is considered the default SMS app. 358 private String mCachedSmsRolePackageName = ""; 359 // Callback to monitor rcs provisioning change 360 private CarrierConfigManager mCarrierConfigManager; 361 // Cached allowed feature tags from carrier config 362 private ArraySet<String> mFeatureTagsAllowed = new ArraySet<>(); 363 364 /** 365 * Create an instance of SipTransportController. 366 * @param context The Context associated with this controller. 367 * @param slotId The slot index associated with this controller. 368 * @param subId The subscription ID associated with this controller when it was first created. 369 */ SipTransportController(Context context, int slotId, int subId)370 public SipTransportController(Context context, int slotId, int subId) { 371 mSlotId = slotId; 372 mSubId = subId; 373 374 mRoleManagerAdapter = new RoleManagerAdapterImpl(context); 375 mTimerAdapter = new TimerAdapterImpl(); 376 mExecutorService = Executors.newSingleThreadScheduledExecutor(); 377 mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class); 378 } 379 380 /** 381 * Constructor to inject dependencies for testing. 382 */ 383 @VisibleForTesting SipTransportController(Context context, int slotId, int subId, SipDelegateControllerFactory delegateFactory, RoleManagerAdapter roleManagerAdapter, TimerAdapter timerAdapter, ScheduledExecutorService executor)384 public SipTransportController(Context context, int slotId, int subId, 385 SipDelegateControllerFactory delegateFactory, RoleManagerAdapter roleManagerAdapter, 386 TimerAdapter timerAdapter, ScheduledExecutorService executor) { 387 mSlotId = slotId; 388 mSubId = subId; 389 390 mRoleManagerAdapter = roleManagerAdapter; 391 mTimerAdapter = timerAdapter; 392 mDelegateControllerFactory = delegateFactory; 393 mExecutorService = executor; 394 mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class); 395 logi("created"); 396 } 397 398 @Override onRcsConnected(RcsFeatureManager manager)399 public void onRcsConnected(RcsFeatureManager manager) { 400 mExecutorService.submit(() -> onRcsManagerChanged(manager)); 401 } 402 403 @Override onRcsDisconnected()404 public void onRcsDisconnected() { 405 mExecutorService.submit(() -> onRcsManagerChanged(null)); 406 } 407 408 @Override onAssociatedSubscriptionUpdated(int subId)409 public void onAssociatedSubscriptionUpdated(int subId) { 410 mExecutorService.submit(()-> onSubIdChanged(subId)); 411 } 412 413 @Override onCarrierConfigChanged()414 public void onCarrierConfigChanged() { 415 mExecutorService.submit(this::onCarrierConfigChangedInternal); 416 } 417 418 @Override onDestroy()419 public void onDestroy() { 420 mExecutorService.submit(()-> { 421 // Ensure new create/destroy requests are denied. 422 mSubId = -1; 423 triggerDeregistrationEvent(); 424 scheduleDestroyDelegates( 425 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN) 426 .thenRun(mExecutorService::shutdown); 427 }); 428 } 429 430 /** 431 * Optionally create a new {@link SipDelegate} based off of the {@link DelegateRequest} given 432 * based on the state of this controller and associate it with the given callbacks. 433 * <p> 434 * Once the {@link SipDelegate} has been created, 435 * {@link ISipDelegateConnectionStateCallback#onCreated(ISipDelegate)} must be called with 436 * the AIDL instance corresponding to the remote {@link SipDelegate}. 437 * @param subId the subId associated with the request. 438 * @param uid the uid associated with the request 439 * @param request The request parameters used to create the {@link SipDelegate}. 440 * @param delegateState The {@link DelegateConnectionStateCallback} Binder connection. 441 * @param delegateMessage The {@link DelegateConnectionMessageCallback} Binder Connection 442 * @throws ImsException if the request to create the {@link SipDelegate} did not complete. 443 */ createSipDelegate(int subId, int uid, DelegateRequest request, String packageName, ISipDelegateConnectionStateCallback delegateState, ISipDelegateMessageCallback delegateMessage)444 public void createSipDelegate(int subId, int uid, DelegateRequest request, String packageName, 445 ISipDelegateConnectionStateCallback delegateState, 446 ISipDelegateMessageCallback delegateMessage) throws ImsException { 447 logi("createSipDelegate: request= " + request + ", packageName= " + packageName); 448 CompletableFuture<ImsException> result = new CompletableFuture<>(); 449 mExecutorService.submit(() -> createSipDelegateInternal(subId, uid, request, packageName, 450 delegateState, 451 // Capture any ImsExceptions generated during the process. 452 delegateMessage, result::complete)); 453 try { 454 ImsException e = result.get(); 455 logi("createSipDelegate: request finished"); 456 if (e != null) { 457 throw e; 458 } 459 } catch (InterruptedException | ExecutionException e) { 460 logw("createSipDelegate: exception completing future: " + e); 461 } 462 } 463 464 /** 465 * The remote IMS application has requested the destruction of an existing {@link SipDelegate}. 466 * @param subId The subId associated with the request. 467 * @param connection The internal Binder connection associated with the {@link SipDelegate}. 468 * @param reason The reason for why the {@link SipDelegate} was destroyed. 469 */ destroySipDelegate(int subId, ISipDelegate connection, int reason)470 public void destroySipDelegate(int subId, ISipDelegate connection, int reason) { 471 mExecutorService.execute(() -> destroySipDelegateInternal(subId, connection, reason)); 472 } 473 474 /** 475 * The remote IMS application has requested that the ImsService tear down and re-register for 476 * IMS features due to an error it received on the network in response to a SIP request. 477 */ triggerFullNetworkRegistration(int subId, ISipDelegate connection, int sipCode, String sipReason)478 public void triggerFullNetworkRegistration(int subId, ISipDelegate connection, int sipCode, 479 String sipReason) { 480 mExecutorService.execute(() -> triggerFullNetworkRegistrationInternal(subId, connection, 481 sipCode, sipReason)); 482 } 483 484 /** 485 * @return Whether or not SipTransports are supported on the connected ImsService. This can 486 * change based on the capabilities of the ImsService. 487 * @throws ImsException if the ImsService connected to this controller is currently down. 488 */ isSupported(int subId)489 public boolean isSupported(int subId) throws ImsException { 490 Boolean result = waitForMethodToComplete(() -> isSupportedInternal(subId)); 491 if (result == null) { 492 logw("isSupported, unexpected null result, returning false"); 493 return false; 494 } 495 return result; 496 } 497 createSipDelegateInternal(int subId, int uid, DelegateRequest request, String packageName, ISipDelegateConnectionStateCallback delegateState, ISipDelegateMessageCallback delegateMessage, Consumer<ImsException> startedErrorConsumer)498 private void createSipDelegateInternal(int subId, int uid, DelegateRequest request, 499 String packageName, ISipDelegateConnectionStateCallback delegateState, 500 ISipDelegateMessageCallback delegateMessage, 501 Consumer<ImsException> startedErrorConsumer) { 502 ISipTransport transport; 503 IImsRegistration registration; 504 // Send back any errors via Consumer early in creation process if it is clear that the 505 // SipDelegate will never be created. 506 try { 507 checkStateOfController(subId); 508 transport = mRcsManager.getSipTransport(); 509 registration = mRcsManager.getImsRegistration(); 510 if (transport == null) { 511 logw("createSipDelegateInternal, transport null during request."); 512 startedErrorConsumer.accept(new ImsException("SipTransport not supported", 513 ImsException.CODE_ERROR_UNSUPPORTED_OPERATION)); 514 return; 515 } else { 516 // Release the binder thread as there were no issues processing the initial request. 517 startedErrorConsumer.accept(null); 518 } 519 } catch (ImsException e) { 520 logw("createSipDelegateInternal, ImsException during create: " + e); 521 startedErrorConsumer.accept(e); 522 return; 523 } 524 525 SipDelegateController c = mDelegateControllerFactory.create(subId, uid, request, 526 packageName, transport, registration, mExecutorService, delegateState, 527 delegateMessage); 528 logi("createSipDelegateInternal: request= " + request + ", packageName= " + packageName 529 + ", controller created: " + c); 530 addPendingCreateAndEvaluate(c); 531 // If SipDialogStateCallback is registered, listener will be set. 532 if (mSipDialogStateCallbacks.getRegisteredCallbackCount() > 0) { 533 c.setSipDialogsListener(mSipDialogsListener, false); 534 } 535 } 536 destroySipDelegateInternal(int subId, ISipDelegate connection, int reason)537 private void destroySipDelegateInternal(int subId, ISipDelegate connection, int reason) { 538 if (subId != mSubId) { 539 logw("destroySipDelegateInternal: ignoring destroy, as this is about to be destroyed " 540 + "anyway due to subId change, delegate=" + connection); 541 return; 542 } 543 if (connection == null) { 544 logw("destroySipDelegateInternal: ignoring destroy, null connection binder."); 545 return; 546 } 547 SipDelegateController match = null; 548 for (SipDelegateController controller : mDelegatePriorityQueue) { 549 if (Objects.equal(connection.asBinder(), 550 controller.getSipDelegateInterface().asBinder())) { 551 match = controller; 552 break; 553 } 554 } 555 if (match == null) { 556 logw("destroySipDelegateInternal: could not find matching connection=" + connection); 557 return; 558 } 559 560 logi("destroySipDelegateInternal: destroy queued for connection= " + connection); 561 addPendingDestroyAndEvaluate(match, reason); 562 } 563 triggerFullNetworkRegistrationInternal(int subId, ISipDelegate connection, int sipCode, String sipReason)564 private void triggerFullNetworkRegistrationInternal(int subId, ISipDelegate connection, 565 int sipCode, String sipReason) { 566 if (subId != mSubId) { 567 logw("triggerFullNetworkRegistrationInternal: ignoring network reg request, as this is" 568 + "about to be destroyed anyway due to subId change, delegate=" + connection); 569 return; 570 } 571 if (connection == null) { 572 logw("triggerFullNetworkRegistrationInternal: ignoring, null connection binder."); 573 return; 574 } 575 // Ensure the requester has a valid SipDelegate registered. 576 SipDelegateController match = null; 577 for (SipDelegateController controller : mDelegatePriorityQueue) { 578 if (Objects.equal(connection.asBinder(), 579 controller.getSipDelegateInterface().asBinder())) { 580 match = controller; 581 break; 582 } 583 } 584 if (match == null) { 585 logw("triggerFullNetworkRegistrationInternal: could not find matching connection, " 586 + "ignoring"); 587 return; 588 } 589 590 match.triggerFullNetworkRegistration(sipCode, sipReason); 591 } 592 593 /** 594 * Cancel pending update IMS registration events if they exist and instead send a deregister 595 * event. 596 */ triggerDeregistrationEvent()597 private void triggerDeregistrationEvent() { 598 logi("triggerDeregistrationEvent: Sending deregister event to ImsService"); 599 cancelPendingUpdateRegistration(); 600 601 IImsRegistration registrationImpl = mRcsManager.getImsRegistration(); 602 if (registrationImpl != null) { 603 try { 604 registrationImpl.triggerSipDelegateDeregistration(); 605 } catch (RemoteException e) { 606 logi("triggerDeregistrationEvent: received RemoteException: " + e); 607 } 608 } 609 } 610 611 /** 612 * Schedule an update to the IMS registration. If there is an existing update scheduled, cancel 613 * it and reschedule. 614 * <p> 615 * We want to wait because this can directly result in changes to the IMS registration on the 616 * network, so we need to wait for a steady state where all changes have been made before 617 * triggering an update to the network registration. 618 */ scheduleUpdateRegistration()619 private void scheduleUpdateRegistration() { 620 cancelPendingUpdateRegistration(); 621 622 ScheduledFuture<?> f = mExecutorService.schedule(this::triggerUpdateRegistrationEvent, 623 mTimerAdapter.getUpdateRegistrationDelayMilliseconds(), TimeUnit.MILLISECONDS); 624 logi("scheduleUpdateRegistration: scheduling new event: " + f); 625 mPendingUpdateRegistrationFuture = f; 626 } 627 628 /** 629 * Cancel an existing pending task to update the IMS registration associated with SIP delegates. 630 */ cancelPendingUpdateRegistration()631 private void cancelPendingUpdateRegistration() { 632 if (mPendingUpdateRegistrationFuture == null 633 || mPendingUpdateRegistrationFuture.isDone()) { 634 return; 635 } 636 // Cancel the old pending operation and reschedule again. 637 mPendingUpdateRegistrationFuture.cancel(false); 638 logi("scheduleUpdateRegistration: cancelling existing reg update event: " 639 + mPendingUpdateRegistrationFuture); 640 } 641 642 /** 643 * Triggers an event to update the IMS registration of the ImsService. Should only be called 644 * from {@link #scheduleUpdateRegistration()}. 645 */ triggerUpdateRegistrationEvent()646 private void triggerUpdateRegistrationEvent() { 647 logi("triggerUpdateRegistrationEvent: Sending update registration event to ImsService"); 648 IImsRegistration registrationImpl = mRcsManager.getImsRegistration(); 649 if (registrationImpl != null) { 650 try { 651 registrationImpl.triggerUpdateSipDelegateRegistration(); 652 } catch (RemoteException e) { 653 logi("triggerUpdateRegistrationEvent: received RemoteException: " + e); 654 } 655 } 656 } 657 658 /** 659 * Returns whether or not the ImsService implementation associated with the supplied subId 660 * supports the SipTransport APIs. 661 * <p> 662 * This should only be called on the ExecutorService. 663 * @return true if SipTransport is supported on this subscription, false otherwise. 664 * @throws ImsException thrown if there was an error determining the state of the ImsService. 665 */ isSupportedInternal(int subId)666 private boolean isSupportedInternal(int subId) throws ImsException { 667 checkStateOfController(subId); 668 return (mRcsManager.getSipTransport() != null); 669 } 670 addPendingDestroy(SipDelegateController c, int reason)671 private boolean addPendingDestroy(SipDelegateController c, int reason) { 672 DestroyRequest request = new DestroyRequest(c, reason); 673 if (!mDelegatePendingDestroy.contains(request)) { 674 return mDelegatePendingDestroy.add(request); 675 } 676 return false; 677 } 678 679 /** 680 * The supplied SipDelegateController has been destroyed and associated feature tags have been 681 * released. Trigger the re-evaluation of the priority queue with the new configuration. 682 */ addPendingDestroyAndEvaluate(SipDelegateController c, int reason)683 private void addPendingDestroyAndEvaluate(SipDelegateController c, int reason) { 684 if (addPendingDestroy(c, reason)) { 685 scheduleThrottledReevaluate(); 686 } 687 } 688 689 /** 690 * A new SipDelegateController has been created, add to the back of the priority queue and 691 * trigger the re-evaluation of the priority queue with the new configuration. 692 */ addPendingCreateAndEvaluate(SipDelegateController c)693 private void addPendingCreateAndEvaluate(SipDelegateController c) { 694 mDelegatePendingCreate.add(c); 695 scheduleThrottledReevaluate(); 696 } 697 698 /** 699 * The priority queue has changed, which will cause a re-evaluation of the feature tags granted 700 * to each SipDelegate. 701 * <p> 702 * Note: re-evaluations are throttled to happen at a minimum to occur every 703 * REEVALUATE_THROTTLE_MS seconds. We also do not reevaluate while another reevaluate operation 704 * is in progress, so in this case, defer schedule itself. 705 */ scheduleThrottledReevaluate()706 private void scheduleThrottledReevaluate() { 707 if (isEvaluatePendingAndNotInProgress()) { 708 logi("scheduleThrottledReevaluate: throttling reevaluate, eval already pending: " 709 + mScheduledEvaluateFuture); 710 } else { 711 mScheduledEvaluateFuture = mExecutorService.schedule(this::reevaluateDelegates, 712 mTimerAdapter.getReevaluateThrottleTimerMilliseconds(), TimeUnit.MILLISECONDS); 713 logi("scheduleThrottledReevaluate: new reevaluate scheduled: " 714 + mScheduledEvaluateFuture); 715 } 716 } 717 718 /** 719 * @return true if there is a evaluate pending, false if there is not. If evaluate has already 720 * begun, but we are waiting for it to complete, this will also return false. 721 */ isEvaluatePendingAndNotInProgress()722 private boolean isEvaluatePendingAndNotInProgress() { 723 boolean isEvalScheduled = mScheduledEvaluateFuture != null 724 && !mScheduledEvaluateFuture.isDone(); 725 boolean isEvalInProgress = mEvaluateCompleteFuture != null 726 && !mEvaluateCompleteFuture.isDone(); 727 return isEvalScheduled && !isEvalInProgress; 728 } 729 730 /** 731 * Cancel any pending re-evaluates and perform it as soon as possible. This is done in the case 732 * where we need to do something like tear down this controller or change subId. 733 */ scheduleReevaluateNow(CompletableFuture<Void> onDoneFuture)734 private void scheduleReevaluateNow(CompletableFuture<Void> onDoneFuture) { 735 if (isEvaluatePendingAndNotInProgress()) { 736 mScheduledEvaluateFuture.cancel(false /*interrupt*/); 737 logi("scheduleReevaluateNow: cancelling pending reevaluate: " 738 + mScheduledEvaluateFuture); 739 } 740 // we have tasks that depend on this potentially, so once the last reevaluate is done, 741 // schedule a new one right away. 742 if (mEvaluateCompleteFuture != null && !mEvaluateCompleteFuture.isDone()) { 743 mEvaluateCompleteFuture.thenRunAsync( 744 () -> scheduleReevaluateNow(onDoneFuture), mExecutorService); 745 return; 746 } 747 748 reevaluateDelegates(); 749 mEvaluateCompleteFuture.thenAccept(onDoneFuture::complete); 750 } 751 752 /** 753 * Apply all operations that have been pending by collecting pending create/destroy operations 754 * and batch applying them to the mDelegatePriorityQueue. 755 * 756 * First perform the operation of destroying all SipDelegateConnections that have been pending 757 * destroy. Next, add all pending new SipDelegateControllers to the end of 758 * mDelegatePriorityQueue and loop through all in the queue, applying feature tags to the 759 * appropriate SipDelegateController if they pass role checks and have not already been claimed 760 * by another delegate higher in the priority queue. 761 */ reevaluateDelegates()762 private void reevaluateDelegates() { 763 // We need to cancel the pending update now and reschedule IMS registration update for 764 // after the reevaluate is complete. 765 cancelPendingUpdateRegistration(); 766 if (mEvaluateCompleteFuture != null && !mEvaluateCompleteFuture.isDone()) { 767 logw("reevaluateDelegates: last evaluate not done, deferring new request"); 768 // Defer re-evaluate until after the pending re-evaluate is complete. 769 mEvaluateCompleteFuture.thenRunAsync(this::scheduleThrottledReevaluate, 770 mExecutorService); 771 return; 772 } 773 774 // Remove tracking for all SipDelegates being destroyed first 775 for (DestroyRequest d : mDelegatePendingDestroy) { 776 logi("reevaluateDelegates: starting destroy for: " + d.controller.getPackageName()); 777 mActiveAppBinders.unregister(d.controller.getAppMessageCallback()); 778 } 779 // Destroy all pending destroy delegates first. Order doesn't matter. 780 List<CompletableFuture<Void>> pendingDestroyList = mDelegatePendingDestroy.stream() 781 .map(d -> triggerDestroy(d.controller, d.reason)).collect( 782 Collectors.toList()); 783 CompletableFuture<Void> pendingDestroy = CompletableFuture.allOf( 784 pendingDestroyList.toArray(new CompletableFuture[mDelegatePendingDestroy.size()])); 785 mDelegatePriorityQueue.removeAll(mDelegatePendingDestroy.stream().map(d -> d.controller) 786 .collect(Collectors.toList())); 787 mDelegatePendingDestroy.clear(); 788 789 // Add newly created SipDelegates to end of queue before evaluating associated features. 790 mDelegatePriorityQueue.addAll(mDelegatePendingCreate); 791 for (SipDelegateController c : mDelegatePendingCreate) { 792 logi("reevaluateDelegates: pending create: " + c.getPackageName()); 793 mActiveAppBinders.register(c.getAppMessageCallback(), c); 794 } 795 mDelegatePendingCreate.clear(); 796 797 // Wait for destroy stages to complete, then loop from oldest to most recent and associate 798 // feature tags that the app has requested to the SipDelegate. 799 // Each feature tag can only be associated with one SipDelegate, so as feature tags are 800 // taken, do not allow other SipDelegates to be associated with those tags as well. Each 801 // stage of the CompletableFuture chain passes the previously claimed feature tags into the 802 // next stage so that those feature tags can be denied if already claimed. 803 // Executor doesn't matter here, just composing here to transform to the next stage. 804 CompletableFuture<Set<String>> pendingChange = pendingDestroy.thenCompose((ignore) -> { 805 logi("reevaluateDelegates: destroy phase complete"); 806 return CompletableFuture.completedFuture(new ArraySet<>()); 807 }); 808 final String cachedSmsRolePackage = mCachedSmsRolePackageName; 809 for (SipDelegateController c : mDelegatePriorityQueue) { 810 logi("reevaluateDelegates: pending reeval: " + c); 811 pendingChange = pendingChange.thenComposeAsync((takenTags) -> { 812 logi("reevaluateDelegates: last stage completed with result:" + takenTags); 813 if (takenTags == null) { 814 // return early, the ImsService is no longer available. This will eventually be 815 // destroyed. 816 return CompletableFuture.completedFuture(null /*failed*/); 817 } 818 return changeSupportedFeatureTags(c, cachedSmsRolePackage, takenTags); 819 }, mExecutorService); 820 } 821 822 // Executor doesn't matter here, schedule an event to update the IMS registration. 823 mEvaluateCompleteFuture = pendingChange 824 .whenComplete((f, ex) -> { 825 if (ex != null) { 826 logw("reevaluateDelegates: Exception caught: " + ex); 827 } 828 }).thenAccept((associatedFeatures) -> { 829 logi("reevaluateDelegates: reevaluate complete, feature tags associated: " 830 + associatedFeatures); 831 scheduleUpdateRegistration(); 832 }); 833 logi("reevaluateDelegates: future created."); 834 } 835 triggerDestroy(SipDelegateController c, int reason)836 private CompletableFuture<Void> triggerDestroy(SipDelegateController c, int reason) { 837 return c.destroy(isForcedFromReason(reason), reason) 838 // Executor doesn't matter here, just for logging. 839 .thenAccept((delegateReason) -> logi("destroy triggered with " + reason 840 + " and finished with reason= " + delegateReason)); 841 } 842 isForcedFromReason(int reason)843 private boolean isForcedFromReason(int reason) { 844 switch (reason) { 845 case SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_UNKNOWN: 846 logw("isForcedFromReason, unknown reason"); 847 /*intentional fallthrough*/ 848 case SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP: 849 /*intentional fallthrough*/ 850 case SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS: 851 return false; 852 case SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD: 853 /*intentional fallthrough*/ 854 case SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN: 855 return true; 856 } 857 logw("isForcedFromReason, unexpected reason: " + reason); 858 return false; 859 } 860 861 /** 862 * Called by RoleManager when a role has changed so that we can query the new role holder. 863 * @param roleName the name of the role whose holders are changed 864 * @param user the user for this role holder change 865 */ 866 // Called on mExecutorThread 867 @Override onRoleHoldersChanged(@onNull String roleName, @NonNull UserHandle user)868 public void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) { 869 logi("onRoleHoldersChanged, roleName= " + roleName + ", user= " + user); 870 // Only monitor changes on the system 871 if (!UserHandle.SYSTEM.equals(user)) { 872 return; 873 } 874 875 if (!RoleManager.ROLE_SMS.equals(roleName)) { 876 logi("onRoleHoldersChanged, ignoring non SMS role change"); 877 // TODO: only target default sms app for now and add new roles later using 878 // CarrierConfigManager 879 return; 880 } 881 boolean roleChanged = updateRoleCache(); 882 if (roleChanged) { 883 if (!TextUtils.isEmpty(mCachedSmsRolePackageName)) { 884 // the role change event will go A -> "" and then "" -> B. In order to not send two 885 // events to the modem, only trigger the deregistration event when we move to a 886 // non-empty package name. 887 triggerDeregistrationEvent(); 888 } 889 // new denied tags will be picked up when reevaluate completes. 890 scheduleThrottledReevaluate(); 891 } 892 } 893 894 895 /** 896 * @return true, if the role cache has changed, false otherwise. 897 */ updateRoleCache()898 private boolean updateRoleCache() { 899 String newSmsRolePackageName = ""; 900 try { 901 // Only one app can fulfill the SMS role. 902 newSmsRolePackageName = mRoleManagerAdapter.getRoleHolders(RoleManager.ROLE_SMS) 903 .stream().findFirst().orElse(""); 904 } catch (Exception e) { 905 logi("updateRoleCache: exception=" + e); 906 } 907 908 logi("updateRoleCache: new packageName=" + newSmsRolePackageName); 909 if (TextUtils.equals(mCachedSmsRolePackageName, newSmsRolePackageName)) { 910 logi("updateRoleCache, skipping, role did not change"); 911 return false; 912 } 913 mCachedSmsRolePackageName = newSmsRolePackageName; 914 return true; 915 } 916 917 /** 918 * Check the requested roles for the specified package name and return the tags that were 919 * applied to that SipDelegateController. 920 * @param controller Controller to attribute feature tags to. 921 * @param alreadyRequestedTags The feature tags that were already granted to other SipDelegates. 922 * @return Once complete, contains the set of feature tags that the SipDelegate now has 923 * associated with it along with the feature tags that previous SipDelegates had. 924 * 925 * // TODO: we currently only track SMS role, extend to support other roles as well. 926 */ changeSupportedFeatureTags( SipDelegateController controller, String smsRolePackageName, Set<String> alreadyRequestedTags)927 private CompletableFuture<Set<String>> changeSupportedFeatureTags( 928 SipDelegateController controller, String smsRolePackageName, 929 Set<String> alreadyRequestedTags) { 930 Set<String> requestedFeatureTags = controller.getInitialRequest().getFeatureTags(); 931 String packageName = controller.getPackageName(); 932 if (!smsRolePackageName.equals(packageName)) { 933 // Deny all tags. 934 Set<FeatureTagState> deniedTags = new ArraySet<>(); 935 for (String s : requestedFeatureTags) { 936 deniedTags.add(new FeatureTagState(s, 937 SipDelegateManager.DENIED_REASON_NOT_ALLOWED)); 938 } 939 CompletableFuture<Boolean> pendingDeny = controller.changeSupportedFeatureTags( 940 Collections.emptySet(), deniedTags); 941 logi("changeSupportedFeatureTags pendingDeny=" + pendingDeny); 942 // do not worry about executor used here, this stage used to interpret result + add log. 943 return pendingDeny.thenApply((completedSuccessfully) -> { 944 logi("changeSupportedFeatureTags: deny completed: " + completedSuccessfully); 945 if (!completedSuccessfully) return null; 946 // Return back the previous list of requested tags, as we did not add any more. 947 return alreadyRequestedTags; 948 }); 949 } 950 951 ArraySet<String> previouslyGrantedTags = new ArraySet<>(alreadyRequestedTags); 952 ArraySet<String> candidateFeatureTags = new ArraySet<>(requestedFeatureTags); 953 Set<FeatureTagState> deniedTags = 954 updateSupportedTags(candidateFeatureTags, previouslyGrantedTags); 955 956 // Add newly granted tags to the already requested tags list. 957 previouslyGrantedTags.addAll(candidateFeatureTags); 958 CompletableFuture<Boolean> pendingChange = controller.changeSupportedFeatureTags( 959 candidateFeatureTags, deniedTags); 960 logi("changeSupportedFeatureTags pendingChange=" + pendingChange); 961 // do not worry about executor used here, this stage used to interpret result + add log. 962 return pendingChange.thenApply((completedSuccessfully) -> { 963 logi("changeSupportedFeatureTags: change completed: " + completedSuccessfully); 964 if (!completedSuccessfully) return null; 965 return previouslyGrantedTags; 966 }); 967 } 968 969 /** 970 * Update candidate feature tags according to feature tags allowed by carrier config, 971 * and previously granted by other SipDelegates. 972 * 973 * @param candidateFeatureTags The candidate feature tags to be updated. It will be 974 * updated as needed per the carrier config and previously granted feature tags. 975 * @param previouslyGrantedTags The feature tags already granted by other SipDelegates. 976 * @return The set of denied feature tags. 977 */ 978 private Set<FeatureTagState> updateSupportedTags(Set<String> candidateFeatureTags, 979 Set<String> previouslyGrantedTags) { 980 Boolean overrideRes = RcsProvisioningMonitor.getInstance() 981 .getImsFeatureValidationOverride(mSubId); 982 // deny tags already used by other delegates 983 Set<FeatureTagState> deniedTags = new ArraySet<>(); 984 985 // match config if feature validation is not overridden 986 if (overrideRes == null) { 987 Iterator<String> it = candidateFeatureTags.iterator(); 988 while (it.hasNext()) { 989 String tag = it.next(); 990 if (previouslyGrantedTags.contains(tag)) { 991 logi(tag + " has already been granted previously."); 992 it.remove(); 993 deniedTags.add(new FeatureTagState(tag, 994 SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE)); 995 } else if (!mFeatureTagsAllowed.contains(tag.trim().toLowerCase(Locale.ROOT))) { 996 logi(tag + " is not allowed per config."); 997 it.remove(); 998 deniedTags.add(new FeatureTagState(tag, 999 SipDelegateManager.DENIED_REASON_NOT_ALLOWED)); 1000 } 1001 } 1002 } else if (Boolean.FALSE.equals(overrideRes)) { 1003 logi("all features are denied for test purpose."); 1004 for (String s : candidateFeatureTags) { 1005 deniedTags.add(new FeatureTagState(s, 1006 SipDelegateManager.DENIED_REASON_NOT_ALLOWED)); 1007 } 1008 candidateFeatureTags.clear(); 1009 } 1010 1011 return deniedTags; 1012 } 1013 1014 /** 1015 * Run a Callable on the ExecutorService Thread and wait for the result. 1016 * If an ImsException is thrown, catch it and rethrow it to caller. 1017 */ 1018 private <T> T waitForMethodToComplete(Callable<T> callable) throws ImsException { 1019 Future<T> r = mExecutorService.submit(callable); 1020 T result; 1021 try { 1022 result = r.get(); 1023 } catch (InterruptedException e) { 1024 result = null; 1025 } catch (ExecutionException e) { 1026 Throwable cause = e.getCause(); 1027 if (cause instanceof ImsException) { 1028 // Rethrow the exception 1029 throw (ImsException) cause; 1030 } 1031 logw("Unexpected Exception, returning null: " + cause); 1032 result = null; 1033 } 1034 return result; 1035 } 1036 1037 /** 1038 * Throw an ImsException for common scenarios where the state of the controller is not ready 1039 * for communication. 1040 * <p> 1041 * This should only be called while running on the on the ExecutorService. 1042 */ 1043 private void checkStateOfController(int subId) throws ImsException { 1044 if (mSubId != subId) { 1045 // sub ID has changed while this was in the queue. 1046 throw new ImsException("subId is no longer valid for this request.", 1047 ImsException.CODE_ERROR_INVALID_SUBSCRIPTION); 1048 } 1049 if (mRcsManager == null) { 1050 throw new ImsException("Connection to ImsService is not available", 1051 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 1052 } 1053 } 1054 1055 private void onRcsManagerChanged(RcsFeatureManager m) { 1056 logi("manager changed, " + mRcsManager + "->" + m); 1057 if (mRcsManager == m) return; 1058 mRcsManager = m; 1059 if (mRcsManager == null) { 1060 logi("onRcsManagerChanged: lost connection to ImsService, tearing down..."); 1061 unregisterListeners(); 1062 scheduleDestroyDelegates(SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD); 1063 } else { 1064 logi("onRcsManagerChanged: registering listeners/updating role cache..."); 1065 registerListeners(); 1066 updateRoleCache(); 1067 } 1068 } 1069 1070 private void registerListeners() { 1071 try { 1072 mRoleManagerAdapter.addOnRoleHoldersChangedListenerAsUser(mExecutorService, this, 1073 UserHandle.SYSTEM); 1074 } catch (Exception e) { 1075 logi("registerListeners: exception=" + e); 1076 } 1077 } 1078 1079 private void unregisterListeners() { 1080 mCachedSmsRolePackageName = ""; 1081 mRoleManagerAdapter.removeOnRoleHoldersChangedListenerAsUser(this, UserHandle.SYSTEM); 1082 } 1083 1084 /** 1085 * Called when the sub ID associated with the slot has changed. 1086 */ 1087 private void onSubIdChanged(int newSubId) { 1088 logi("subId changed, " + mSubId + "->" + newSubId); 1089 if (mSubId != newSubId) { 1090 // Swap subId, any pending create/destroy on old subId will be denied. 1091 mSubId = newSubId; 1092 scheduleDestroyDelegates( 1093 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN); 1094 } 1095 1096 onCarrierConfigChangedInternal(); 1097 } 1098 1099 /** 1100 * Called when the carrier configuration associated with the same subId has changed. 1101 */ 1102 private void onCarrierConfigChangedInternal() { 1103 logi("Carrier Config changed for subId: " + mSubId); 1104 mFeatureTagsAllowed.clear(); 1105 PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId); 1106 String[] tagConfigs = carrierConfig.getStringArray( 1107 CarrierConfigManager.Ims.KEY_RCS_FEATURE_TAG_ALLOWED_STRING_ARRAY); 1108 if (tagConfigs != null && tagConfigs.length > 0) { 1109 for (String tag : tagConfigs) { 1110 mFeatureTagsAllowed.add(tag.trim().toLowerCase(Locale.ROOT)); 1111 } 1112 } 1113 } 1114 1115 /** 1116 * Destroy all tracked SipDelegateConnections due to the subscription being torn down. 1117 * @return A CompletableFuture that will complete when all SipDelegates have been torn down. 1118 */ 1119 private CompletableFuture<Void> scheduleDestroyDelegates(int reason) { 1120 boolean addedDestroy = false; 1121 for (SipDelegateController c : mDelegatePriorityQueue) { 1122 logi("scheduleDestroyDelegates: Controller pending destroy: " + c); 1123 addedDestroy |= addPendingDestroy(c, reason); 1124 } 1125 if (addedDestroy) { 1126 CompletableFuture<Void> pendingDestroy = new CompletableFuture<>(); 1127 scheduleReevaluateNow(pendingDestroy); 1128 return pendingDestroy; 1129 } else { 1130 return CompletableFuture.completedFuture(null); 1131 } 1132 } 1133 1134 /** 1135 * Adds a callback that gets called when SipDialog status has changed. 1136 * @param subId The subId associated with the request. 1137 * @param cb A {@link android.telephony.ims.SipDialogStateCallback} that will notify the caller 1138 * when Dialog status has changed. 1139 */ 1140 public void addCallbackForSipDialogState(int subId, ISipDialogStateCallback cb) { 1141 if (subId != mSubId) { 1142 logw("addCallbackForSipDialogState the subId is not supported"); 1143 return; 1144 } 1145 // callback register and no delegate : register this callback / notify (empty state) 1146 // callback register and delegates : register this callback / release listener / notify 1147 mSipDialogStateCallbacks.register(cb); 1148 if (!mDelegatePriorityQueue.isEmpty()) { 1149 for (SipDelegateController dc : mDelegatePriorityQueue) { 1150 dc.setSipDialogsListener(mSipDialogsListener, true); 1151 } 1152 } else { 1153 mSipDialogsListener.notifySipDialogState(); 1154 } 1155 } 1156 1157 /** 1158 * Unregister previously registered callback 1159 * @param subId The subId associated with the request. 1160 * @param cb A {@link android.telephony.ims.SipDialogStateCallback} that will be unregistering. 1161 */ 1162 public void removeCallbackForSipDialogState(int subId, ISipDialogStateCallback cb) { 1163 if (subId != mSubId) { 1164 logw("addCallbackForSipDialogState the subId is not supported"); 1165 return; 1166 } 1167 if (cb == null) { 1168 throw new IllegalArgumentException("callback is null"); 1169 } 1170 1171 // remove callback register and no delegate : only unregister this callback 1172 // remove callback register and delegates : 1173 // unregister this callback and setListener(null) 1174 mSipDialogStateCallbacks.unregister(cb); 1175 if (mSipDialogStateCallbacks.getRegisteredCallbackCount() == 0) { 1176 if (!mDelegatePriorityQueue.isEmpty()) { 1177 for (SipDelegateController dc : mDelegatePriorityQueue) { 1178 dc.setSipDialogsListener(null, false); 1179 } 1180 } 1181 } 1182 } 1183 1184 @Override 1185 public void dump(PrintWriter printWriter) { 1186 IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); 1187 pw.println("SipTransportController" + "[" + mSlotId + "->" + mSubId + "]:"); 1188 pw.increaseIndent(); 1189 pw.println("LocalLog:"); 1190 pw.increaseIndent(); 1191 mLocalLog.dump(pw); 1192 pw.decreaseIndent(); 1193 pw.println("SipDelegateControllers (in priority order):"); 1194 pw.increaseIndent(); 1195 if (mDelegatePriorityQueue.isEmpty()) { 1196 pw.println("[NONE]"); 1197 } else { 1198 for (SipDelegateController c : mDelegatePriorityQueue) { 1199 c.dump(pw); 1200 } 1201 } 1202 pw.decreaseIndent(); 1203 pw.decreaseIndent(); 1204 } 1205 1206 private void logi(String log) { 1207 Log.i(LOG_TAG, "[" + mSlotId + "->" + mSubId + "] " + log); 1208 mLocalLog.log("[I] " + log); 1209 } 1210 1211 private void logw(String log) { 1212 Log.w(LOG_TAG, "[" + mSlotId + "->" + mSubId + "] " + log); 1213 mLocalLog.log("[W] " + log); 1214 } 1215 } 1216