1 /*
2  * Copyright (C) 2018 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.NonNull;
21 import android.net.Uri;
22 import android.os.RemoteException;
23 import android.telephony.ims.ImsException;
24 import android.telephony.ims.RcsContactUceCapability;
25 import android.telephony.ims.feature.ImsFeature;
26 import android.telephony.ims.feature.RcsFeature;
27 import android.util.Log;
28 
29 import java.lang.annotation.Retention;
30 import java.lang.annotation.RetentionPolicy;
31 import java.util.List;
32 
33 /**
34  * Base implementation for RCS User Capability Exchange using Presence. Any ImsService implementing
35  * this service must implement the stub methods {@link #requestCapabilities(List, int)}  and
36  * {@link #updateCapabilities(RcsContactUceCapability, int)}.
37  *
38  * @hide
39  */
40 public class RcsPresenceExchangeImplBase extends RcsCapabilityExchange {
41 
42     private static final String LOG_TAG = "RcsPresenceExchangeIB";
43 
44     /**
45      * The request has resulted in any other 4xx/5xx/6xx that is not covered below. No retry will be
46      * attempted.
47      */
48     public static final int RESPONSE_SUBSCRIBE_GENERIC_FAILURE = -1;
49 
50     /**
51      * The request has succeeded with a “200” message from the network.
52      */
53     public static final int RESPONSE_SUCCESS = 0;
54 
55     /**
56      * The request has resulted in a “403” (User Not Registered) error from the network. Will retry
57      * capability polling with an exponential backoff.
58      */
59     public static final int RESPONSE_NOT_REGISTERED = 1;
60 
61     /**
62      * The request has resulted in a “403” (not authorized (Requestor)) error from the network. No
63      * retry will be attempted.
64      */
65     public static final int RESPONSE_NOT_AUTHORIZED_FOR_PRESENCE = 2;
66 
67     /**
68      * The request has resulted in a "403” (Forbidden) or other “403” error from the network and
69      * will be handled the same as “404” Not found. No retry will be attempted.
70      */
71     public static final int RESPONSE_FORBIDDEN = 3;
72 
73     /**
74      * The request has resulted in a “404” (Not found) result from the network. No retry will be
75      * attempted.
76      */
77     public static final int RESPONSE_NOT_FOUND = 4;
78 
79     /**
80      * The request has resulted in a “408” response. Retry after exponential backoff.
81      */
82     public static final int RESPONSE_SIP_REQUEST_TIMEOUT = 5;
83 
84     /**
85      *  The network has responded with a “413” (Too Large) response from the network. Capability
86      *  request contains too many items and must be shrunk before the request will be accepted.
87      */
88     public static final int RESPONSE_SUBSCRIBE_TOO_LARGE = 6;
89 
90     /**
91      * The request has resulted in a “423” response. Retry after exponential backoff.
92      */
93     public static final int RESPONSE_SIP_INTERVAL_TOO_SHORT = 7;
94 
95     /**
96      * The request has resulted in a “503” response. Retry after exponential backoff.
97      */
98     public static final int RESPONSE_SIP_SERVICE_UNAVAILABLE = 8;
99 
100     /** @hide*/
101     @Retention(RetentionPolicy.SOURCE)
102     @IntDef(prefix = "RESPONSE_", value = {
103             RESPONSE_SUBSCRIBE_GENERIC_FAILURE,
104             RESPONSE_SUCCESS,
105             RESPONSE_NOT_REGISTERED,
106             RESPONSE_NOT_AUTHORIZED_FOR_PRESENCE,
107             RESPONSE_FORBIDDEN,
108             RESPONSE_NOT_FOUND,
109             RESPONSE_SIP_REQUEST_TIMEOUT,
110             RESPONSE_SUBSCRIBE_TOO_LARGE,
111             RESPONSE_SIP_INTERVAL_TOO_SHORT,
112             RESPONSE_SIP_SERVICE_UNAVAILABLE
113     })
114     public @interface PresenceResponseCode {}
115 
116 
117     /** A capability update has been requested due to the Entity Tag (ETag) expiring. */
118     public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 0;
119     /** A capability update has been requested due to moving to LTE with VoPS disabled. */
120     public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 1;
121     /** A capability update has been requested due to moving to LTE with VoPS enabled. */
122     public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 2;
123     /** A capability update has been requested due to moving to eHRPD. */
124     public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 3;
125     /** A capability update has been requested due to moving to HSPA+. */
126     public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 4;
127     /** A capability update has been requested due to moving to 3G. */
128     public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 5;
129     /** A capability update has been requested due to moving to 2G. */
130     public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 6;
131     /** A capability update has been requested due to moving to WLAN */
132     public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 7;
133     /** A capability update has been requested due to moving to IWLAN */
134     public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 8;
135     /** A capability update has been requested but the reason is unknown. */
136     public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 9;
137     /** A capability update has been requested due to moving to 5G NR with VoPS disabled. */
138     public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10;
139     /** A capability update has been requested due to moving to 5G NR with VoPS enabled. */
140     public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11;
141 
142     /** @hide*/
143     @IntDef(value = {
144             CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED,
145             CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED,
146             CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED,
147             CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD,
148             CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS,
149             CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G,
150             CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G,
151             CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN,
152             CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN,
153             CAPABILITY_UPDATE_TRIGGER_UNKNOWN,
154             CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED,
155             CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED
156     }, prefix = "CAPABILITY_UPDATE_TRIGGER_")
157     @Retention(RetentionPolicy.SOURCE)
158     public @interface StackPublishTriggerType {
159     }
160 
161     /**
162      * Provide the framework with a subsequent network response update to
163      * {@link #updateCapabilities(RcsContactUceCapability, int)} and
164      * {@link #requestCapabilities(List, int)} operations.
165      *
166      * @param code The SIP response code sent from the network for the operation token specified.
167      * @param reason The optional reason response from the network. If the network provided no
168      *         reason with the code, the string should be empty.
169      * @param operationToken The token associated with the operation this service is providing a
170      *         response for.
171      * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently
172      * connected to the framework. This can happen if the {@link RcsFeature} is not
173      * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
174      * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
175      * Telephony stack has crashed.
176      */
onNetworkResponse(@resenceResponseCode int code, @NonNull String reason, int operationToken)177     public final void onNetworkResponse(@PresenceResponseCode int code, @NonNull String reason,
178             int operationToken) throws ImsException {
179         try {
180             getListener().onNetworkResponse(code, reason, operationToken);
181         } catch (RemoteException e) {
182             throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
183         }
184     }
185 
186     /**
187      * Provides the framework with the requested contacts’ capabilities requested by the framework
188      * using {@link #requestCapabilities(List, int)}.
189      *
190      * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently
191      * connected to the framework. This can happen if the {@link RcsFeature} is not
192      * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
193      * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
194      * Telephony stack has crashed.
195      */
onCapabilityRequestResponse(@onNull List<RcsContactUceCapability> infos, int operationToken)196     public final void onCapabilityRequestResponse(@NonNull List<RcsContactUceCapability> infos,
197             int operationToken) throws ImsException {
198         try {
199             getListener().onCapabilityRequestResponsePresence(infos, operationToken);
200         } catch (RemoteException e) {
201             throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
202         }
203     }
204 
205     /**
206      * Trigger the framework to provide a capability update using
207      * {@link #updateCapabilities(RcsContactUceCapability, int)}.
208      * <p>
209      * This is typically used when trying to generate an initial PUBLISH for a new subscription to
210      * the network. The device will cache all presence publications after boot until this method is
211      * called once.
212      * @param publishTriggerType {@link StackPublishTriggerType} The reason for the capability
213      *         update request.
214      * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently
215      * connected to the framework. This can happen if the {@link RcsFeature} is not
216      * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
217      * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
218      * Telephony stack has crashed.
219      */
onNotifyUpdateCapabilites(@tackPublishTriggerType int publishTriggerType)220     public final void onNotifyUpdateCapabilites(@StackPublishTriggerType int publishTriggerType)
221             throws ImsException {
222         try {
223             getListener().onNotifyUpdateCapabilities(publishTriggerType);
224         } catch (RemoteException e) {
225             throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
226         }
227     }
228 
229     /**
230      * Notify the framework that the device’s capabilities have been unpublished from the network.
231      *
232      * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently
233      * connected to the framework. This can happen if the {@link RcsFeature} is not
234      * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
235      * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
236      * Telephony stack has crashed.
237      */
onUnpublish()238     public final void onUnpublish() throws ImsException {
239         try {
240             getListener().onUnpublish();
241         } catch (RemoteException e) {
242             throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
243         }
244     }
245 
246     /**
247      * The user capabilities of one or multiple contacts have been requested by the framework.
248      * <p>
249      * The implementer must follow up this call with an {@link #onCommandUpdate(int, int)} call to
250      * indicate whether or not this operation succeeded.  If this operation succeeds, network
251      * response updates should be sent to the framework using
252      * {@link #onNetworkResponse(int, String, int)}. When the operation is completed,
253      * {@link #onCapabilityRequestResponse(List, int)} should be called with the presence
254      * information for the contacts specified.
255      * @param uris A {@link List} of the {@link Uri}s that the framework is requesting the UCE
256      *             capabilities for.
257      * @param operationToken The token associated with this operation. Updates to this request using
258      *         {@link #onCommandUpdate(int, int)}, {@link #onNetworkResponse(int, String, int)}, and
259      *         {@link #onCapabilityRequestResponse(List, int)}  must use the same operation token
260      *         in response.
261      */
requestCapabilities(@onNull List<Uri> uris, int operationToken)262     public void requestCapabilities(@NonNull List<Uri> uris, int operationToken) {
263         // Stub - to be implemented by service
264         Log.w(LOG_TAG, "requestCapabilities called with no implementation.");
265         try {
266             getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken);
267         } catch (RemoteException | ImsException e) {
268             // Do not do anything, this is a stub implementation.
269         }
270     }
271 
272     /**
273      * The capabilities of this device have been updated and should be published to the network.
274      * <p>
275      * The implementer must follow up this call with an {@link #onCommandUpdate(int, int)} call to
276      * indicate whether or not this operation succeeded. If this operation succeeds, network
277      * response updates should be sent to the framework using
278      * {@link #onNetworkResponse(int, String, int)}.
279      * @param capabilities The capabilities for this device.
280      * @param operationToken The token associated with this operation. Any subsequent
281      *         {@link #onCommandUpdate(int, int)} or {@link #onNetworkResponse(int, String, int)}
282      *         calls regarding this update must use the same token.
283      */
updateCapabilities(@onNull RcsContactUceCapability capabilities, int operationToken)284     public void updateCapabilities(@NonNull RcsContactUceCapability capabilities,
285             int operationToken) {
286         // Stub - to be implemented by service
287         Log.w(LOG_TAG, "updateCapabilities called with no implementation.");
288         try {
289             getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken);
290         } catch (RemoteException | ImsException e) {
291             // Do not do anything, this is a stub implementation.
292         }
293     }
294 }
295