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