1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package android.telephony.ims.stub;
18 
19 import android.annotation.IntDef;
20 import android.annotation.IntRange;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.SystemApi;
24 import android.net.Uri;
25 import android.os.RemoteException;
26 import android.telephony.ims.ImsReasonInfo;
27 import android.telephony.ims.ImsRegistrationAttributes;
28 import android.telephony.ims.RegistrationManager;
29 import android.telephony.ims.aidl.IImsRegistration;
30 import android.telephony.ims.aidl.IImsRegistrationCallback;
31 import android.util.Log;
32 
33 import com.android.internal.telephony.util.RemoteCallbackListExt;
34 import com.android.internal.util.ArrayUtils;
35 
36 import java.lang.annotation.Retention;
37 import java.lang.annotation.RetentionPolicy;
38 
39 /**
40  * Controls IMS registration for this ImsService and notifies the framework when the IMS
41  * registration for this ImsService has changed status.
42  * <p>
43  * Note: There is no guarantee on the thread that the calls from the framework will be called on. It
44  * is the implementors responsibility to handle moving the calls to a working thread if required.
45  * @hide
46  */
47 @SystemApi
48 public class ImsRegistrationImplBase {
49 
50     private static final String LOG_TAG = "ImsRegistrationImplBase";
51 
52     /**
53      * @hide
54      */
55     // Defines the underlying radio technology type that we have registered for IMS over.
56     @IntDef(value = {
57                     REGISTRATION_TECH_NONE,
58                     REGISTRATION_TECH_LTE,
59                     REGISTRATION_TECH_IWLAN,
60                     REGISTRATION_TECH_CROSS_SIM,
61                     REGISTRATION_TECH_NR
62             })
63     @Retention(RetentionPolicy.SOURCE)
64     public @interface ImsRegistrationTech {}
65     /**
66      * No registration technology specified, used when we are not registered.
67      */
68     public static final int REGISTRATION_TECH_NONE = -1;
69     /**
70      * This ImsService is registered to IMS via LTE.
71      */
72     public static final int REGISTRATION_TECH_LTE = 0;
73     /**
74      * This ImsService is registered to IMS via IWLAN.
75      */
76     public static final int REGISTRATION_TECH_IWLAN = 1;
77 
78     /**
79      * This ImsService is registered to IMS via internet over second subscription.
80      */
81     public static final int REGISTRATION_TECH_CROSS_SIM = 2;
82 
83     /**
84      * This ImsService is registered to IMS via NR.
85      */
86     public static final int REGISTRATION_TECH_NR = 3;
87 
88     // Registration states, used to notify new ImsRegistrationImplBase#Callbacks of the current
89     // state.
90     // The unknown state is set as the initialization state. This is so that we do not call back
91     // with NOT_REGISTERED in the case where the ImsService has not updated the registration state
92     // yet.
93     private static final int REGISTRATION_STATE_UNKNOWN = -1;
94 
95     private final IImsRegistration mBinder = new IImsRegistration.Stub() {
96 
97         @Override
98         public @ImsRegistrationTech int getRegistrationTechnology() throws RemoteException {
99             synchronized (mLock) {
100                 return (mRegistrationAttributes == null) ? REGISTRATION_TECH_NONE
101                         : mRegistrationAttributes.getRegistrationTechnology();
102             }
103         }
104 
105         @Override
106         public void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
107             ImsRegistrationImplBase.this.addRegistrationCallback(c);
108         }
109 
110         @Override
111         public void removeRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
112             ImsRegistrationImplBase.this.removeRegistrationCallback(c);
113         }
114 
115         @Override
116         public void triggerFullNetworkRegistration(int sipCode, String sipReason) {
117             ImsRegistrationImplBase.this.triggerFullNetworkRegistration(sipCode, sipReason);
118         }
119 
120         @Override
121         public void triggerUpdateSipDelegateRegistration() {
122             ImsRegistrationImplBase.this.updateSipDelegateRegistration();
123         }
124 
125         @Override
126         public void triggerSipDelegateDeregistration() {
127             ImsRegistrationImplBase.this.triggerSipDelegateDeregistration();
128         }
129     };
130 
131     private final RemoteCallbackListExt<IImsRegistrationCallback> mCallbacks =
132             new RemoteCallbackListExt<>();
133     private final Object mLock = new Object();
134     // Locked on mLock
135     private ImsRegistrationAttributes mRegistrationAttributes;
136     // Locked on mLock
137     private int mRegistrationState = REGISTRATION_STATE_UNKNOWN;
138     // Locked on mLock, create unspecified disconnect cause.
139     private ImsReasonInfo mLastDisconnectCause = new ImsReasonInfo();
140 
141     // We hold onto the uris each time they change so that we can send it to a callback when its
142     // first added.
143     private Uri[] mUris = new Uri[0];
144     private boolean mUrisSet = false;
145 
146     /**
147      * @hide
148      */
getBinder()149     public final IImsRegistration getBinder() {
150         return mBinder;
151     }
152 
addRegistrationCallback(IImsRegistrationCallback c)153     private void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
154         mCallbacks.register(c);
155         updateNewCallbackWithState(c);
156     }
157 
removeRegistrationCallback(IImsRegistrationCallback c)158     private void removeRegistrationCallback(IImsRegistrationCallback c) {
159         mCallbacks.unregister(c);
160     }
161 
162     /**
163      * Called by the framework to request that the ImsService perform the network registration
164      * of all SIP delegates associated with this ImsService.
165      * <p>
166      * If the SIP delegate feature tag configuration has changed, then this method will be
167      * called in order to let the ImsService know that it can pick up these changes in the IMS
168      * registration.
169      */
updateSipDelegateRegistration()170     public void updateSipDelegateRegistration() {
171         // Stub implementation, ImsService should implement this
172     }
173 
174 
175     /**
176      * Called by the framework to request that the ImsService perform the network deregistration of
177      * all SIP delegates associated with this ImsService.
178      * <p>
179      * This is typically called in situations where the user has changed the configuration of the
180      * device (for example, the default messaging application) and the framework is reconfiguring
181      * the tags associated with each IMS application.
182      * <p>
183      * This should not affect the registration of features managed by the ImsService itself, such as
184      * feature tags related to MMTEL registration.
185      */
triggerSipDelegateDeregistration()186     public void triggerSipDelegateDeregistration() {
187         // Stub implementation, ImsService should implement this
188     }
189 
190     /**
191      * Called by the framework to notify the ImsService that a SIP delegate connection has received
192      * a SIP message containing a permanent failure response (such as a 403) or an indication that a
193      * SIP response timer has timed out in response to an outgoing SIP message. This method will be
194      * called when this condition occurs to trigger the ImsService to tear down the full IMS
195      * registration and re-register again.
196      *
197      * @param sipCode The SIP error code that represents a permanent failure that was received in
198      *    response to a request generated by the IMS application. See RFC3261 7.2 for the general
199      *    classes of responses available here, however the codes that generate this condition may
200      *    be carrier specific.
201      * @param sipReason The reason associated with the SIP error code. {@code null} if there was no
202      *    reason associated with the error.
203      */
triggerFullNetworkRegistration(@ntRangefrom = 100, to = 699) int sipCode, @Nullable String sipReason)204     public void triggerFullNetworkRegistration(@IntRange(from = 100, to = 699) int sipCode,
205             @Nullable String sipReason) {
206         // Stub implementation, ImsService should implement this
207     }
208 
209 
210     /**
211      * Notify the framework that the device is connected to the IMS network.
212      *
213      * @param imsRadioTech the radio access technology.
214      */
onRegistered(@msRegistrationTech int imsRadioTech)215     public final void onRegistered(@ImsRegistrationTech int imsRadioTech) {
216         onRegistered(new ImsRegistrationAttributes.Builder(imsRadioTech).build());
217     }
218 
219     /**
220      * Notify the framework that the device is connected to the IMS network.
221      *
222      * @param attributes The attributes associated with the IMS registration.
223      */
onRegistered(@onNull ImsRegistrationAttributes attributes)224     public final void onRegistered(@NonNull ImsRegistrationAttributes attributes) {
225         updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERED);
226         mCallbacks.broadcastAction((c) -> {
227             try {
228                 c.onRegistered(attributes);
229             } catch (RemoteException e) {
230                 Log.w(LOG_TAG, e + "onRegistered(int, Set) - Skipping callback.");
231             }
232         });
233     }
234 
235     /**
236      * Notify the framework that the device is trying to connect the IMS network.
237      *
238      * @param imsRadioTech the radio access technology.
239      */
onRegistering(@msRegistrationTech int imsRadioTech)240     public final void onRegistering(@ImsRegistrationTech int imsRadioTech) {
241         onRegistering(new ImsRegistrationAttributes.Builder(imsRadioTech).build());
242     }
243 
244     /**
245      * Notify the framework that the device is trying to connect the IMS network.
246      *
247      * @param attributes The attributes associated with the IMS registration.
248      */
onRegistering(@onNull ImsRegistrationAttributes attributes)249     public final void onRegistering(@NonNull ImsRegistrationAttributes attributes) {
250         updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERING);
251         mCallbacks.broadcastAction((c) -> {
252             try {
253                 c.onRegistering(attributes);
254             } catch (RemoteException e) {
255                 Log.w(LOG_TAG, e + "onRegistering(int, Set) - Skipping callback.");
256             }
257         });
258     }
259 
260     /**
261      * Notify the framework that the device is disconnected from the IMS network.
262      * <p>
263      * Note: Prior to calling {@link #onDeregistered(ImsReasonInfo)}, you should ensure that any
264      * changes to {@link android.telephony.ims.feature.ImsFeature} capability availability is sent
265      * to the framework.  For example,
266      * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO}
267      * and
268      * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}
269      * may be set to unavailable to ensure the framework knows these services are no longer
270      * available due to de-registration.  If you do not report capability changes impacted by
271      * de-registration, the framework will not know which features are no longer available as a
272      * result.
273      *
274      * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
275      */
onDeregistered(ImsReasonInfo info)276     public final void onDeregistered(ImsReasonInfo info) {
277         updateToDisconnectedState(info);
278         // ImsReasonInfo should never be null.
279         final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
280         mCallbacks.broadcastAction((c) -> {
281             try {
282                 c.onDeregistered(reasonInfo);
283             } catch (RemoteException e) {
284                 Log.w(LOG_TAG, e + "onDeregistered() - Skipping callback.");
285             }
286         });
287     }
288 
289     /**
290      * Notify the framework that the handover from the current radio technology to the technology
291      * defined in {@code imsRadioTech} has failed.
292      * @param imsRadioTech The technology that has failed to be changed. Valid values are
293      * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and
294      * {@link #REGISTRATION_TECH_CROSS_SIM}.
295      * @param info The {@link ImsReasonInfo} for the failure to change technology.
296      */
onTechnologyChangeFailed(@msRegistrationTech int imsRadioTech, ImsReasonInfo info)297     public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
298             ImsReasonInfo info) {
299         final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
300         mCallbacks.broadcastAction((c) -> {
301             try {
302                 c.onTechnologyChangeFailed(imsRadioTech, reasonInfo);
303             } catch (RemoteException e) {
304                 Log.w(LOG_TAG, e + "onTechnologyChangeFailed() - Skipping callback.");
305             }
306         });
307     }
308 
309     /**
310      * Invoked when the {@link Uri}s associated to this device's subscriber have changed.
311      * These {@link Uri}s' are filtered out during conference calls.
312      *
313      * The {@link Uri}s are not guaranteed to be different between subsequent calls.
314      * @param uris changed uris
315      */
onSubscriberAssociatedUriChanged(Uri[] uris)316     public final void onSubscriberAssociatedUriChanged(Uri[] uris) {
317         synchronized (mLock) {
318             mUris = ArrayUtils.cloneOrNull(uris);
319             mUrisSet = true;
320         }
321         mCallbacks.broadcastAction((c) -> onSubscriberAssociatedUriChanged(c, uris));
322     }
323 
onSubscriberAssociatedUriChanged(IImsRegistrationCallback callback, Uri[] uris)324     private void onSubscriberAssociatedUriChanged(IImsRegistrationCallback callback, Uri[] uris) {
325         try {
326             callback.onSubscriberAssociatedUriChanged(uris);
327         } catch (RemoteException e) {
328             Log.w(LOG_TAG, e + "onSubscriberAssociatedUriChanged() - Skipping callback.");
329         }
330     }
331 
updateToState(ImsRegistrationAttributes attributes, int newState)332     private void updateToState(ImsRegistrationAttributes attributes, int newState) {
333         synchronized (mLock) {
334             mRegistrationAttributes = attributes;
335             mRegistrationState = newState;
336             mLastDisconnectCause = null;
337         }
338     }
339 
updateToDisconnectedState(ImsReasonInfo info)340     private void updateToDisconnectedState(ImsReasonInfo info) {
341         synchronized (mLock) {
342             //We don't want to send this info over if we are disconnected
343             mUrisSet = false;
344             mUris = null;
345 
346             updateToState(new ImsRegistrationAttributes.Builder(REGISTRATION_TECH_NONE).build(),
347                     RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED);
348             if (info != null) {
349                 mLastDisconnectCause = info;
350             } else {
351                 Log.w(LOG_TAG, "updateToDisconnectedState: no ImsReasonInfo provided.");
352                 mLastDisconnectCause = new ImsReasonInfo();
353             }
354         }
355     }
356 
357     /**
358      * @param c the newly registered callback that will be updated with the current registration
359      *         state.
360      */
updateNewCallbackWithState(IImsRegistrationCallback c)361     private void updateNewCallbackWithState(IImsRegistrationCallback c)
362             throws RemoteException {
363         int state;
364         ImsRegistrationAttributes attributes;
365         ImsReasonInfo disconnectInfo;
366         boolean urisSet;
367         Uri[] uris;
368         synchronized (mLock) {
369             state = mRegistrationState;
370             attributes = mRegistrationAttributes;
371             disconnectInfo = mLastDisconnectCause;
372             urisSet = mUrisSet;
373             uris = mUris;
374         }
375         switch (state) {
376             case RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED: {
377                 c.onDeregistered(disconnectInfo);
378                 break;
379             }
380             case RegistrationManager.REGISTRATION_STATE_REGISTERING: {
381                 c.onRegistering(attributes);
382                 break;
383             }
384             case RegistrationManager.REGISTRATION_STATE_REGISTERED: {
385                 c.onRegistered(attributes);
386                 break;
387             }
388             case REGISTRATION_STATE_UNKNOWN: {
389                 // Do not callback if the state has not been updated yet by the ImsService.
390                 break;
391             }
392         }
393         if (urisSet) {
394             onSubscriberAssociatedUriChanged(c, uris);
395         }
396     }
397 }
398