1 /*
2  * Copyright (c) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.telephony.ims.stub;
18 
19 import android.annotation.IntDef;
20 import android.annotation.IntRange;
21 import android.annotation.NonNull;
22 import android.annotation.SuppressLint;
23 import android.annotation.SystemApi;
24 import android.net.Uri;
25 import android.telephony.ims.ImsException;
26 import android.telephony.ims.RcsUceAdapter;
27 import android.telephony.ims.feature.ImsFeature;
28 import android.telephony.ims.feature.RcsFeature;
29 import android.util.Log;
30 import android.util.Pair;
31 
32 import java.lang.annotation.Retention;
33 import java.lang.annotation.RetentionPolicy;
34 import java.util.Collection;
35 import java.util.List;
36 import java.util.Set;
37 import java.util.concurrent.Executor;
38 
39 /**
40  * Extend this base class to implement RCS User Capability Exchange (UCE) for the AOSP platform
41  * using the vendor ImsService.
42  * <p>
43  * See RCC.07 for more details on UCE as well as how UCE should be implemented.
44  * @hide
45  */
46 @SystemApi
47 public class RcsCapabilityExchangeImplBase {
48 
49     private static final String LOG_TAG = "RcsCapExchangeImplBase";
50 
51     /**
52      * Service is unknown.
53      */
54     public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0;
55 
56     /**
57      * The command failed with an unknown error.
58      */
59     public static final int COMMAND_CODE_GENERIC_FAILURE = 1;
60 
61     /**
62      * Invalid parameter(s).
63      */
64     public static final int COMMAND_CODE_INVALID_PARAM = 2;
65 
66     /**
67      * Fetch error.
68      */
69     public static final int COMMAND_CODE_FETCH_ERROR = 3;
70 
71     /**
72      * Request timed out.
73      */
74     public static final int COMMAND_CODE_REQUEST_TIMEOUT = 4;
75 
76     /**
77      * Failure due to insufficient memory available.
78      */
79     public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 5;
80 
81     /**
82      * Network connection is lost.
83      */
84     public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 6;
85 
86     /**
87      * Requested feature/resource is not supported.
88      */
89     public static final int COMMAND_CODE_NOT_SUPPORTED = 7;
90 
91     /**
92      * Contact or resource is not found.
93      */
94     public static final int COMMAND_CODE_NOT_FOUND = 8;
95 
96     /**
97      * Service is not available.
98      */
99     public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 9;
100 
101     /**
102      * Command resulted in no change in state, ignoring.
103      */
104     public static final int COMMAND_CODE_NO_CHANGE = 10;
105 
106     /**@hide*/
107     @Retention(RetentionPolicy.SOURCE)
108     @IntDef(prefix = "COMMAND_CODE_", value = {
109             COMMAND_CODE_SERVICE_UNKNOWN,
110             COMMAND_CODE_GENERIC_FAILURE,
111             COMMAND_CODE_INVALID_PARAM,
112             COMMAND_CODE_FETCH_ERROR,
113             COMMAND_CODE_REQUEST_TIMEOUT,
114             COMMAND_CODE_INSUFFICIENT_MEMORY,
115             COMMAND_CODE_LOST_NETWORK_CONNECTION,
116             COMMAND_CODE_NOT_SUPPORTED,
117             COMMAND_CODE_NOT_FOUND,
118             COMMAND_CODE_SERVICE_UNAVAILABLE,
119             COMMAND_CODE_NO_CHANGE
120     })
121     public @interface CommandCode {}
122 
123     /**
124      * Interface used by the framework to receive the response of the publish request.
125      */
126     public interface PublishResponseCallback {
127         /**
128          * Notify the framework that the command associated with the
129          * {@link #publishCapabilities(String, PublishResponseCallback)} has failed.
130          *
131          * @param code The reason why the associated command has failed.
132          * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
133          * not currently connected to the framework. This can happen if the {@link RcsFeature}
134          * is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
135          * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases
136          * when the Telephony stack has crashed.
137          */
onCommandError(@ommandCode int code)138         void onCommandError(@CommandCode int code) throws ImsException;
139 
140         /**
141          * Provide the framework with a subsequent network response update to
142          * {@link #publishCapabilities(String, PublishResponseCallback)}.
143          *
144          * If this network response also contains a “Reason” header, then the
145          * {@link #onNetworkResponse(int, String, int, String)} method should be used instead.
146          *
147          * @param sipCode The SIP response code sent from the network for the operation
148          * token specified.
149          * @param reason The optional reason response from the network. If there is a reason header
150          * included in the response, that should take precedence over the reason provided in the
151          * status line. If the network provided no reason with the sip code, the string should be
152          * empty.
153          * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
154          * not currently connected to the framework. This can happen if the {@link RcsFeature}
155          * is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
156          * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases
157          * when the Telephony stack has crashed.
158          */
onNetworkResponse(@ntRangefrom = 100, to = 699) int sipCode, @NonNull String reason)159         void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
160                 @NonNull String reason) throws ImsException;
161 
162         /**
163          * Provide the framework with a subsequent network response update to
164          * {@link #publishCapabilities(String, PublishResponseCallback)} that also
165          * includes a reason provided in the “reason” header. See RFC3326 for more
166          * information.
167          *
168          * @param sipCode The SIP response code sent from the network.
169          * @param reasonPhrase The optional reason response from the network. If the
170          * network provided no reason with the sip code, the string should be empty.
171          * @param reasonHeaderCause The “cause” parameter of the “reason” header
172          * included in the SIP message.
173          * @param reasonHeaderText The “text” parameter of the “reason” header
174          * included in the SIP message.
175          * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
176          * not currently connected to the framework. This can happen if the
177          * {@link RcsFeature} is not
178          * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
179          * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
180          * rare cases when the Telephony stack has crashed.
181          */
onNetworkResponse(@ntRangefrom = 100, to = 699) int sipCode, @NonNull String reasonPhrase, @IntRange(from = 100, to = 699) int reasonHeaderCause, @NonNull String reasonHeaderText)182         void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
183                 @NonNull String reasonPhrase,
184                 @IntRange(from = 100, to = 699) int reasonHeaderCause,
185                 @NonNull String reasonHeaderText) throws ImsException;
186     }
187 
188     /**
189      * Interface used by the framework to respond to OPTIONS requests.
190      */
191     public interface OptionsResponseCallback {
192         /**
193          * Notify the framework that the command associated with this callback has failed.
194          *
195          * @param code The reason why the associated command has failed.
196          * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
197          * not currently connected to the framework. This can happen if the
198          * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature}
199          * has not received the {@link ImsFeature#onFeatureReady()} callback. This may also happen
200          * in rare cases when the Telephony stack has crashed.
201          */
onCommandError(@ommandCode int code)202         void onCommandError(@CommandCode int code) throws ImsException;
203 
204         /**
205          * Send the response of a SIP OPTIONS capability exchange to the framework.
206          * @param sipCode The SIP response code that was sent by the network in response
207          * to the request sent by {@link #sendOptionsCapabilityRequest}.
208          * @param reason The optional SIP response reason sent by the network.
209          * If none was sent, this should be an empty string.
210          * @param theirCaps the contact's UCE capabilities associated with the
211          * capability request.
212          * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not
213          * currently connected to the framework. This can happen if the
214          * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
215          * {@link RcsFeature} has not received the
216          * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare
217          * cases when the Telephony stack has crashed.
218          */
onNetworkResponse(int sipCode, @NonNull String reason, @NonNull List<String> theirCaps)219         void onNetworkResponse(int sipCode, @NonNull String reason,
220                 @NonNull List<String> theirCaps) throws ImsException;
221     }
222 
223     /**
224      * Interface used by the framework to receive the response of the subscribe request.
225      */
226     public interface SubscribeResponseCallback {
227         /**
228          * Notify the framework that the command associated with this callback has failed.
229          * <p>
230          * Must only be called when there was an error generating a SUBSCRIBE request due to an
231          * IMS stack error. This is a terminating event, so no other callback event will be
232          * expected after this callback.
233          *
234          * @param code The reason why the associated command has failed.
235          * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
236          * not currently connected to the framework. This can happen if the
237          * {@link RcsFeature} is not
238          * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
239          * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
240          * rare cases when the Telephony stack has crashed.
241          */
onCommandError(@ommandCode int code)242         void onCommandError(@CommandCode int code) throws ImsException;
243 
244         /**
245          * Notify the framework of the response to the SUBSCRIBE request from
246          * {@link #subscribeForCapabilities(Collection, SubscribeResponseCallback)}.
247          * <p>
248          * If the carrier network responds to the SUBSCRIBE request with a 2XX response, then the
249          * framework will expect the IMS stack to call {@link #onNotifyCapabilitiesUpdate},
250          * {@link #onResourceTerminated}, and {@link #onTerminated} as required for the
251          * subsequent NOTIFY responses to the subscription.
252          *
253          * If this network response also contains a “Reason” header, then the
254          * {@link #onNetworkResponse(int, String, int, String)} method should be used instead.
255          *
256          * @param sipCode The SIP response code sent from the network for the operation
257          * token specified.
258          * @param reason The optional reason response from the network. If the network
259          *  provided no reason with the sip code, the string should be empty.
260          * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
261          * not currently connected to the framework. This can happen if the
262          * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
263          * {@link RcsFeature} has not received the {@link ImsFeature#onFeatureReady()} callback.
264          * This may also happen in rare cases when the Telephony stack has crashed.
265          */
onNetworkResponse(@ntRangefrom = 100, to = 699) int sipCode, @NonNull String reason)266         void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
267                 @NonNull String reason) throws ImsException;
268 
269         /**
270          * Notify the framework  of the response to the SUBSCRIBE request from
271          * {@link #subscribeForCapabilities(Collection, SubscribeResponseCallback)} that also
272          * includes a reason provided in the “reason” header. See RFC3326 for more
273          * information.
274          *
275          * @param sipCode The SIP response code sent from the network,
276          * @param reasonPhrase The optional reason response from the network. If the
277          * network provided no reason with the sip code, the string should be empty.
278          * @param reasonHeaderCause The “cause” parameter of the “reason” header
279          * included in the SIP message.
280          * @param reasonHeaderText The “text” parameter of the “reason” header
281          * included in the SIP message.
282          * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
283          * not currently connected to the framework. This can happen if the
284          * {@link RcsFeature} is not
285          * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
286          * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
287          * rare cases when the Telephony stack has crashed.
288          */
onNetworkResponse(@ntRangefrom = 100, to = 699) int sipCode, @NonNull String reasonPhrase, @IntRange(from = 100, to = 699) int reasonHeaderCause, @NonNull String reasonHeaderText)289         void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
290                 @NonNull String reasonPhrase,
291                 @IntRange(from = 100, to = 699) int reasonHeaderCause,
292                 @NonNull String reasonHeaderText) throws ImsException;
293 
294         /**
295          * Notify the framework of the latest XML PIDF documents included in the network response
296          * for the requested contacts' capabilities requested by the Framework using
297          * {@link RcsUceAdapter#requestCapabilities(List, Executor,
298          * RcsUceAdapter.CapabilitiesCallback)}.
299          * <p>
300          * The expected format for the PIDF XML is defined in RFC3861. Each XML document must be a
301          * "application/pidf+xml" object and start with a root <presence> element. For NOTIFY
302          * responses that contain RLMI information and potentially multiple PIDF XMLs, each
303          * PIDF XML should be separated and added as a separate item in the List. This should be
304          * called every time a new NOTIFY event is received with new capability information.
305          *
306          * @param pidfXmls The list of the PIDF XML data for the contact URIs that it subscribed
307          * for.
308          * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
309          * not currently connected to the framework.
310          * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
311          * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not
312          * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
313          * rare cases when the Telephony stack has crashed.
314          */
onNotifyCapabilitiesUpdate(@onNull List<String> pidfXmls)315         void onNotifyCapabilitiesUpdate(@NonNull List<String> pidfXmls) throws ImsException;
316 
317         /**
318          * Notify the framework that a resource in the RLMI XML contained in the NOTIFY response
319          * for the ongoing SUBSCRIBE dialog has been terminated.
320          * <p>
321          * This will be used to notify the framework that a contact URI that the IMS stack has
322          * subscribed to on the Resource List Server has been terminated as well as the reason why.
323          * Usually this means that there will not be any capability information for the contact URI
324          * that they subscribed for. See RFC 4662 for more information.
325          *
326          * @param uriTerminatedReason The contact URIs which have been terminated. Each pair in the
327          * list is the contact URI and its terminated reason.
328          * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
329          * not currently connected to the framework.
330          * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
331          * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not
332          * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
333          * rare cases when the Telephony stack has crashed.
334          */
onResourceTerminated( @onNull List<Pair<Uri, String>> uriTerminatedReason)335         void onResourceTerminated(
336                 @NonNull List<Pair<Uri, String>> uriTerminatedReason) throws ImsException;
337 
338         /**
339          * The subscription associated with a previous
340          * {@link RcsUceAdapter#requestCapabilities(List, Executor,
341          * RcsUceAdapter.CapabilitiesCallback)}
342          * operation has been terminated. This will mostly be due to the network sending a final
343          * NOTIFY response due to the subscription expiring, but this may also happen due to a
344          * network error.
345          *
346          * @param reason The reason for the request being unable to process.
347          * @param retryAfterMilliseconds The time in milliseconds the requesting application should
348          * wait before retrying, if non-zero.
349          * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
350          * not currently connected to the framework.
351          * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
352          * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not
353          * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
354          * rare cases when the Telephony stack has crashed.
355          */
onTerminated(@onNull String reason, long retryAfterMilliseconds)356         void onTerminated(@NonNull String reason, long retryAfterMilliseconds) throws ImsException;
357     }
358 
359     /**
360      * Create a new RcsCapabilityExchangeImplBase instance.
361      */
RcsCapabilityExchangeImplBase()362     public RcsCapabilityExchangeImplBase() {
363     }
364 
365     /**
366      * The user capabilities of one or multiple contacts have been requested by the framework.
367      * <p>
368      * The implementer must follow up this call with an
369      * {@link SubscribeResponseCallback#onCommandError} call to indicate this operation has failed.
370      * The response from the network to the SUBSCRIBE request must be sent back to the framework
371      * using {@link SubscribeResponseCallback#onNetworkResponse(int, String)}.
372      * As NOTIFY requests come in from the network, the requested contact’s capabilities should be
373      * sent back to the framework using
374      * {@link SubscribeResponseCallback#onNotifyCapabilitiesUpdate(List<String>}) and
375      * {@link SubscribeResponseCallback#onResourceTerminated(List<Pair<Uri, String>>)}
376      * should be called with the presence information for the contacts specified.
377      * <p>
378      * Once the subscription is terminated,
379      * {@link SubscribeResponseCallback#onTerminated(String, long)} must be called for the
380      * framework to finish listening for NOTIFY responses.
381      *
382      * @param uris A {@link Collection} of the {@link Uri}s that the framework is requesting the
383      * UCE capabilities for.
384      * @param cb The callback of the subscribe request.
385      */
386     // executor used is defined in the constructor.
387     @SuppressLint("ExecutorRegistration")
subscribeForCapabilities(@onNull Collection<Uri> uris, @NonNull SubscribeResponseCallback cb)388     public void subscribeForCapabilities(@NonNull Collection<Uri> uris,
389             @NonNull SubscribeResponseCallback cb) {
390         // Stub - to be implemented by service
391         Log.w(LOG_TAG, "subscribeForCapabilities called with no implementation.");
392         try {
393             cb.onCommandError(COMMAND_CODE_NOT_SUPPORTED);
394         } catch (ImsException e) {
395             // Do not do anything, this is a stub implementation.
396         }
397     }
398 
399     /**
400      * The capabilities of this device have been updated and should be published to the network.
401      * <p>
402      * If this operation succeeds, network response updates should be sent to the framework using
403      * {@link PublishResponseCallback#onNetworkResponse(int, String)}.
404      * @param pidfXml The XML PIDF document containing the capabilities of this device to be sent
405      * to the carrier’s presence server.
406      * @param cb The callback of the publish request
407      */
408     // executor used is defined in the constructor.
409     @SuppressLint("ExecutorRegistration")
publishCapabilities(@onNull String pidfXml, @NonNull PublishResponseCallback cb)410     public void publishCapabilities(@NonNull String pidfXml, @NonNull PublishResponseCallback cb) {
411         // Stub - to be implemented by service
412         Log.w(LOG_TAG, "publishCapabilities called with no implementation.");
413         try {
414             cb.onCommandError(COMMAND_CODE_NOT_SUPPORTED);
415         } catch (ImsException e) {
416             // Do not do anything, this is a stub implementation.
417         }
418     }
419 
420     /**
421      * Push one's own capabilities to a remote user via the SIP OPTIONS presence exchange mechanism
422      * in order to receive the capabilities of the remote user in response.
423      * <p>
424      * The implementer must use {@link OptionsResponseCallback} to send the response of
425      * this query from the network back to the framework.
426      * @param contactUri The URI of the remote user that we wish to get the capabilities of.
427      * @param myCapabilities The capabilities of this device to send to the remote user.
428      * @param callback The callback of this request which is sent from the remote user.
429      */
430     // executor used is defined in the constructor.
431     @SuppressLint("ExecutorRegistration")
sendOptionsCapabilityRequest(@onNull Uri contactUri, @NonNull Set<String> myCapabilities, @NonNull OptionsResponseCallback callback)432     public void sendOptionsCapabilityRequest(@NonNull Uri contactUri,
433             @NonNull Set<String> myCapabilities, @NonNull OptionsResponseCallback callback) {
434         // Stub - to be implemented by service
435         Log.w(LOG_TAG, "sendOptionsCapabilityRequest called with no implementation.");
436         try {
437             callback.onCommandError(COMMAND_CODE_NOT_SUPPORTED);
438         } catch (ImsException e) {
439             // Do not do anything, this is a stub implementation.
440         }
441     }
442 }
443