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