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 {@link 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 */ 254 @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) registerImsRegistrationCallback( @onNull @allbackExecutor Executor executor, @NonNull RegistrationManager.RegistrationCallback c)255 public void registerImsRegistrationCallback( 256 @NonNull @CallbackExecutor Executor executor, 257 @NonNull RegistrationManager.RegistrationCallback c) 258 throws ImsException { 259 if (c == null) { 260 throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); 261 } 262 if (executor == null) { 263 throw new IllegalArgumentException("Must include a non-null Executor."); 264 } 265 266 IImsRcsController imsRcsController = getIImsRcsController(); 267 if (imsRcsController == null) { 268 Log.w(TAG, "Register registration callback: IImsRcsController is null"); 269 throw new ImsException("Cannot find remote IMS service", 270 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 271 } 272 273 c.setExecutor(executor); 274 try { 275 imsRcsController.registerImsRegistrationCallback(mSubId, c.getBinder()); 276 } catch (ServiceSpecificException e) { 277 throw new ImsException(e.toString(), e.errorCode); 278 } catch (RemoteException | IllegalStateException e) { 279 throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 280 } 281 } 282 283 /** 284 * Removes an existing {@link RegistrationManager.RegistrationCallback}. 285 * 286 * When the subscription associated with this callback is removed (SIM removed, ESIM swap, 287 * etc...), this callback will automatically be removed. If this method is called for an 288 * inactive subscription, it will result in a no-op. 289 * 290 * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE 291 * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges 292 * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). 293 * 294 * @param c The {@link RegistrationManager.RegistrationCallback} to be removed. 295 * @see android.telephony.SubscriptionManager.OnSubscriptionsChangedListener 296 * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) 297 */ 298 @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) unregisterImsRegistrationCallback( @onNull RegistrationManager.RegistrationCallback c)299 public void unregisterImsRegistrationCallback( 300 @NonNull RegistrationManager.RegistrationCallback c) { 301 if (c == null) { 302 throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); 303 } 304 305 IImsRcsController imsRcsController = getIImsRcsController(); 306 if (imsRcsController == null) { 307 Log.w(TAG, "Unregister registration callback: IImsRcsController is null"); 308 throw new IllegalStateException("Cannot find remote IMS service"); 309 } 310 311 try { 312 imsRcsController.unregisterImsRegistrationCallback(mSubId, c.getBinder()); 313 } catch (RemoteException e) { 314 throw e.rethrowAsRuntimeException(); 315 } 316 } 317 318 /** 319 * Gets the registration state of the IMS service. 320 * 321 * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE 322 * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges 323 * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). 324 * 325 * @param executor The {@link Executor} that will be used to call the IMS registration state 326 * callback. 327 * @param stateCallback A callback called on the supplied {@link Executor} that will contain the 328 * registration state of the IMS service, which will be one of the 329 * following: {@link RegistrationManager#REGISTRATION_STATE_NOT_REGISTERED}, 330 * {@link RegistrationManager#REGISTRATION_STATE_REGISTERING}, or 331 * {@link RegistrationManager#REGISTRATION_STATE_REGISTERED}. 332 */ 333 @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) getRegistrationState(@onNull @allbackExecutor Executor executor, @NonNull @RegistrationManager.ImsRegistrationState Consumer<Integer> stateCallback)334 public void getRegistrationState(@NonNull @CallbackExecutor Executor executor, 335 @NonNull @RegistrationManager.ImsRegistrationState Consumer<Integer> stateCallback) { 336 if (stateCallback == null) { 337 throw new IllegalArgumentException("Must include a non-null stateCallback."); 338 } 339 if (executor == null) { 340 throw new IllegalArgumentException("Must include a non-null Executor."); 341 } 342 343 IImsRcsController imsRcsController = getIImsRcsController(); 344 if (imsRcsController == null) { 345 Log.w(TAG, "Get registration state error: IImsRcsController is null"); 346 throw new IllegalStateException("Cannot find remote IMS service"); 347 } 348 349 try { 350 imsRcsController.getImsRcsRegistrationState(mSubId, new IIntegerConsumer.Stub() { 351 @Override 352 public void accept(int result) { 353 final long identity = Binder.clearCallingIdentity(); 354 try { 355 executor.execute(() -> stateCallback.accept(result)); 356 } finally { 357 Binder.restoreCallingIdentity(identity); 358 } 359 } 360 }); 361 } catch (ServiceSpecificException | RemoteException e) { 362 Log.w(TAG, "Get registration state error: " + e); 363 executor.execute(() -> stateCallback.accept( 364 RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED)); 365 } 366 } 367 368 /** 369 * Gets the Transport Type associated with the current IMS registration. 370 * 371 * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE 372 * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges 373 * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). 374 * 375 * @param executor The {@link Executor} that will be used to call the transportTypeCallback. 376 * @param transportTypeCallback The transport type associated with the current IMS registration, 377 * which will be one of following: 378 * {@see AccessNetworkConstants#TRANSPORT_TYPE_WWAN}, 379 * {@see AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, or 380 * {@see AccessNetworkConstants#TRANSPORT_TYPE_INVALID}. 381 */ 382 @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) getRegistrationTransportType(@onNull @allbackExecutor Executor executor, @NonNull @AccessNetworkConstants.TransportType Consumer<Integer> transportTypeCallback)383 public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor, 384 @NonNull @AccessNetworkConstants.TransportType 385 Consumer<Integer> transportTypeCallback) { 386 if (transportTypeCallback == null) { 387 throw new IllegalArgumentException("Must include a non-null transportTypeCallback."); 388 } 389 if (executor == null) { 390 throw new IllegalArgumentException("Must include a non-null Executor."); 391 } 392 393 IImsRcsController imsRcsController = getIImsRcsController(); 394 if (imsRcsController == null) { 395 Log.w(TAG, "Get registration transport type error: IImsRcsController is null"); 396 throw new IllegalStateException("Cannot find remote IMS service"); 397 } 398 399 try { 400 imsRcsController.getImsRcsRegistrationTransportType(mSubId, 401 new IIntegerConsumer.Stub() { 402 @Override 403 public void accept(int result) { 404 final long identity = Binder.clearCallingIdentity(); 405 try { 406 executor.execute(() -> transportTypeCallback.accept(result)); 407 } finally { 408 Binder.restoreCallingIdentity(identity); 409 } 410 } 411 }); 412 } catch (ServiceSpecificException | RemoteException e) { 413 Log.w(TAG, "Get registration transport type error: " + e); 414 executor.execute(() -> transportTypeCallback.accept( 415 AccessNetworkConstants.TRANSPORT_TYPE_INVALID)); 416 } 417 } 418 419 /** 420 * Add an {@link OnAvailabilityChangedListener} with the system, which will provide RCS 421 * availability updates for the subscription specified. 422 * 423 * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to 424 * subscription changed events and call 425 * {@link #removeOnAvailabilityChangedListener(OnAvailabilityChangedListener)} to clean up 426 * after a subscription is removed. 427 * <p> 428 * When the listener is registered, it will initiate the callback listener to be called with 429 * the current capabilities. 430 * 431 * @param executor The executor the callback events should be run on. 432 * @param listener The RCS {@link OnAvailabilityChangedListener} to be registered. 433 * @see #removeOnAvailabilityChangedListener(OnAvailabilityChangedListener) 434 * @throws ImsException if the subscription associated with this instance of 435 * {@link ImsRcsManager} is valid, but the ImsService associated with the subscription is not 436 * available. This can happen if the ImsService has crashed, for example, or if the subscription 437 * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. 438 * @hide 439 */ 440 @SystemApi 441 @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) addOnAvailabilityChangedListener(@onNull @allbackExecutor Executor executor, @NonNull OnAvailabilityChangedListener listener)442 public void addOnAvailabilityChangedListener(@NonNull @CallbackExecutor Executor executor, 443 @NonNull OnAvailabilityChangedListener listener) throws ImsException { 444 if (listener == null) { 445 throw new IllegalArgumentException("Must include a non-null" 446 + "OnAvailabilityChangedListener."); 447 } 448 if (executor == null) { 449 throw new IllegalArgumentException("Must include a non-null Executor."); 450 } 451 452 IImsRcsController imsRcsController = getIImsRcsController(); 453 if (imsRcsController == null) { 454 Log.w(TAG, "Add availability changed listener: IImsRcsController is null"); 455 throw new ImsException("Cannot find remote IMS service", 456 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 457 } 458 459 AvailabilityCallbackAdapter adapter = 460 addAvailabilityChangedListenerToCollection(executor, listener); 461 try { 462 imsRcsController.registerRcsAvailabilityCallback(mSubId, adapter.getBinder()); 463 } catch (ServiceSpecificException e) { 464 throw new ImsException(e.toString(), e.errorCode); 465 } catch (RemoteException e) { 466 Log.w(TAG, "Error calling IImsRcsController#registerRcsAvailabilityCallback", e); 467 throw new ImsException("Remote IMS Service is not available", 468 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 469 } 470 } 471 472 /** 473 * Removes an existing RCS {@link OnAvailabilityChangedListener}. 474 * <p> 475 * When the subscription associated with this callback is removed (SIM removed, ESIM swap, 476 * etc...), this callback will automatically be unregistered. If this method is called for an 477 * inactive subscription, it will result in a no-op. 478 * @param listener The RCS {@link OnAvailabilityChangedListener} to be removed. 479 * @see #addOnAvailabilityChangedListener(Executor, OnAvailabilityChangedListener) 480 * @throws ImsException if the IMS service is not available when calling this method. 481 * See {@link ImsException#getCode()} for more information on the error codes. 482 * @hide 483 */ 484 @SystemApi 485 @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) removeOnAvailabilityChangedListener( @onNull OnAvailabilityChangedListener listener)486 public void removeOnAvailabilityChangedListener( 487 @NonNull OnAvailabilityChangedListener listener) { 488 if (listener == null) { 489 throw new IllegalArgumentException("Must include a non-null" 490 + "OnAvailabilityChangedListener."); 491 } 492 493 IImsRcsController imsRcsController = getIImsRcsController(); 494 if (imsRcsController == null) { 495 Log.w(TAG, "Remove availability changed listener: IImsRcsController is null"); 496 return; 497 } 498 499 AvailabilityCallbackAdapter callback = 500 removeAvailabilityChangedListenerFromCollection(listener); 501 if (callback == null) { 502 return; 503 } 504 505 try { 506 imsRcsController.unregisterRcsAvailabilityCallback(mSubId, callback.getBinder()); 507 } catch (RemoteException e) { 508 Log.w(TAG, "Error calling IImsRcsController#unregisterRcsAvailabilityCallback", e); 509 } 510 } 511 512 /** 513 * Query for the capability of an IMS RCS service provided by the framework. 514 * <p> 515 * This only reports the status of RCS capabilities provided by the framework, not necessarily 516 * RCS capabilities provided over-the-top by applications. 517 * 518 * @param capability The RCS capability to query. 519 * @param radioTech The radio technology type that we are querying. 520 * @return true if the RCS capability is capable for this subscription, false otherwise. This 521 * does not necessarily mean that we are registered for IMS and the capability is available, but 522 * rather the subscription is capable of this service over IMS. 523 * @see #isAvailable(int, int) 524 * @see android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL 525 * @see android.telephony.CarrierConfigManager.Ims#KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL 526 * @throws ImsException if the IMS service is not available when calling this method. 527 * See {@link ImsException#getCode()} for more information on the error codes. 528 * @hide 529 */ 530 @SystemApi 531 @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) isCapable(@csImsCapabilityFlag int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)532 public boolean isCapable(@RcsImsCapabilityFlag int capability, 533 @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException { 534 IImsRcsController imsRcsController = getIImsRcsController(); 535 if (imsRcsController == null) { 536 Log.w(TAG, "isCapable: IImsRcsController is null"); 537 throw new ImsException("Cannot find remote IMS service", 538 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 539 } 540 541 try { 542 return imsRcsController.isCapable(mSubId, capability, radioTech); 543 } catch (ServiceSpecificException e) { 544 throw new ImsException(e.getMessage(), e.errorCode); 545 } catch (RemoteException e) { 546 Log.w(TAG, "Error calling IImsRcsController#isCapable", e); 547 throw new ImsException("Remote IMS Service is not available", 548 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 549 } 550 } 551 552 /** 553 * Query the availability of an IMS RCS capability. 554 * <p> 555 * This only reports the status of RCS capabilities provided by the framework, not necessarily 556 * RCS capabilities provided by over-the-top by applications. 557 * 558 * @param capability the RCS capability to query. 559 * @param radioTech The radio technology type that we are querying. 560 * @return true if the RCS capability is currently available for the associated subscription, 561 * false otherwise. If the capability is available, IMS is registered and the service is 562 * currently available over IMS. 563 * @see #isCapable(int, int) 564 * @throws ImsException if the IMS service is not available when calling this method. 565 * See {@link ImsException#getCode()} for more information on the error codes. 566 * @hide 567 */ 568 @SystemApi 569 @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) isAvailable(@csImsCapabilityFlag int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)570 public boolean isAvailable(@RcsImsCapabilityFlag int capability, 571 @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) 572 throws ImsException { 573 IImsRcsController imsRcsController = getIImsRcsController(); 574 if (imsRcsController == null) { 575 Log.w(TAG, "isAvailable: IImsRcsController is null"); 576 throw new ImsException("Cannot find remote IMS service", 577 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 578 } 579 580 try { 581 return imsRcsController.isAvailable(mSubId, capability, radioTech); 582 } catch (ServiceSpecificException e) { 583 throw new ImsException(e.getMessage(), e.errorCode); 584 } catch (RemoteException e) { 585 Log.w(TAG, "Error calling IImsRcsController#isAvailable", e); 586 throw new ImsException("Remote IMS Service is not available", 587 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 588 } 589 } 590 591 /** 592 * Register a new callback, which is used to notify the registrant of changes to 593 * the state of the underlying IMS service that is attached to telephony to 594 * implement IMS functionality. If the manager is created for 595 * the {@link android.telephony.SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}, 596 * this throws an {@link ImsException}. 597 * 598 * <p>Requires Permission: 599 * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE} 600 * or that the calling app has carrier privileges 601 * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). 602 * 603 * @param executor the Executor that will be used to call the {@link ImsStateCallback}. 604 * @param callback The callback instance being registered. 605 * @throws ImsException in the case that the callback can not be registered. 606 * See {@link ImsException#getCode} for more information on when this is called. 607 */ 608 @RequiresPermission(anyOf = {Manifest.permission.READ_PRECISE_PHONE_STATE, 609 Manifest.permission.READ_PRIVILEGED_PHONE_STATE, 610 Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE}) registerImsStateCallback(@onNull Executor executor, @NonNull ImsStateCallback callback)611 public void registerImsStateCallback(@NonNull Executor executor, 612 @NonNull ImsStateCallback callback) throws ImsException { 613 Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback."); 614 Objects.requireNonNull(executor, "Must include a non-null Executor."); 615 616 callback.init(executor); 617 ITelephony telephony = mTelephonyBinderCache.listenOnBinder(callback, callback::binderDied); 618 if (telephony == null) { 619 throw new ImsException("Telephony server is down", 620 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 621 } 622 623 try { 624 telephony.registerImsStateCallback( 625 mSubId, ImsFeature.FEATURE_RCS, 626 callback.getCallbackBinder(), mContext.getOpPackageName()); 627 } catch (ServiceSpecificException e) { 628 throw new ImsException(e.getMessage(), e.errorCode); 629 } catch (RemoteException | IllegalStateException e) { 630 throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 631 } 632 } 633 634 /** 635 * Unregisters a previously registered callback. 636 * 637 * @param callback The callback instance to be unregistered. 638 */ unregisterImsStateCallback(@onNull ImsStateCallback callback)639 public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) { 640 Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback."); 641 642 ITelephony telephony = mTelephonyBinderCache.removeRunnable(callback); 643 try { 644 if (telephony != null) { 645 telephony.unregisterImsStateCallback(callback.getCallbackBinder()); 646 } 647 } catch (RemoteException ignore) { 648 // ignore it 649 } 650 } 651 652 /** 653 * Add the {@link OnAvailabilityChangedListener} to collection for tracking. 654 * @param executor The executor that will be used when the publish state is changed and the 655 * {@link OnAvailabilityChangedListener} is called. 656 * @param listener The {@link OnAvailabilityChangedListener} to call the publish state changed. 657 * @return The {@link AvailabilityCallbackAdapter} to wrapper the 658 * {@link OnAvailabilityChangedListener} 659 */ addAvailabilityChangedListenerToCollection( @onNull Executor executor, @NonNull OnAvailabilityChangedListener listener)660 private AvailabilityCallbackAdapter addAvailabilityChangedListenerToCollection( 661 @NonNull Executor executor, @NonNull OnAvailabilityChangedListener listener) { 662 AvailabilityCallbackAdapter adapter = new AvailabilityCallbackAdapter(executor, listener); 663 synchronized (mAvailabilityChangedCallbacks) { 664 mAvailabilityChangedCallbacks.put(listener, adapter); 665 } 666 return adapter; 667 } 668 669 /** 670 * Remove the existing {@link OnAvailabilityChangedListener} from the collection. 671 * @param listener The {@link OnAvailabilityChangedListener} to remove from the collection. 672 * @return The wrapper class {@link AvailabilityCallbackAdapter} associated with the 673 * {@link OnAvailabilityChangedListener}. 674 */ removeAvailabilityChangedListenerFromCollection( @onNull OnAvailabilityChangedListener listener)675 private AvailabilityCallbackAdapter removeAvailabilityChangedListenerFromCollection( 676 @NonNull OnAvailabilityChangedListener listener) { 677 synchronized (mAvailabilityChangedCallbacks) { 678 return mAvailabilityChangedCallbacks.remove(listener); 679 } 680 } 681 getIImsRcsController()682 private IImsRcsController getIImsRcsController() { 683 IBinder binder = TelephonyFrameworkInitializer 684 .getTelephonyServiceManager() 685 .getTelephonyImsServiceRegisterer() 686 .get(); 687 return IImsRcsController.Stub.asInterface(binder); 688 } 689 } 690