• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.RequiresFeature;
24 import android.annotation.RequiresPermission;
25 import android.annotation.SdkConstant;
26 import android.annotation.SystemApi;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.pm.PackageManager;
30 import android.os.Binder;
31 import android.os.IBinder;
32 import android.os.RemoteException;
33 import android.os.ServiceSpecificException;
34 import android.provider.Settings;
35 import android.telephony.AccessNetworkConstants;
36 import android.telephony.BinderCacheManager;
37 import android.telephony.TelephonyFrameworkInitializer;
38 import android.telephony.ims.aidl.IImsCapabilityCallback;
39 import android.telephony.ims.aidl.IImsRcsController;
40 import android.telephony.ims.feature.ImsFeature;
41 import android.telephony.ims.stub.ImsRegistrationImplBase;
42 import android.util.Log;
43 
44 import com.android.internal.telephony.IIntegerConsumer;
45 import com.android.internal.telephony.ITelephony;
46 
47 import java.lang.annotation.Retention;
48 import java.lang.annotation.RetentionPolicy;
49 import java.util.HashMap;
50 import java.util.Map;
51 import java.util.Objects;
52 import java.util.concurrent.Executor;
53 import java.util.function.Consumer;
54 
55 /**
56  * Manager for interfacing with the framework RCS services, including the User Capability Exchange
57  * (UCE) service, as well as managing user settings.
58  *
59  * Use {@link ImsManager#getImsRcsManager(int)} to create an instance of this manager.
60  */
61 @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
62 public class ImsRcsManager {
63     private static final String TAG = "ImsRcsManager";
64 
65     /**
66      * Activity Action: Show the opt-in dialog for enabling or disabling RCS contact discovery
67      * using User Capability Exchange (UCE), which enables a service that periodically shares the
68      * phone numbers of all of the contacts in the user's address book with the carrier to refresh
69      * the RCS capabilities associated with those contacts as the local cache becomes stale.
70      * <p>
71      * An application that depends on RCS contact discovery being enabled must send this intent
72      * using {@link Context#startActivity(Intent)} to ask the user to opt-in for contacts upload for
73      * capability exchange if it is currently disabled. Whether or not RCS contact discovery has
74      * been enabled by the user can be queried using {@link RcsUceAdapter#isUceSettingEnabled()}.
75      * <p>
76      * This intent will always be handled by the system, however the application should only send
77      * this Intent if the carrier supports bulk RCS contact exchange, which will be true if either
78      * key {@link android.telephony.CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL}
79      * or {@link android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL} is set to true.
80      * Otherwise, the RCS contact discovery opt-in dialog will not be shown.
81      * <p>
82      * Input: A mandatory {@link Settings#EXTRA_SUB_ID} extra containing the subscription that the
83      * setting will be be shown for.
84      * <p>
85      * Output: Nothing
86      * @see RcsUceAdapter
87      */
88     @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
89     public static final String ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN =
90             "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN";
91 
92     /**
93      * This carrier supports User Capability Exchange as, defined by the framework using a
94      * presence server. If set, the RcsFeature should support capability exchange. If not set, this
95      * RcsFeature should not publish capabilities or service capability requests.
96      * @hide
97      */
98     @Retention(RetentionPolicy.SOURCE)
99     @IntDef(prefix = "CAPABILITY_TYPE_", flag = true, value = {
100             CAPABILITY_TYPE_NONE,
101             CAPABILITY_TYPE_OPTIONS_UCE,
102             CAPABILITY_TYPE_PRESENCE_UCE
103     })
104     public @interface RcsImsCapabilityFlag {}
105 
106     /**
107      * Undefined capability type for initialization
108      */
109     public static final int CAPABILITY_TYPE_NONE = 0;
110 
111     /**
112      * This carrier supports User Capability Exchange using SIP OPTIONS as defined by the
113      * framework. If set, the RcsFeature should support capability exchange using SIP OPTIONS.
114      * If not set, this RcsFeature should not service capability requests.
115      */
116     public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1 << 0;
117 
118     /**
119      * This carrier supports User Capability Exchange using a presence server as defined by the
120      * framework. If set, the RcsFeature should support capability exchange using a presence
121      * server. If not set, this RcsFeature should not publish capabilities or service capability
122      * requests using presence.
123      */
124     public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1;
125 
126     /**
127      * This is used to check the upper range of RCS capability
128      * @hide
129      */
130     public static final int CAPABILITY_TYPE_MAX = CAPABILITY_TYPE_PRESENCE_UCE + 1;
131 
132     /**
133      * An application can use {@link #addOnAvailabilityChangedListener} to register a
134      * {@link OnAvailabilityChangedListener}, which will notify the user when the RCS feature
135      * availability status updates from the ImsService.
136      * @hide
137      */
138     @SystemApi
139     public interface OnAvailabilityChangedListener {
140         /**
141          * The availability of the feature's capabilities has changed to either available or
142          * unavailable.
143          * <p>
144          * If unavailable, the feature does not support the capability at the current time. This may
145          * be due to network or subscription provisioning changes, such as the IMS registration
146          * being lost, network type changing, or OMA-DM provisioning updates.
147          *
148          * @param capabilities The new availability of the capabilities.
149          */
onAvailabilityChanged(@csImsCapabilityFlag int capabilities)150         void onAvailabilityChanged(@RcsImsCapabilityFlag int capabilities);
151     }
152 
153     /**
154      * Receive the availability status changed from the ImsService and pass the status change to
155      * the associated {@link OnAvailabilityChangedListener}
156      */
157     private static class AvailabilityCallbackAdapter {
158 
159         private static class CapabilityBinder extends IImsCapabilityCallback.Stub {
160             private final OnAvailabilityChangedListener mOnAvailabilityChangedListener;
161             private final Executor mExecutor;
162 
CapabilityBinder(OnAvailabilityChangedListener listener, Executor executor)163             CapabilityBinder(OnAvailabilityChangedListener listener, Executor executor) {
164                 mExecutor = executor;
165                 mOnAvailabilityChangedListener = listener;
166             }
167 
168             @Override
onCapabilitiesStatusChanged(int config)169             public void onCapabilitiesStatusChanged(int config) {
170                 if (mOnAvailabilityChangedListener == null) return;
171 
172                 final long callingIdentity = Binder.clearCallingIdentity();
173                 try {
174                     mExecutor.execute(() ->
175                             mOnAvailabilityChangedListener.onAvailabilityChanged(config));
176                 } finally {
177                     restoreCallingIdentity(callingIdentity);
178                 }
179             }
180 
181             @Override
onQueryCapabilityConfiguration(int capability, int radioTech, boolean isEnabled)182             public void onQueryCapabilityConfiguration(int capability, int radioTech,
183                     boolean isEnabled) {
184                 // This is not used.
185             }
186 
187             @Override
onChangeCapabilityConfigurationError(int capability, int radioTech, @ImsFeature.ImsCapabilityError int reason)188             public void onChangeCapabilityConfigurationError(int capability, int radioTech,
189                     @ImsFeature.ImsCapabilityError int reason) {
190                 // This is not used.
191             }
192         }
193 
194         private final CapabilityBinder mBinder;
195 
AvailabilityCallbackAdapter(@onNull Executor executor, @NonNull OnAvailabilityChangedListener listener)196         AvailabilityCallbackAdapter(@NonNull Executor executor,
197                 @NonNull OnAvailabilityChangedListener listener) {
198             mBinder = new CapabilityBinder(listener, executor);
199         }
200 
201         /**@hide*/
getBinder()202         public final IImsCapabilityCallback getBinder() {
203             return mBinder;
204         }
205     }
206 
207     private final int mSubId;
208     private final Context mContext;
209     private final BinderCacheManager<IImsRcsController> mBinderCache;
210     private final BinderCacheManager<ITelephony> mTelephonyBinderCache;
211     private final Map<OnAvailabilityChangedListener, AvailabilityCallbackAdapter>
212             mAvailabilityChangedCallbacks;
213 
214     /**
215      * Use {@link ImsManager#getImsRcsManager(int)} to create an instance of this class.
216      * @hide
217      */
ImsRcsManager(Context context, int subId, BinderCacheManager<IImsRcsController> binderCache, BinderCacheManager<ITelephony> telephonyBinderCache)218     public ImsRcsManager(Context context, int subId,
219             BinderCacheManager<IImsRcsController> binderCache,
220             BinderCacheManager<ITelephony> telephonyBinderCache) {
221         mSubId = subId;
222         mContext = context;
223         mBinderCache = binderCache;
224         mAvailabilityChangedCallbacks = new HashMap<>();
225         mTelephonyBinderCache = telephonyBinderCache;
226     }
227 
228     /**
229      * @return A {@link RcsUceAdapter} used for User Capability Exchange (UCE) operations for
230      * this subscription.
231      */
232     @NonNull
getUceAdapter()233     public RcsUceAdapter getUceAdapter() {
234         return new RcsUceAdapter(mContext, mSubId);
235     }
236 
237     /**
238      * Registers a {@link RegistrationManager.RegistrationCallback} with the system. When the
239      * callback is registered, it will initiate the callback c to be called with the current
240      * registration state.
241      *
242      * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
243      * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
244      * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
245      *
246      * @param executor The executor the callback events should be run on.
247      * @param c The {@link RegistrationManager.RegistrationCallback} to be added.
248      * @see #unregisterImsRegistrationCallback(RegistrationManager.RegistrationCallback)
249      * @throws ImsException if the subscription associated with this callback is valid, but
250      * the {@code ImsService} associated with the subscription is not available. This can happen if
251      * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
252      * reason.
253      * @throws UnsupportedOperationException If the device does not have
254      *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
255      */
256     @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
registerImsRegistrationCallback( @onNull @allbackExecutor Executor executor, @NonNull RegistrationManager.RegistrationCallback c)257     public void registerImsRegistrationCallback(
258             @NonNull @CallbackExecutor Executor executor,
259             @NonNull RegistrationManager.RegistrationCallback c)
260             throws ImsException {
261         if (c == null) {
262             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
263         }
264         if (executor == null) {
265             throw new IllegalArgumentException("Must include a non-null Executor.");
266         }
267 
268         IImsRcsController imsRcsController = getIImsRcsController();
269         if (imsRcsController == null) {
270             Log.w(TAG, "Register registration callback: IImsRcsController is null");
271             throw new ImsException("Cannot find remote IMS service",
272                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
273         }
274 
275         c.setExecutor(executor);
276         try {
277             imsRcsController.registerImsRegistrationCallback(mSubId, c.getBinder());
278         } catch (ServiceSpecificException e) {
279             throw new ImsException(e.toString(), e.errorCode);
280         } catch (RemoteException | IllegalStateException e) {
281             throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
282         }
283     }
284 
285     /**
286      * Removes an existing {@link RegistrationManager.RegistrationCallback}.
287      *
288      * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
289      * etc...), this callback will automatically be removed. If this method is called for an
290      * inactive subscription, it will result in a no-op.
291      *
292      * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
293      * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
294      * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
295      *
296      * @param c The {@link RegistrationManager.RegistrationCallback} to be removed.
297      * @see android.telephony.SubscriptionManager.OnSubscriptionsChangedListener
298      * @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
299      * @throws UnsupportedOperationException If the device does not have
300      *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
301      */
302     @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
unregisterImsRegistrationCallback( @onNull RegistrationManager.RegistrationCallback c)303     public void unregisterImsRegistrationCallback(
304             @NonNull RegistrationManager.RegistrationCallback c) {
305         if (c == null) {
306             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
307         }
308 
309         IImsRcsController imsRcsController = getIImsRcsController();
310         if (imsRcsController == null) {
311             Log.w(TAG, "Unregister registration callback: IImsRcsController is null");
312             throw new IllegalStateException("Cannot find remote IMS service");
313         }
314 
315         try {
316             imsRcsController.unregisterImsRegistrationCallback(mSubId, c.getBinder());
317         } catch (RemoteException e) {
318             throw e.rethrowAsRuntimeException();
319         }
320     }
321 
322     /**
323      * Gets the registration state of the IMS service.
324      *
325      * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
326      * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
327      * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
328      *
329      * @param executor The {@link Executor} that will be used to call the IMS registration state
330      * callback.
331      * @param stateCallback A callback called on the supplied {@link Executor} that will contain the
332      * registration state of the IMS service, which will be one of the
333      * following: {@link RegistrationManager#REGISTRATION_STATE_NOT_REGISTERED},
334      * {@link RegistrationManager#REGISTRATION_STATE_REGISTERING}, or
335      * {@link RegistrationManager#REGISTRATION_STATE_REGISTERED}.
336      * @throws UnsupportedOperationException If the device does not have
337      *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
338      */
339     @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
getRegistrationState(@onNull @allbackExecutor Executor executor, @NonNull @RegistrationManager.ImsRegistrationState Consumer<Integer> stateCallback)340     public void getRegistrationState(@NonNull @CallbackExecutor Executor executor,
341             @NonNull @RegistrationManager.ImsRegistrationState Consumer<Integer> stateCallback) {
342         if (stateCallback == null) {
343             throw new IllegalArgumentException("Must include a non-null stateCallback.");
344         }
345         if (executor == null) {
346             throw new IllegalArgumentException("Must include a non-null Executor.");
347         }
348 
349         IImsRcsController imsRcsController = getIImsRcsController();
350         if (imsRcsController == null) {
351             Log.w(TAG, "Get registration state error: IImsRcsController is null");
352             throw new IllegalStateException("Cannot find remote IMS service");
353         }
354 
355         try {
356             imsRcsController.getImsRcsRegistrationState(mSubId, new IIntegerConsumer.Stub() {
357                 @Override
358                 public void accept(int result) {
359                     final long identity = Binder.clearCallingIdentity();
360                     try {
361                         executor.execute(() -> stateCallback.accept(result));
362                     } finally {
363                         Binder.restoreCallingIdentity(identity);
364                     }
365                 }
366             });
367         } catch (ServiceSpecificException | RemoteException e) {
368             Log.w(TAG, "Get registration state error: " + e);
369             executor.execute(() -> stateCallback.accept(
370                     RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED));
371         }
372     }
373 
374     /**
375      * Gets the Transport Type associated with the current IMS registration.
376      *
377      * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
378      * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
379      * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
380      *
381      * @param executor The {@link Executor} that will be used to call the transportTypeCallback.
382      * @param transportTypeCallback The transport type associated with the current IMS registration,
383      * which will be one of following:
384      * {@see AccessNetworkConstants#TRANSPORT_TYPE_WWAN},
385      * {@see AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, or
386      * {@see AccessNetworkConstants#TRANSPORT_TYPE_INVALID}.
387      * @throws UnsupportedOperationException If the device does not have
388      *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
389      */
390     @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
getRegistrationTransportType(@onNull @allbackExecutor Executor executor, @NonNull @AccessNetworkConstants.TransportType Consumer<Integer> transportTypeCallback)391     public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor,
392             @NonNull @AccessNetworkConstants.TransportType
393                     Consumer<Integer> transportTypeCallback) {
394         if (transportTypeCallback == null) {
395             throw new IllegalArgumentException("Must include a non-null transportTypeCallback.");
396         }
397         if (executor == null) {
398             throw new IllegalArgumentException("Must include a non-null Executor.");
399         }
400 
401         IImsRcsController imsRcsController = getIImsRcsController();
402         if (imsRcsController == null) {
403             Log.w(TAG, "Get registration transport type error: IImsRcsController is null");
404             throw new IllegalStateException("Cannot find remote IMS service");
405         }
406 
407         try {
408             imsRcsController.getImsRcsRegistrationTransportType(mSubId,
409                     new IIntegerConsumer.Stub() {
410                         @Override
411                         public void accept(int result) {
412                             final long identity = Binder.clearCallingIdentity();
413                             try {
414                                 executor.execute(() -> transportTypeCallback.accept(result));
415                             } finally {
416                                 Binder.restoreCallingIdentity(identity);
417                             }
418                         }
419                     });
420         } catch (ServiceSpecificException | RemoteException e) {
421             Log.w(TAG, "Get registration transport type error: " + e);
422             executor.execute(() -> transportTypeCallback.accept(
423                     AccessNetworkConstants.TRANSPORT_TYPE_INVALID));
424         }
425     }
426 
427     /**
428      * Add an {@link OnAvailabilityChangedListener} with the system, which will provide RCS
429      * availability updates for the subscription specified.
430      *
431      * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
432      * subscription changed events and call
433      * {@link #removeOnAvailabilityChangedListener(OnAvailabilityChangedListener)} to clean up
434      * after a subscription is removed.
435      * <p>
436      * When the listener is registered, it will initiate the callback listener to be called with
437      * the current capabilities.
438      *
439      * @param executor The executor the callback events should be run on.
440      * @param listener The RCS {@link OnAvailabilityChangedListener} to be registered.
441      * @see #removeOnAvailabilityChangedListener(OnAvailabilityChangedListener)
442      * @throws ImsException if the subscription associated with this instance of
443      * {@link ImsRcsManager} is valid, but the ImsService associated with the subscription is not
444      * available. This can happen if the ImsService has crashed, for example, or if the subscription
445      * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
446      * @throws UnsupportedOperationException If the device does not have
447      *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
448      * @hide
449      */
450     @SystemApi
451     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
addOnAvailabilityChangedListener(@onNull @allbackExecutor Executor executor, @NonNull OnAvailabilityChangedListener listener)452     public void addOnAvailabilityChangedListener(@NonNull @CallbackExecutor Executor executor,
453             @NonNull OnAvailabilityChangedListener listener) throws ImsException {
454         if (listener == null) {
455             throw new IllegalArgumentException("Must include a non-null"
456                     + "OnAvailabilityChangedListener.");
457         }
458         if (executor == null) {
459             throw new IllegalArgumentException("Must include a non-null Executor.");
460         }
461 
462         IImsRcsController imsRcsController = getIImsRcsController();
463         if (imsRcsController == null) {
464             Log.w(TAG, "Add availability changed listener: IImsRcsController is null");
465             throw new ImsException("Cannot find remote IMS service",
466                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
467         }
468 
469         AvailabilityCallbackAdapter adapter =
470                 addAvailabilityChangedListenerToCollection(executor, listener);
471         try {
472             imsRcsController.registerRcsAvailabilityCallback(mSubId, adapter.getBinder());
473         } catch (ServiceSpecificException e) {
474             throw new ImsException(e.toString(), e.errorCode);
475         } catch (RemoteException e) {
476             Log.w(TAG, "Error calling IImsRcsController#registerRcsAvailabilityCallback", e);
477             throw new ImsException("Remote IMS Service is not available",
478                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
479         }
480     }
481 
482      /**
483      * Removes an existing RCS {@link OnAvailabilityChangedListener}.
484      * <p>
485      * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
486      * etc...), this callback will automatically be unregistered. If this method is called for an
487      * inactive subscription, it will result in a no-op.
488      * @param listener The RCS {@link OnAvailabilityChangedListener} to be removed.
489      * @see #addOnAvailabilityChangedListener(Executor, OnAvailabilityChangedListener)
490      * @throws ImsException if the IMS service is not available when calling this method.
491      * See {@link ImsException#getCode()} for more information on the error codes.
492      * @throws UnsupportedOperationException If the device does not have
493      *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
494      * @hide
495      */
496     @SystemApi
497     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
removeOnAvailabilityChangedListener( @onNull OnAvailabilityChangedListener listener)498     public void removeOnAvailabilityChangedListener(
499             @NonNull OnAvailabilityChangedListener listener) {
500         if (listener == null) {
501             throw new IllegalArgumentException("Must include a non-null"
502                     + "OnAvailabilityChangedListener.");
503         }
504 
505         IImsRcsController imsRcsController = getIImsRcsController();
506         if (imsRcsController == null) {
507             Log.w(TAG, "Remove availability changed listener: IImsRcsController is null");
508             return;
509         }
510 
511         AvailabilityCallbackAdapter callback =
512                 removeAvailabilityChangedListenerFromCollection(listener);
513         if (callback == null) {
514             return;
515         }
516 
517         try {
518             imsRcsController.unregisterRcsAvailabilityCallback(mSubId, callback.getBinder());
519         } catch (RemoteException e) {
520             Log.w(TAG, "Error calling IImsRcsController#unregisterRcsAvailabilityCallback", e);
521         }
522     }
523 
524     /**
525      * Query for the capability of an IMS RCS service provided by the framework.
526      * <p>
527      * This only reports the status of RCS capabilities provided by the framework, not necessarily
528      * RCS capabilities provided over-the-top by applications.
529      *
530      * @param capability The RCS capability to query.
531      * @param radioTech The radio technology type that we are querying.
532      * @return true if the RCS capability is capable for this subscription, false otherwise. This
533      * does not necessarily mean that we are registered for IMS and the capability is available, but
534      * rather the subscription is capable of this service over IMS.
535      * @see #isAvailable(int, int)
536      * @see android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL
537      * @see android.telephony.CarrierConfigManager.Ims#KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL
538      * @throws ImsException if the IMS service is not available when calling this method.
539      * See {@link ImsException#getCode()} for more information on the error codes.
540      * @throws UnsupportedOperationException If the device does not have
541      *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
542      * @hide
543      */
544     @SystemApi
545     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
isCapable(@csImsCapabilityFlag int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)546     public boolean isCapable(@RcsImsCapabilityFlag int capability,
547             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException {
548         IImsRcsController imsRcsController = getIImsRcsController();
549         if (imsRcsController == null) {
550             Log.w(TAG, "isCapable: IImsRcsController is null");
551             throw new ImsException("Cannot find remote IMS service",
552                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
553         }
554 
555         try {
556             return imsRcsController.isCapable(mSubId, capability, radioTech);
557         } catch (ServiceSpecificException e) {
558             throw new ImsException(e.getMessage(), e.errorCode);
559         } catch (RemoteException e) {
560             Log.w(TAG, "Error calling IImsRcsController#isCapable", e);
561             throw new ImsException("Remote IMS Service is not available",
562                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
563         }
564     }
565 
566     /**
567      * Query the availability of an IMS RCS capability.
568      * <p>
569      * This only reports the status of RCS capabilities provided by the framework, not necessarily
570      * RCS capabilities provided by over-the-top by applications.
571      *
572      * @param capability the RCS capability to query.
573      * @param radioTech The radio technology type that we are querying.
574      * @return true if the RCS capability is currently available for the associated subscription,
575      * false otherwise. If the capability is available, IMS is registered and the service is
576      * currently available over IMS.
577      * @see #isCapable(int, int)
578      * @throws ImsException if the IMS service is not available when calling this method.
579      * See {@link ImsException#getCode()} for more information on the error codes.
580      * @throws UnsupportedOperationException If the device does not have
581      *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
582      * @hide
583      */
584     @SystemApi
585     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
isAvailable(@csImsCapabilityFlag int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)586     public boolean isAvailable(@RcsImsCapabilityFlag int capability,
587             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)
588             throws ImsException {
589         IImsRcsController imsRcsController = getIImsRcsController();
590         if (imsRcsController == null) {
591             Log.w(TAG, "isAvailable: IImsRcsController is null");
592             throw new ImsException("Cannot find remote IMS service",
593                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
594         }
595 
596         try {
597             return imsRcsController.isAvailable(mSubId, capability, radioTech);
598         } catch (ServiceSpecificException e) {
599             throw new ImsException(e.getMessage(), e.errorCode);
600         } catch (RemoteException e) {
601             Log.w(TAG, "Error calling IImsRcsController#isAvailable", e);
602             throw new ImsException("Remote IMS Service is not available",
603                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
604         }
605     }
606 
607     /**
608      * Register a new callback, which is used to notify the registrant of changes to
609      * the state of the underlying IMS service that is attached to telephony to
610      * implement IMS functionality. If the manager is created for
611      * the {@link android.telephony.SubscriptionManager#DEFAULT_SUBSCRIPTION_ID},
612      * this throws an {@link ImsException}.
613      *
614      * <p>Requires Permission:
615      * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE}
616      * or that the calling app has carrier privileges
617      * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
618      *
619      * @param executor the Executor that will be used to call the {@link ImsStateCallback}.
620      * @param callback The callback instance being registered.
621      * @throws ImsException in the case that the callback can not be registered.
622      * See {@link ImsException#getCode} for more information on when this is called.
623      */
624     @RequiresPermission(anyOf = {Manifest.permission.READ_PRECISE_PHONE_STATE,
625             Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
626             Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE})
registerImsStateCallback(@onNull Executor executor, @NonNull ImsStateCallback callback)627     public void registerImsStateCallback(@NonNull Executor executor,
628             @NonNull ImsStateCallback callback) throws ImsException {
629         Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
630         Objects.requireNonNull(executor, "Must include a non-null Executor.");
631 
632         callback.init(executor);
633         ITelephony telephony = mTelephonyBinderCache.listenOnBinder(callback, callback::binderDied);
634         if (telephony == null) {
635             throw new ImsException("Telephony server is down",
636                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
637         }
638 
639         try {
640             telephony.registerImsStateCallback(
641                     mSubId, ImsFeature.FEATURE_RCS,
642                     callback.getCallbackBinder(), mContext.getOpPackageName());
643         } catch (ServiceSpecificException e) {
644             throw new ImsException(e.getMessage(), e.errorCode);
645         } catch (RemoteException | IllegalStateException e) {
646             throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
647         }
648     }
649 
650     /**
651      * Unregisters a previously registered callback.
652      *
653      * @param callback The callback instance to be unregistered.
654      */
unregisterImsStateCallback(@onNull ImsStateCallback callback)655     public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) {
656         Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
657 
658         ITelephony telephony = mTelephonyBinderCache.removeRunnable(callback);
659         try {
660             if (telephony != null) {
661                 telephony.unregisterImsStateCallback(callback.getCallbackBinder());
662             }
663         } catch (RemoteException ignore) {
664             // ignore it
665         }
666     }
667 
668     /**
669      * Add the {@link OnAvailabilityChangedListener} to collection for tracking.
670      * @param executor The executor that will be used when the publish state is changed and the
671      * {@link OnAvailabilityChangedListener} is called.
672      * @param listener The {@link OnAvailabilityChangedListener} to call the publish state changed.
673      * @return The {@link AvailabilityCallbackAdapter} to wrapper the
674      * {@link OnAvailabilityChangedListener}
675      */
addAvailabilityChangedListenerToCollection( @onNull Executor executor, @NonNull OnAvailabilityChangedListener listener)676     private AvailabilityCallbackAdapter addAvailabilityChangedListenerToCollection(
677             @NonNull Executor executor, @NonNull OnAvailabilityChangedListener listener) {
678         AvailabilityCallbackAdapter adapter = new AvailabilityCallbackAdapter(executor, listener);
679         synchronized (mAvailabilityChangedCallbacks) {
680             mAvailabilityChangedCallbacks.put(listener, adapter);
681         }
682         return adapter;
683     }
684 
685     /**
686      * Remove the existing {@link OnAvailabilityChangedListener} from the collection.
687      * @param listener The {@link OnAvailabilityChangedListener} to remove from the collection.
688      * @return The wrapper class {@link AvailabilityCallbackAdapter} associated with the
689      * {@link OnAvailabilityChangedListener}.
690      */
removeAvailabilityChangedListenerFromCollection( @onNull OnAvailabilityChangedListener listener)691     private AvailabilityCallbackAdapter removeAvailabilityChangedListenerFromCollection(
692             @NonNull OnAvailabilityChangedListener listener) {
693         synchronized (mAvailabilityChangedCallbacks) {
694             return mAvailabilityChangedCallbacks.remove(listener);
695         }
696     }
697 
getIImsRcsController()698     private IImsRcsController getIImsRcsController() {
699         IBinder binder = TelephonyFrameworkInitializer
700                 .getTelephonyServiceManager()
701                 .getTelephonyImsServiceRegisterer()
702                 .get();
703         return IImsRcsController.Stub.asInterface(binder);
704     }
705 }
706