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