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