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.RequiresPermission; 24 import android.annotation.SystemApi; 25 import android.annotation.TestApi; 26 import android.content.Context; 27 import android.net.Uri; 28 import android.os.Binder; 29 import android.os.IBinder; 30 import android.os.RemoteException; 31 import android.os.ServiceManager; 32 import android.telephony.ims.aidl.IImsRcsController; 33 import android.telephony.ims.aidl.IRcsUceControllerCallback; 34 import android.telephony.ims.aidl.IRcsUcePublishStateCallback; 35 import android.util.Log; 36 37 import java.lang.annotation.Retention; 38 import java.lang.annotation.RetentionPolicy; 39 import java.util.List; 40 import java.util.concurrent.Executor; 41 42 /** 43 * Manages RCS User Capability Exchange for the subscription specified. 44 * 45 * @see ImsRcsManager#getUceAdapter() for information on creating an instance of this class. 46 */ 47 public class RcsUceAdapter { 48 private static final String TAG = "RcsUceAdapter"; 49 50 /** 51 * An unknown error has caused the request to fail. 52 * @hide 53 */ 54 public static final int ERROR_GENERIC_FAILURE = 1; 55 /** 56 * The carrier network does not have UCE support enabled for this subscriber. 57 * @hide 58 */ 59 public static final int ERROR_NOT_ENABLED = 2; 60 /** 61 * The data network that the device is connected to does not support UCE currently (e.g. it is 62 * 1x only currently). 63 * @hide 64 */ 65 public static final int ERROR_NOT_AVAILABLE = 3; 66 /** 67 * The network has responded with SIP 403 error and a reason "User not registered." 68 * @hide 69 */ 70 public static final int ERROR_NOT_REGISTERED = 4; 71 /** 72 * The network has responded to this request with a SIP 403 error and reason "not authorized for 73 * presence" for this subscriber. 74 * @hide 75 */ 76 public static final int ERROR_NOT_AUTHORIZED = 5; 77 /** 78 * The network has responded to this request with a SIP 403 error and no reason. 79 * @hide 80 */ 81 public static final int ERROR_FORBIDDEN = 6; 82 /** 83 * The contact URI requested is not provisioned for VoLTE or it is not known as an IMS 84 * subscriber to the carrier network. 85 * @hide 86 */ 87 public static final int ERROR_NOT_FOUND = 7; 88 /** 89 * The capabilities request contained too many URIs for the carrier network to handle. Retry 90 * with a lower number of contact numbers. The number varies per carrier. 91 * @hide 92 */ 93 // TODO: Try to integrate this into the API so that the service will split based on carrier. 94 public static final int ERROR_REQUEST_TOO_LARGE = 8; 95 /** 96 * The network did not respond to the capabilities request before the request timed out. 97 * @hide 98 */ 99 public static final int ERROR_REQUEST_TIMEOUT = 10; 100 /** 101 * The request failed due to the service having insufficient memory. 102 * @hide 103 */ 104 public static final int ERROR_INSUFFICIENT_MEMORY = 11; 105 /** 106 * The network was lost while trying to complete the request. 107 * @hide 108 */ 109 public static final int ERROR_LOST_NETWORK = 12; 110 /** 111 * The request has failed because the same request has already been added to the queue. 112 * @hide 113 */ 114 public static final int ERROR_ALREADY_IN_QUEUE = 13; 115 116 /**@hide*/ 117 @Retention(RetentionPolicy.SOURCE) 118 @IntDef(prefix = "ERROR_", value = { 119 ERROR_GENERIC_FAILURE, 120 ERROR_NOT_ENABLED, 121 ERROR_NOT_AVAILABLE, 122 ERROR_NOT_REGISTERED, 123 ERROR_NOT_AUTHORIZED, 124 ERROR_FORBIDDEN, 125 ERROR_NOT_FOUND, 126 ERROR_REQUEST_TOO_LARGE, 127 ERROR_REQUEST_TIMEOUT, 128 ERROR_INSUFFICIENT_MEMORY, 129 ERROR_LOST_NETWORK, 130 ERROR_ALREADY_IN_QUEUE 131 }) 132 public @interface ErrorCode {} 133 134 /** 135 * The last publish has resulted in a "200 OK" response or the device is using SIP OPTIONS for 136 * UCE. 137 * @hide 138 */ 139 public static final int PUBLISH_STATE_OK = 1; 140 141 /** 142 * The hasn't published its capabilities since boot or hasn't gotten any publish response yet. 143 * @hide 144 */ 145 public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; 146 147 /** 148 * The device has tried to publish its capabilities, which has resulted in an error. This error 149 * is related to the fact that the device is not VoLTE provisioned. 150 * @hide 151 */ 152 public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3; 153 154 /** 155 * The device has tried to publish its capabilities, which has resulted in an error. This error 156 * is related to the fact that the device is not RCS or UCE provisioned. 157 * @hide 158 */ 159 public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; 160 161 /** 162 * The last publish resulted in a "408 Request Timeout" response. 163 * @hide 164 */ 165 public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; 166 167 /** 168 * The last publish resulted in another unknown error, such as SIP 503 - "Service Unavailable" 169 * or SIP 423 - "Interval too short". 170 * <p> 171 * Device shall retry with exponential back-off. 172 * @hide 173 */ 174 public static final int PUBLISH_STATE_OTHER_ERROR = 6; 175 176 /**@hide*/ 177 @Retention(RetentionPolicy.SOURCE) 178 @IntDef(prefix = "PUBLISH_STATE_", value = { 179 PUBLISH_STATE_OK, 180 PUBLISH_STATE_NOT_PUBLISHED, 181 PUBLISH_STATE_VOLTE_PROVISION_ERROR, 182 PUBLISH_STATE_RCS_PROVISION_ERROR, 183 PUBLISH_STATE_REQUEST_TIMEOUT, 184 PUBLISH_STATE_OTHER_ERROR 185 }) 186 public @interface PublishState {} 187 188 /** 189 * An application can use {@link #registerPublishStateCallback} to register a 190 * {@link PublishStateCallback), which will notify the user when the publish state to the 191 * network changes. 192 * @hide 193 */ 194 public static class PublishStateCallback { 195 196 private static class PublishStateBinder extends IRcsUcePublishStateCallback.Stub { 197 198 private final PublishStateCallback mLocalCallback; 199 private Executor mExecutor; 200 PublishStateBinder(PublishStateCallback c)201 PublishStateBinder(PublishStateCallback c) { 202 mLocalCallback = c; 203 } 204 205 @Override onPublishStateChanged(int publishState)206 public void onPublishStateChanged(int publishState) { 207 if (mLocalCallback == null) return; 208 209 long callingIdentity = Binder.clearCallingIdentity(); 210 try { 211 mExecutor.execute(() -> mLocalCallback.onChanged(publishState)); 212 } finally { 213 restoreCallingIdentity(callingIdentity); 214 } 215 } 216 setExecutor(Executor executor)217 private void setExecutor(Executor executor) { 218 mExecutor = executor; 219 } 220 } 221 222 private final PublishStateBinder mBinder = new PublishStateBinder(this); 223 224 /**@hide*/ getBinder()225 public final IRcsUcePublishStateCallback getBinder() { 226 return mBinder; 227 } 228 setExecutor(Executor executor)229 private void setExecutor(Executor executor) { 230 mBinder.setExecutor(executor); 231 } 232 233 /** 234 * Notifies the callback when the publish state has changed. 235 * @param publishState The latest update to the publish state. 236 */ onChanged(@ublishState int publishState)237 public void onChanged(@PublishState int publishState) { 238 } 239 } 240 241 /** 242 * Provides a one-time callback for the response to a UCE request. After this callback is called 243 * by the framework, the reference to this callback will be discarded on the service side. 244 * @see #requestCapabilities(Executor, List, CapabilitiesCallback) 245 * @hide 246 */ 247 public static class CapabilitiesCallback { 248 249 /** 250 * Notify this application that the pending capability request has returned successfully. 251 * @param contactCapabilities List of capabilities associated with each contact requested. 252 */ onCapabilitiesReceived( @onNull List<RcsContactUceCapability> contactCapabilities)253 public void onCapabilitiesReceived( 254 @NonNull List<RcsContactUceCapability> contactCapabilities) { 255 256 } 257 258 /** 259 * The pending request has resulted in an error and may need to be retried, depending on the 260 * error code. 261 * @param errorCode The reason for the framework being unable to process the request. 262 */ onError(@rrorCode int errorCode)263 public void onError(@ErrorCode int errorCode) { 264 265 } 266 } 267 268 private final Context mContext; 269 private final int mSubId; 270 271 /** 272 * Not to be instantiated directly, use 273 * {@link ImsRcsManager#getUceAdapter()} to instantiate this manager class. 274 * @hide 275 */ RcsUceAdapter(Context context, int subId)276 RcsUceAdapter(Context context, int subId) { 277 mContext = context; 278 mSubId = subId; 279 } 280 281 /** 282 * Request the User Capability Exchange capabilities for one or more contacts. 283 * <p> 284 * Be sure to check the availability of this feature using 285 * {@link ImsRcsManager#isAvailable(int)} and ensuring 286 * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or 287 * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is enabled or else 288 * this operation will fail with {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}. 289 * 290 * @param executor The executor that will be used when the request is completed and the 291 * {@link CapabilitiesCallback} is called. 292 * @param contactNumbers A list of numbers that the capabilities are being requested for. 293 * @param c A one-time callback for when the request for capabilities completes or there is an 294 * error processing the request. 295 * @throws ImsException if the subscription associated with this instance of 296 * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not 297 * available. This can happen if the ImsService has crashed, for example, or if the subscription 298 * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. 299 * @hide 300 */ 301 @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) requestCapabilities(@onNull @allbackExecutor Executor executor, @NonNull List<Uri> contactNumbers, @NonNull CapabilitiesCallback c)302 public void requestCapabilities(@NonNull @CallbackExecutor Executor executor, 303 @NonNull List<Uri> contactNumbers, 304 @NonNull CapabilitiesCallback c) throws ImsException { 305 if (c == null) { 306 throw new IllegalArgumentException("Must include a non-null AvailabilityCallback."); 307 } 308 if (executor == null) { 309 throw new IllegalArgumentException("Must include a non-null Executor."); 310 } 311 if (contactNumbers == null) { 312 throw new IllegalArgumentException("Must include non-null contact number list."); 313 } 314 315 IImsRcsController imsRcsController = getIImsRcsController(); 316 if (imsRcsController == null) { 317 Log.e(TAG, "requestCapabilities: IImsRcsController is null"); 318 throw new ImsException("Can not find remote IMS service", 319 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 320 } 321 322 IRcsUceControllerCallback internalCallback = new IRcsUceControllerCallback.Stub() { 323 @Override 324 public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) { 325 long callingIdentity = Binder.clearCallingIdentity(); 326 try { 327 executor.execute(() -> 328 c.onCapabilitiesReceived(contactCapabilities)); 329 } finally { 330 restoreCallingIdentity(callingIdentity); 331 } 332 } 333 @Override 334 public void onError(int errorCode) { 335 long callingIdentity = Binder.clearCallingIdentity(); 336 try { 337 executor.execute(() -> c.onError(errorCode)); 338 } finally { 339 restoreCallingIdentity(callingIdentity); 340 } 341 } 342 }; 343 344 try { 345 imsRcsController.requestCapabilities(mSubId, mContext.getOpPackageName(), 346 null /*featureId*/, contactNumbers, internalCallback); 347 } catch (RemoteException e) { 348 Log.e(TAG, "Error calling IImsRcsController#requestCapabilities", e); 349 throw new ImsException("Remote IMS Service is not available", 350 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 351 } 352 } 353 354 /** 355 * Gets the last publish result from the UCE service if the device is using an RCS presence 356 * server. 357 * @return The last publish result from the UCE service. If the device is using SIP OPTIONS, 358 * this method will return {@link #PUBLISH_STATE_OK} as well. 359 * @throws ImsException if the subscription associated with this instance of 360 * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not 361 * available. This can happen if the ImsService has crashed, for example, or if the subscription 362 * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. 363 * @hide 364 */ 365 @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) getUcePublishState()366 public @PublishState int getUcePublishState() throws ImsException { 367 IImsRcsController imsRcsController = getIImsRcsController(); 368 if (imsRcsController == null) { 369 Log.e(TAG, "getUcePublishState: IImsRcsController is null"); 370 throw new ImsException("Can not find remote IMS service", 371 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 372 } 373 374 try { 375 return imsRcsController.getUcePublishState(mSubId); 376 } catch (android.os.ServiceSpecificException e) { 377 throw new ImsException(e.getMessage(), e.errorCode); 378 } catch (RemoteException e) { 379 Log.e(TAG, "Error calling IImsRcsController#getUcePublishState", e); 380 throw new ImsException("Remote IMS Service is not available", 381 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 382 } 383 } 384 385 /** 386 * Registers a {@link PublishStateCallback} with the system, which will provide publish state 387 * updates for the subscription specified in {@link ImsManager@getRcsManager(subid)}. 388 * <p> 389 * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to subscription 390 * changed events and call {@link #unregisterPublishStateCallback} to clean up. 391 * <p> 392 * The registered {@link PublishStateCallback} will also receive a callback when it is 393 * registered with the current publish state. 394 * 395 * @param executor The executor the listener callback events should be run on. 396 * @param c The {@link PublishStateCallback} to be added. 397 * @throws ImsException if the subscription associated with this callback is valid, but 398 * the {@link ImsService} associated with the subscription is not available. This can happen if 399 * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed 400 * reason. 401 * @hide 402 */ 403 @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) registerPublishStateCallback(@onNull @allbackExecutor Executor executor, @NonNull PublishStateCallback c)404 public void registerPublishStateCallback(@NonNull @CallbackExecutor Executor executor, 405 @NonNull PublishStateCallback c) throws ImsException { 406 if (c == null) { 407 throw new IllegalArgumentException("Must include a non-null PublishStateCallback."); 408 } 409 if (executor == null) { 410 throw new IllegalArgumentException("Must include a non-null Executor."); 411 } 412 413 IImsRcsController imsRcsController = getIImsRcsController(); 414 if (imsRcsController == null) { 415 Log.e(TAG, "registerPublishStateCallback : IImsRcsController is null"); 416 throw new ImsException("Cannot find remote IMS service", 417 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 418 } 419 420 c.setExecutor(executor); 421 try { 422 imsRcsController.registerUcePublishStateCallback(mSubId, c.getBinder()); 423 } catch (android.os.ServiceSpecificException e) { 424 throw new ImsException(e.getMessage(), e.errorCode); 425 } catch (RemoteException e) { 426 Log.e(TAG, "Error calling IImsRcsController#registerUcePublishStateCallback", e); 427 throw new ImsException("Remote IMS Service is not available", 428 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 429 } 430 } 431 432 /** 433 * Removes an existing {@link PublishStateCallback}. 434 * <p> 435 * When the subscription associated with this callback is removed 436 * (SIM removed, ESIM swap,etc...), this callback will automatically be removed. If this method 437 * is called for an inactive subscription, it will result in a no-op. 438 * 439 * @param c The callback to be unregistered. 440 * @throws ImsException if the subscription associated with this callback is valid, but 441 * the {@link ImsService} associated with the subscription is not available. This can happen if 442 * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed 443 * reason. 444 * @hide 445 */ 446 @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) unregisterPublishStateCallback(@onNull PublishStateCallback c)447 public void unregisterPublishStateCallback(@NonNull PublishStateCallback c) 448 throws ImsException { 449 if (c == null) { 450 throw new IllegalArgumentException("Must include a non-null PublishStateCallback."); 451 } 452 IImsRcsController imsRcsController = getIImsRcsController(); 453 if (imsRcsController == null) { 454 Log.e(TAG, "unregisterPublishStateCallback: IImsRcsController is null"); 455 throw new ImsException("Cannot find remote IMS service", 456 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 457 } 458 459 try { 460 imsRcsController.unregisterUcePublishStateCallback(mSubId, c.getBinder()); 461 } catch (android.os.ServiceSpecificException e) { 462 throw new ImsException(e.getMessage(), e.errorCode); 463 } catch (RemoteException e) { 464 Log.e(TAG, "Error calling IImsRcsController#unregisterUcePublishStateCallback", e); 465 throw new ImsException("Remote IMS Service is not available", 466 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 467 } 468 } 469 470 /** 471 * The user’s setting for whether or not User Capability Exchange (UCE) is enabled for the 472 * associated subscription. 473 * <p> 474 * Note: This setting does not affect whether or not the device publishes its service 475 * capabilities if the subscription supports presence publication. 476 * 477 * @return true if the user’s setting for UCE is enabled, false otherwise. 478 * @throws ImsException if the subscription associated with this instance of 479 * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not 480 * available. This can happen if the ImsService has crashed, for example, or if the subscription 481 * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. 482 */ 483 @RequiresPermission(Manifest.permission.READ_PHONE_STATE) isUceSettingEnabled()484 public boolean isUceSettingEnabled() throws ImsException { 485 IImsRcsController imsRcsController = getIImsRcsController(); 486 if (imsRcsController == null) { 487 Log.e(TAG, "isUceSettingEnabled: IImsRcsController is null"); 488 throw new ImsException("Can not find remote IMS service", 489 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 490 } 491 try { 492 // Telephony.SimInfo#IMS_RCS_UCE_ENABLED can also be used to listen to changes to this. 493 return imsRcsController.isUceSettingEnabled(mSubId, mContext.getOpPackageName(), 494 null /*featureId*/); 495 } catch (RemoteException e) { 496 Log.e(TAG, "Error calling IImsRcsController#isUceSettingEnabled", e); 497 throw new ImsException("Remote IMS Service is not available", 498 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 499 } 500 } 501 502 /** 503 * Change the user’s setting for whether or not UCE is enabled for the associated subscription. 504 * <p> 505 * If an application Requires UCE, they will launch an Activity using the Intent 506 * {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN}, which will ask the user if 507 * they wish to enable this feature. This setting should only be enabled after the user has 508 * opted-in to capability exchange. 509 * <p> 510 * Note: This setting does not affect whether or not the device publishes its service 511 * capabilities if the subscription supports presence publication. 512 * 513 * @param isEnabled the user's setting for whether or not they wish for User 514 * Capability Exchange to be enabled. 515 * @throws ImsException if the subscription associated with this instance of 516 * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not 517 * available. This can happen if the ImsService has crashed, for example, or if the subscription 518 * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. 519 * @hide 520 */ 521 @SystemApi 522 @TestApi 523 @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) setUceSettingEnabled(boolean isEnabled)524 public void setUceSettingEnabled(boolean isEnabled) throws ImsException { 525 IImsRcsController imsRcsController = getIImsRcsController(); 526 if (imsRcsController == null) { 527 Log.e(TAG, "setUceSettingEnabled: IImsRcsController is null"); 528 throw new ImsException("Can not find remote IMS service", 529 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 530 } 531 532 try { 533 imsRcsController.setUceSettingEnabled(mSubId, isEnabled); 534 } catch (RemoteException e) { 535 Log.e(TAG, "Error calling IImsRcsController#setUceSettingEnabled", e); 536 throw new ImsException("Remote IMS Service is not available", 537 ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); 538 } 539 } 540 getIImsRcsController()541 private IImsRcsController getIImsRcsController() { 542 IBinder binder = ServiceManager.getService(Context.TELEPHONY_IMS_SERVICE); 543 return IImsRcsController.Stub.asInterface(binder); 544 } 545 } 546