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.SystemApi;
21 import android.net.Uri;
22 import android.os.RemoteCallbackList;
23 import android.os.RemoteException;
24 import android.telephony.ims.aidl.IImsRegistration;
25 import android.telephony.ims.aidl.IImsRegistrationCallback;
26 import android.util.Log;
27 
28 import android.telephony.ims.ImsReasonInfo;
29 
30 import com.android.internal.annotations.VisibleForTesting;
31 
32 import java.lang.annotation.Retention;
33 import java.lang.annotation.RetentionPolicy;
34 
35 /**
36  * Controls IMS registration for this ImsService and notifies the framework when the IMS
37  * registration for this ImsService has changed status.
38  * @hide
39  */
40 @SystemApi
41 public class ImsRegistrationImplBase {
42 
43     private static final String LOG_TAG = "ImsRegistrationImplBase";
44 
45     /**
46      * @hide
47      */
48     // Defines the underlying radio technology type that we have registered for IMS over.
49     @IntDef(flag = true,
50             value = {
51                     REGISTRATION_TECH_NONE,
52                     REGISTRATION_TECH_LTE,
53                     REGISTRATION_TECH_IWLAN
54             })
55     @Retention(RetentionPolicy.SOURCE)
56     public @interface ImsRegistrationTech {}
57     /**
58      * No registration technology specified, used when we are not registered.
59      */
60     public static final int REGISTRATION_TECH_NONE = -1;
61     /**
62      * IMS is registered to IMS via LTE.
63      */
64     public static final int REGISTRATION_TECH_LTE = 0;
65     /**
66      * IMS is registered to IMS via IWLAN.
67      */
68     public static final int REGISTRATION_TECH_IWLAN = 1;
69 
70     // Registration states, used to notify new ImsRegistrationImplBase#Callbacks of the current
71     // state.
72     // The unknown state is set as the initialization state. This is so that we do not call back
73     // with NOT_REGISTERED in the case where the ImsService has not updated the registration state
74     // yet.
75     private static final int REGISTRATION_STATE_UNKNOWN = -1;
76     private static final int REGISTRATION_STATE_NOT_REGISTERED = 0;
77     private static final int REGISTRATION_STATE_REGISTERING = 1;
78     private static final int REGISTRATION_STATE_REGISTERED = 2;
79 
80     /**
81      * Callback class for receiving Registration callback events.
82      * @hide
83      */
84     public static class Callback {
85         /**
86          * Notifies the framework when the IMS Provider is connected to the IMS network.
87          *
88          * @param imsRadioTech the radio access technology. Valid values are defined in
89          * {@link ImsRegistrationTech}.
90          */
onRegistered(@msRegistrationTech int imsRadioTech)91         public void onRegistered(@ImsRegistrationTech int imsRadioTech) {
92         }
93 
94         /**
95          * Notifies the framework when the IMS Provider is trying to connect the IMS network.
96          *
97          * @param imsRadioTech the radio access technology. Valid values are defined in
98          * {@link ImsRegistrationTech}.
99          */
onRegistering(@msRegistrationTech int imsRadioTech)100         public void onRegistering(@ImsRegistrationTech int imsRadioTech) {
101         }
102 
103         /**
104          * Notifies the framework when the IMS Provider is disconnected from the IMS network.
105          *
106          * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
107          */
onDeregistered(ImsReasonInfo info)108         public void onDeregistered(ImsReasonInfo info) {
109         }
110 
111         /**
112          * A failure has occurred when trying to handover registration to another technology type,
113          * defined in {@link ImsRegistrationTech}
114          *
115          * @param imsRadioTech The {@link ImsRegistrationTech} type that has failed
116          * @param info A {@link ImsReasonInfo} that identifies the reason for failure.
117          */
onTechnologyChangeFailed(@msRegistrationTech int imsRadioTech, ImsReasonInfo info)118         public void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
119                 ImsReasonInfo info) {
120         }
121 
122         /**
123          * Returns a list of subscriber {@link Uri}s associated with this IMS subscription when
124          * it changes.
125          * @param uris new array of subscriber {@link Uri}s that are associated with this IMS
126          *         subscription.
127          */
onSubscriberAssociatedUriChanged(Uri[] uris)128         public void onSubscriberAssociatedUriChanged(Uri[] uris) {
129 
130         }
131     }
132 
133     private final IImsRegistration mBinder = new IImsRegistration.Stub() {
134 
135         @Override
136         public @ImsRegistrationTech int getRegistrationTechnology() throws RemoteException {
137             return getConnectionType();
138         }
139 
140         @Override
141         public void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
142             ImsRegistrationImplBase.this.addRegistrationCallback(c);
143         }
144 
145         @Override
146         public void removeRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
147             ImsRegistrationImplBase.this.removeRegistrationCallback(c);
148         }
149     };
150 
151     private final RemoteCallbackList<IImsRegistrationCallback> mCallbacks
152             = new RemoteCallbackList<>();
153     private final Object mLock = new Object();
154     // Locked on mLock
155     private @ImsRegistrationTech
156     int mConnectionType = REGISTRATION_TECH_NONE;
157     // Locked on mLock
158     private int mRegistrationState = REGISTRATION_STATE_UNKNOWN;
159     // Locked on mLock, create unspecified disconnect cause.
160     private ImsReasonInfo mLastDisconnectCause = new ImsReasonInfo();
161 
162     /**
163      * @hide
164      */
getBinder()165     public final IImsRegistration getBinder() {
166         return mBinder;
167     }
168 
addRegistrationCallback(IImsRegistrationCallback c)169     private void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
170         mCallbacks.register(c);
171         updateNewCallbackWithState(c);
172     }
173 
removeRegistrationCallback(IImsRegistrationCallback c)174     private void removeRegistrationCallback(IImsRegistrationCallback c) {
175         mCallbacks.unregister(c);
176     }
177 
178     /**
179      * Notify the framework that the device is connected to the IMS network.
180      *
181      * @param imsRadioTech the radio access technology. Valid values are defined as
182      * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}.
183      */
onRegistered(@msRegistrationTech int imsRadioTech)184     public final void onRegistered(@ImsRegistrationTech int imsRadioTech) {
185         updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERED);
186         mCallbacks.broadcast((c) -> {
187             try {
188                 c.onRegistered(imsRadioTech);
189             } catch (RemoteException e) {
190                 Log.w(LOG_TAG, e + " " + "onRegistrationConnected() - Skipping " +
191                         "callback.");
192             }
193         });
194     }
195 
196     /**
197      * Notify the framework that the device is trying to connect the IMS network.
198      *
199      * @param imsRadioTech the radio access technology. Valid values are defined as
200      * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}.
201      */
onRegistering(@msRegistrationTech int imsRadioTech)202     public final void onRegistering(@ImsRegistrationTech int imsRadioTech) {
203         updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERING);
204         mCallbacks.broadcast((c) -> {
205             try {
206                 c.onRegistering(imsRadioTech);
207             } catch (RemoteException e) {
208                 Log.w(LOG_TAG, e + " " + "onRegistrationProcessing() - Skipping " +
209                         "callback.");
210             }
211         });
212     }
213 
214     /**
215      * Notify the framework that the device is disconnected from the IMS network.
216      * <p>
217      * Note: Prior to calling {@link #onDeregistered(ImsReasonInfo)}, you should ensure that any
218      * changes to {@link android.telephony.ims.feature.ImsFeature} capability availability is sent
219      * to the framework.  For example,
220      * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO}
221      * and
222      * {@link android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}
223      * may be set to unavailable to ensure the framework knows these services are no longer
224      * available due to de-registration.  If you do not report capability changes impacted by
225      * de-registration, the framework will not know which features are no longer available as a
226      * result.
227      *
228      * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
229      */
onDeregistered(ImsReasonInfo info)230     public final void onDeregistered(ImsReasonInfo info) {
231         updateToDisconnectedState(info);
232         mCallbacks.broadcast((c) -> {
233             try {
234                 c.onDeregistered(info);
235             } catch (RemoteException e) {
236                 Log.w(LOG_TAG, e + " " + "onRegistrationDisconnected() - Skipping " +
237                         "callback.");
238             }
239         });
240     }
241 
242     /**
243      * Notify the framework that the handover from the current radio technology to the technology
244      * defined in {@code imsRadioTech} has failed.
245      * @param imsRadioTech The technology that has failed to be changed. Valid values are
246      * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}.
247      * @param info The {@link ImsReasonInfo} for the failure to change technology.
248      */
onTechnologyChangeFailed(@msRegistrationTech int imsRadioTech, ImsReasonInfo info)249     public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
250             ImsReasonInfo info) {
251         mCallbacks.broadcast((c) -> {
252             try {
253                 c.onTechnologyChangeFailed(imsRadioTech, info);
254             } catch (RemoteException e) {
255                 Log.w(LOG_TAG, e + " " + "onRegistrationChangeFailed() - Skipping " +
256                         "callback.");
257             }
258         });
259     }
260 
261     /**
262      * The this device's subscriber associated {@link Uri}s have changed, which are used to filter
263      * out this device's {@link Uri}s during conference calling.
264      * @param uris
265      */
onSubscriberAssociatedUriChanged(Uri[] uris)266     public final void onSubscriberAssociatedUriChanged(Uri[] uris) {
267         mCallbacks.broadcast((c) -> {
268             try {
269                 c.onSubscriberAssociatedUriChanged(uris);
270             } catch (RemoteException e) {
271                 Log.w(LOG_TAG, e + " " + "onSubscriberAssociatedUriChanged() - Skipping " +
272                         "callback.");
273             }
274         });
275     }
276 
updateToState(@msRegistrationTech int connType, int newState)277     private void updateToState(@ImsRegistrationTech int connType, int newState) {
278         synchronized (mLock) {
279             mConnectionType = connType;
280             mRegistrationState = newState;
281             mLastDisconnectCause = null;
282         }
283     }
284 
updateToDisconnectedState(ImsReasonInfo info)285     private void updateToDisconnectedState(ImsReasonInfo info) {
286         synchronized (mLock) {
287             updateToState(REGISTRATION_TECH_NONE, REGISTRATION_STATE_NOT_REGISTERED);
288             if (info != null) {
289                 mLastDisconnectCause = info;
290             } else {
291                 Log.w(LOG_TAG, "updateToDisconnectedState: no ImsReasonInfo provided.");
292                 mLastDisconnectCause = new ImsReasonInfo();
293             }
294         }
295     }
296 
297     /**
298      * @return the current registration connection type. Valid values are
299      * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}
300      * @hide
301      */
302     @VisibleForTesting
getConnectionType()303     public final @ImsRegistrationTech int getConnectionType() {
304         synchronized (mLock) {
305             return mConnectionType;
306         }
307     }
308 
309     /**
310      * @param c the newly registered callback that will be updated with the current registration
311      *         state.
312      */
updateNewCallbackWithState(IImsRegistrationCallback c)313     private void updateNewCallbackWithState(IImsRegistrationCallback c) throws RemoteException {
314         int state;
315         ImsReasonInfo disconnectInfo;
316         synchronized (mLock) {
317             state = mRegistrationState;
318             disconnectInfo = mLastDisconnectCause;
319         }
320         switch (state) {
321             case REGISTRATION_STATE_NOT_REGISTERED: {
322                 c.onDeregistered(disconnectInfo);
323                 break;
324             }
325             case REGISTRATION_STATE_REGISTERING: {
326                 c.onRegistering(getConnectionType());
327                 break;
328             }
329             case REGISTRATION_STATE_REGISTERED: {
330                 c.onRegistered(getConnectionType());
331                 break;
332             }
333             case REGISTRATION_STATE_UNKNOWN: {
334                 // Do not callback if the state has not been updated yet by the ImsService.
335                 break;
336             }
337         }
338     }
339 }
340