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;
18 
19 import android.Manifest;
20 import android.annotation.CallbackExecutor;
21 import android.annotation.IntDef;
22 import android.annotation.NonNull;
23 import android.annotation.RequiresPermission;
24 import android.annotation.SystemApi;
25 import android.annotation.TestApi;
26 import android.content.Context;
27 import android.net.Uri;
28 import android.os.Binder;
29 import android.os.IBinder;
30 import android.os.RemoteException;
31 import android.os.ServiceManager;
32 import android.telephony.ims.aidl.IImsRcsController;
33 import android.telephony.ims.aidl.IRcsUceControllerCallback;
34 import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
35 import android.util.Log;
36 
37 import java.lang.annotation.Retention;
38 import java.lang.annotation.RetentionPolicy;
39 import java.util.List;
40 import java.util.concurrent.Executor;
41 
42 /**
43  * Manages RCS User Capability Exchange for the subscription specified.
44  *
45  * @see ImsRcsManager#getUceAdapter() for information on creating an instance of this class.
46  */
47 public class RcsUceAdapter {
48     private static final String TAG = "RcsUceAdapter";
49 
50     /**
51      * An unknown error has caused the request to fail.
52      * @hide
53      */
54     public static final int ERROR_GENERIC_FAILURE = 1;
55     /**
56      * The carrier network does not have UCE support enabled for this subscriber.
57      * @hide
58      */
59     public static final int ERROR_NOT_ENABLED = 2;
60     /**
61      * The data network that the device is connected to does not support UCE currently (e.g. it is
62      * 1x only currently).
63      * @hide
64      */
65     public static final int ERROR_NOT_AVAILABLE = 3;
66     /**
67      * The network has responded with SIP 403 error and a reason "User not registered."
68      * @hide
69      */
70     public static final int ERROR_NOT_REGISTERED = 4;
71     /**
72      * The network has responded to this request with a SIP 403 error and reason "not authorized for
73      * presence" for this subscriber.
74      * @hide
75      */
76     public static final int ERROR_NOT_AUTHORIZED = 5;
77     /**
78      * The network has responded to this request with a SIP 403 error and no reason.
79      * @hide
80      */
81     public static final int ERROR_FORBIDDEN = 6;
82     /**
83      * The contact URI requested is not provisioned for VoLTE or it is not known as an IMS
84      * subscriber to the carrier network.
85      * @hide
86      */
87     public static final int ERROR_NOT_FOUND = 7;
88     /**
89      * The capabilities request contained too many URIs for the carrier network to handle. Retry
90      * with a lower number of contact numbers. The number varies per carrier.
91      * @hide
92      */
93     // TODO: Try to integrate this into the API so that the service will split based on carrier.
94     public static final int ERROR_REQUEST_TOO_LARGE = 8;
95     /**
96      * The network did not respond to the capabilities request before the request timed out.
97      * @hide
98      */
99     public static final int ERROR_REQUEST_TIMEOUT = 10;
100     /**
101      * The request failed due to the service having insufficient memory.
102      * @hide
103      */
104     public static final int ERROR_INSUFFICIENT_MEMORY = 11;
105     /**
106      * The network was lost while trying to complete the request.
107      * @hide
108      */
109     public static final int ERROR_LOST_NETWORK = 12;
110     /**
111      * The request has failed because the same request has already been added to the queue.
112      * @hide
113      */
114     public static final int ERROR_ALREADY_IN_QUEUE = 13;
115 
116     /**@hide*/
117     @Retention(RetentionPolicy.SOURCE)
118     @IntDef(prefix = "ERROR_", value = {
119             ERROR_GENERIC_FAILURE,
120             ERROR_NOT_ENABLED,
121             ERROR_NOT_AVAILABLE,
122             ERROR_NOT_REGISTERED,
123             ERROR_NOT_AUTHORIZED,
124             ERROR_FORBIDDEN,
125             ERROR_NOT_FOUND,
126             ERROR_REQUEST_TOO_LARGE,
127             ERROR_REQUEST_TIMEOUT,
128             ERROR_INSUFFICIENT_MEMORY,
129             ERROR_LOST_NETWORK,
130             ERROR_ALREADY_IN_QUEUE
131     })
132     public @interface ErrorCode {}
133 
134     /**
135      * The last publish has resulted in a "200 OK" response or the device is using SIP OPTIONS for
136      * UCE.
137      * @hide
138      */
139     public static final int PUBLISH_STATE_OK = 1;
140 
141     /**
142      * The hasn't published its capabilities since boot or hasn't gotten any publish response yet.
143      * @hide
144      */
145     public static final int PUBLISH_STATE_NOT_PUBLISHED = 2;
146 
147     /**
148      * The device has tried to publish its capabilities, which has resulted in an error. This error
149      * is related to the fact that the device is not VoLTE provisioned.
150      * @hide
151      */
152     public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3;
153 
154     /**
155      * The device has tried to publish its capabilities, which has resulted in an error. This error
156      * is related to the fact that the device is not RCS or UCE provisioned.
157      * @hide
158      */
159     public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4;
160 
161     /**
162      * The last publish resulted in a "408 Request Timeout" response.
163      * @hide
164      */
165     public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5;
166 
167     /**
168      * The last publish resulted in another unknown error, such as SIP 503 - "Service Unavailable"
169      * or SIP 423 - "Interval too short".
170      * <p>
171      * Device shall retry with exponential back-off.
172      * @hide
173      */
174     public static final int PUBLISH_STATE_OTHER_ERROR = 6;
175 
176     /**@hide*/
177     @Retention(RetentionPolicy.SOURCE)
178     @IntDef(prefix = "PUBLISH_STATE_", value = {
179             PUBLISH_STATE_OK,
180             PUBLISH_STATE_NOT_PUBLISHED,
181             PUBLISH_STATE_VOLTE_PROVISION_ERROR,
182             PUBLISH_STATE_RCS_PROVISION_ERROR,
183             PUBLISH_STATE_REQUEST_TIMEOUT,
184             PUBLISH_STATE_OTHER_ERROR
185     })
186     public @interface PublishState {}
187 
188     /**
189      * An application can use {@link #registerPublishStateCallback} to register a
190      * {@link PublishStateCallback), which will notify the user when the publish state to the
191      * network changes.
192      * @hide
193      */
194     public static class PublishStateCallback {
195 
196         private static class PublishStateBinder extends IRcsUcePublishStateCallback.Stub {
197 
198             private final PublishStateCallback mLocalCallback;
199             private Executor mExecutor;
200 
PublishStateBinder(PublishStateCallback c)201             PublishStateBinder(PublishStateCallback c) {
202                 mLocalCallback = c;
203             }
204 
205             @Override
onPublishStateChanged(int publishState)206             public void onPublishStateChanged(int publishState) {
207                 if (mLocalCallback == null) return;
208 
209                 long callingIdentity = Binder.clearCallingIdentity();
210                 try {
211                     mExecutor.execute(() -> mLocalCallback.onChanged(publishState));
212                 } finally {
213                     restoreCallingIdentity(callingIdentity);
214                 }
215             }
216 
setExecutor(Executor executor)217             private void setExecutor(Executor executor) {
218                 mExecutor = executor;
219             }
220         }
221 
222         private final PublishStateBinder mBinder = new PublishStateBinder(this);
223 
224         /**@hide*/
getBinder()225         public final IRcsUcePublishStateCallback getBinder() {
226             return mBinder;
227         }
228 
setExecutor(Executor executor)229         private void setExecutor(Executor executor) {
230             mBinder.setExecutor(executor);
231         }
232 
233         /**
234          * Notifies the callback when the publish state has changed.
235          * @param publishState The latest update to the publish state.
236          */
onChanged(@ublishState int publishState)237         public void onChanged(@PublishState int publishState) {
238         }
239     }
240 
241     /**
242      * Provides a one-time callback for the response to a UCE request. After this callback is called
243      * by the framework, the reference to this callback will be discarded on the service side.
244      * @see #requestCapabilities(Executor, List, CapabilitiesCallback)
245      * @hide
246      */
247     public static class CapabilitiesCallback {
248 
249         /**
250          * Notify this application that the pending capability request has returned successfully.
251          * @param contactCapabilities List of capabilities associated with each contact requested.
252          */
onCapabilitiesReceived( @onNull List<RcsContactUceCapability> contactCapabilities)253         public void onCapabilitiesReceived(
254                 @NonNull List<RcsContactUceCapability> contactCapabilities) {
255 
256         }
257 
258         /**
259          * The pending request has resulted in an error and may need to be retried, depending on the
260          * error code.
261          * @param errorCode The reason for the framework being unable to process the request.
262          */
onError(@rrorCode int errorCode)263         public void onError(@ErrorCode int errorCode) {
264 
265         }
266     }
267 
268     private final Context mContext;
269     private final int mSubId;
270 
271     /**
272      * Not to be instantiated directly, use
273      * {@link ImsRcsManager#getUceAdapter()} to instantiate this manager class.
274      * @hide
275      */
RcsUceAdapter(Context context, int subId)276     RcsUceAdapter(Context context, int subId) {
277         mContext = context;
278         mSubId = subId;
279     }
280 
281     /**
282      * Request the User Capability Exchange capabilities for one or more contacts.
283      * <p>
284      * Be sure to check the availability of this feature using
285      * {@link ImsRcsManager#isAvailable(int)} and ensuring
286      * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or
287      * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is enabled or else
288      * this operation will fail with {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}.
289      *
290      * @param executor The executor that will be used when the request is completed and the
291      *         {@link CapabilitiesCallback} is called.
292      * @param contactNumbers A list of numbers that the capabilities are being requested for.
293      * @param c A one-time callback for when the request for capabilities completes or there is an
294      *         error processing the request.
295      * @throws ImsException if the subscription associated with this instance of
296      * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
297      * available. This can happen if the ImsService has crashed, for example, or if the subscription
298      * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
299      * @hide
300      */
301     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
requestCapabilities(@onNull @allbackExecutor Executor executor, @NonNull List<Uri> contactNumbers, @NonNull CapabilitiesCallback c)302     public void requestCapabilities(@NonNull @CallbackExecutor Executor executor,
303             @NonNull List<Uri> contactNumbers,
304             @NonNull CapabilitiesCallback c) throws ImsException {
305         if (c == null) {
306             throw new IllegalArgumentException("Must include a non-null AvailabilityCallback.");
307         }
308         if (executor == null) {
309             throw new IllegalArgumentException("Must include a non-null Executor.");
310         }
311         if (contactNumbers == null) {
312             throw new IllegalArgumentException("Must include non-null contact number list.");
313         }
314 
315         IImsRcsController imsRcsController = getIImsRcsController();
316         if (imsRcsController == null) {
317             Log.e(TAG, "requestCapabilities: IImsRcsController is null");
318             throw new ImsException("Can not find remote IMS service",
319                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
320         }
321 
322         IRcsUceControllerCallback internalCallback = new IRcsUceControllerCallback.Stub() {
323             @Override
324             public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) {
325                 long callingIdentity = Binder.clearCallingIdentity();
326                 try {
327                     executor.execute(() ->
328                             c.onCapabilitiesReceived(contactCapabilities));
329                 } finally {
330                     restoreCallingIdentity(callingIdentity);
331                 }
332             }
333             @Override
334             public void onError(int errorCode) {
335                 long callingIdentity = Binder.clearCallingIdentity();
336                 try {
337                     executor.execute(() -> c.onError(errorCode));
338                 } finally {
339                     restoreCallingIdentity(callingIdentity);
340                 }
341             }
342         };
343 
344         try {
345             imsRcsController.requestCapabilities(mSubId, mContext.getOpPackageName(),
346                     null /*featureId*/, contactNumbers, internalCallback);
347         } catch (RemoteException e) {
348             Log.e(TAG, "Error calling IImsRcsController#requestCapabilities", e);
349             throw new ImsException("Remote IMS Service is not available",
350                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
351         }
352     }
353 
354     /**
355      * Gets the last publish result from the UCE service if the device is using an RCS presence
356      * server.
357      * @return The last publish result from the UCE service. If the device is using SIP OPTIONS,
358      * this method will return {@link #PUBLISH_STATE_OK} as well.
359      * @throws ImsException if the subscription associated with this instance of
360      * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
361      * available. This can happen if the ImsService has crashed, for example, or if the subscription
362      * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
363      * @hide
364      */
365     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
getUcePublishState()366     public @PublishState int getUcePublishState() throws ImsException {
367         IImsRcsController imsRcsController = getIImsRcsController();
368         if (imsRcsController == null) {
369             Log.e(TAG, "getUcePublishState: IImsRcsController is null");
370             throw new ImsException("Can not find remote IMS service",
371                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
372         }
373 
374         try {
375             return imsRcsController.getUcePublishState(mSubId);
376         } catch (android.os.ServiceSpecificException e) {
377             throw new ImsException(e.getMessage(), e.errorCode);
378         } catch (RemoteException e) {
379             Log.e(TAG, "Error calling IImsRcsController#getUcePublishState", e);
380             throw new ImsException("Remote IMS Service is not available",
381                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
382         }
383     }
384 
385     /**
386      * Registers a {@link PublishStateCallback} with the system, which will provide publish state
387      * updates for the subscription specified in {@link ImsManager@getRcsManager(subid)}.
388      * <p>
389      * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to subscription
390      * changed events and call {@link #unregisterPublishStateCallback} to clean up.
391      * <p>
392      * The registered {@link PublishStateCallback} will also receive a callback when it is
393      * registered with the current publish state.
394      *
395      * @param executor The executor the listener callback events should be run on.
396      * @param c The {@link PublishStateCallback} to be added.
397      * @throws ImsException if the subscription associated with this callback is valid, but
398      * the {@link ImsService} associated with the subscription is not available. This can happen if
399      * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
400      * reason.
401      * @hide
402      */
403     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
registerPublishStateCallback(@onNull @allbackExecutor Executor executor, @NonNull PublishStateCallback c)404     public void registerPublishStateCallback(@NonNull @CallbackExecutor Executor executor,
405             @NonNull PublishStateCallback c) throws ImsException {
406         if (c == null) {
407             throw new IllegalArgumentException("Must include a non-null PublishStateCallback.");
408         }
409         if (executor == null) {
410             throw new IllegalArgumentException("Must include a non-null Executor.");
411         }
412 
413         IImsRcsController imsRcsController = getIImsRcsController();
414         if (imsRcsController == null) {
415             Log.e(TAG, "registerPublishStateCallback : IImsRcsController is null");
416             throw new ImsException("Cannot find remote IMS service",
417                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
418         }
419 
420         c.setExecutor(executor);
421         try {
422             imsRcsController.registerUcePublishStateCallback(mSubId, c.getBinder());
423         } catch (android.os.ServiceSpecificException e) {
424             throw new ImsException(e.getMessage(), e.errorCode);
425         } catch (RemoteException e) {
426             Log.e(TAG, "Error calling IImsRcsController#registerUcePublishStateCallback", e);
427             throw new ImsException("Remote IMS Service is not available",
428                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
429         }
430     }
431 
432     /**
433      * Removes an existing {@link PublishStateCallback}.
434      * <p>
435      * When the subscription associated with this callback is removed
436      * (SIM removed, ESIM swap,etc...), this callback will automatically be removed. If this method
437      * is called for an inactive subscription, it will result in a no-op.
438      *
439      * @param c The callback to be unregistered.
440      * @throws ImsException if the subscription associated with this callback is valid, but
441      * the {@link ImsService} associated with the subscription is not available. This can happen if
442      * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
443      * reason.
444      * @hide
445      */
446     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
unregisterPublishStateCallback(@onNull PublishStateCallback c)447     public void unregisterPublishStateCallback(@NonNull PublishStateCallback c)
448             throws ImsException {
449         if (c == null) {
450             throw new IllegalArgumentException("Must include a non-null PublishStateCallback.");
451         }
452         IImsRcsController imsRcsController = getIImsRcsController();
453         if (imsRcsController == null) {
454             Log.e(TAG, "unregisterPublishStateCallback: IImsRcsController is null");
455             throw new ImsException("Cannot find remote IMS service",
456                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
457         }
458 
459         try {
460             imsRcsController.unregisterUcePublishStateCallback(mSubId, c.getBinder());
461         } catch (android.os.ServiceSpecificException e) {
462             throw new ImsException(e.getMessage(), e.errorCode);
463         } catch (RemoteException e) {
464             Log.e(TAG, "Error calling IImsRcsController#unregisterUcePublishStateCallback", e);
465             throw new ImsException("Remote IMS Service is not available",
466                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
467         }
468     }
469 
470     /**
471      * The user’s setting for whether or not User Capability Exchange (UCE) is enabled for the
472      * associated subscription.
473      * <p>
474      * Note: This setting does not affect whether or not the device publishes its service
475      * capabilities if the subscription supports presence publication.
476      *
477      * @return true if the user’s setting for UCE is enabled, false otherwise.
478      * @throws ImsException if the subscription associated with this instance of
479      * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
480      * available. This can happen if the ImsService has crashed, for example, or if the subscription
481      * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
482      */
483     @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
isUceSettingEnabled()484     public boolean isUceSettingEnabled() throws ImsException {
485         IImsRcsController imsRcsController = getIImsRcsController();
486         if (imsRcsController == null) {
487             Log.e(TAG, "isUceSettingEnabled: IImsRcsController is null");
488             throw new ImsException("Can not find remote IMS service",
489                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
490         }
491         try {
492             // Telephony.SimInfo#IMS_RCS_UCE_ENABLED can also be used to listen to changes to this.
493             return imsRcsController.isUceSettingEnabled(mSubId, mContext.getOpPackageName(),
494                     null /*featureId*/);
495         } catch (RemoteException e) {
496             Log.e(TAG, "Error calling IImsRcsController#isUceSettingEnabled", e);
497             throw new ImsException("Remote IMS Service is not available",
498                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
499         }
500     }
501 
502     /**
503      * Change the user’s setting for whether or not UCE is enabled for the associated subscription.
504      * <p>
505      * If an application Requires UCE, they will launch an Activity using the Intent
506      * {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN}, which will ask the user if
507      * they wish to enable this feature. This setting should only be enabled after the user has
508      * opted-in to capability exchange.
509      * <p>
510      * Note: This setting does not affect whether or not the device publishes its service
511      * capabilities if the subscription supports presence publication.
512      *
513      * @param isEnabled the user's setting for whether or not they wish for User
514      *         Capability Exchange to be enabled.
515      * @throws ImsException if the subscription associated with this instance of
516      * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
517      * available. This can happen if the ImsService has crashed, for example, or if the subscription
518      * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
519      * @hide
520      */
521     @SystemApi
522     @TestApi
523     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
setUceSettingEnabled(boolean isEnabled)524     public void setUceSettingEnabled(boolean isEnabled) throws ImsException {
525         IImsRcsController imsRcsController = getIImsRcsController();
526         if (imsRcsController == null) {
527             Log.e(TAG, "setUceSettingEnabled: IImsRcsController is null");
528             throw new ImsException("Can not find remote IMS service",
529                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
530         }
531 
532         try {
533             imsRcsController.setUceSettingEnabled(mSubId, isEnabled);
534         } catch (RemoteException e) {
535             Log.e(TAG, "Error calling IImsRcsController#setUceSettingEnabled", e);
536             throw new ImsException("Remote IMS Service is not available",
537                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
538         }
539     }
540 
getIImsRcsController()541     private IImsRcsController getIImsRcsController() {
542         IBinder binder = ServiceManager.getService(Context.TELEPHONY_IMS_SERVICE);
543         return IImsRcsController.Stub.asInterface(binder);
544     }
545 }
546