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