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