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.feature; 18 19 import android.annotation.CallbackExecutor; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.SystemApi; 23 import android.annotation.TestApi; 24 import android.net.Uri; 25 import android.os.RemoteException; 26 import android.telephony.ims.RcsContactUceCapability; 27 import android.telephony.ims.aidl.IImsCapabilityCallback; 28 import android.telephony.ims.aidl.IImsRcsFeature; 29 import android.telephony.ims.aidl.IRcsFeatureListener; 30 import android.telephony.ims.stub.ImsRegistrationImplBase; 31 import android.telephony.ims.stub.RcsPresenceExchangeImplBase; 32 import android.telephony.ims.stub.RcsSipOptionsImplBase; 33 import android.util.Log; 34 35 import com.android.internal.telephony.util.TelephonyUtils; 36 37 import java.lang.annotation.Retention; 38 import java.lang.annotation.RetentionPolicy; 39 import java.util.List; 40 import java.util.concurrent.CancellationException; 41 import java.util.concurrent.CompletableFuture; 42 import java.util.concurrent.CompletionException; 43 import java.util.concurrent.ExecutionException; 44 import java.util.concurrent.Executor; 45 import java.util.function.Supplier; 46 47 /** 48 * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend 49 * this class and provide implementations of the RcsFeature methods that they support. 50 * @hide 51 */ 52 @SystemApi 53 @TestApi 54 public class RcsFeature extends ImsFeature { 55 56 private static final String LOG_TAG = "RcsFeature"; 57 58 private static final class RcsFeatureBinder extends IImsRcsFeature.Stub { 59 // Reference the outer class in order to have better test coverage metrics instead of 60 // creating a inner class referencing the outer class directly. 61 private final RcsFeature mReference; 62 private final Executor mExecutor; 63 RcsFeatureBinder(RcsFeature classRef, @CallbackExecutor Executor executor)64 RcsFeatureBinder(RcsFeature classRef, @CallbackExecutor Executor executor) { 65 mReference = classRef; 66 mExecutor = executor; 67 } 68 69 @Override setListener(IRcsFeatureListener listener)70 public void setListener(IRcsFeatureListener listener) { 71 mReference.setListener(listener); 72 } 73 74 @Override queryCapabilityStatus()75 public int queryCapabilityStatus() throws RemoteException { 76 return executeMethodAsyncForResult( 77 () -> mReference.queryCapabilityStatus().mCapabilities, 78 "queryCapabilityStatus"); 79 } 80 81 @Override addCapabilityCallback(IImsCapabilityCallback c)82 public void addCapabilityCallback(IImsCapabilityCallback c) throws RemoteException { 83 executeMethodAsync(() -> mReference.addCapabilityCallback(c), "addCapabilityCallback"); 84 } 85 86 @Override removeCapabilityCallback(IImsCapabilityCallback c)87 public void removeCapabilityCallback(IImsCapabilityCallback c) throws RemoteException { 88 executeMethodAsync(() -> mReference.removeCapabilityCallback(c), 89 "removeCapabilityCallback"); 90 } 91 92 @Override changeCapabilitiesConfiguration(CapabilityChangeRequest r, IImsCapabilityCallback c)93 public void changeCapabilitiesConfiguration(CapabilityChangeRequest r, 94 IImsCapabilityCallback c) throws RemoteException { 95 executeMethodAsync(() -> mReference.requestChangeEnabledCapabilities(r, c), 96 "changeCapabilitiesConfiguration"); 97 } 98 99 @Override queryCapabilityConfiguration(int capability, int radioTech, IImsCapabilityCallback c)100 public void queryCapabilityConfiguration(int capability, int radioTech, 101 IImsCapabilityCallback c) throws RemoteException { 102 executeMethodAsync(() -> mReference.queryCapabilityConfigurationInternal(capability, 103 radioTech, c), "queryCapabilityConfiguration"); 104 } 105 106 @Override getFeatureState()107 public int getFeatureState() throws RemoteException { 108 return executeMethodAsyncForResult(mReference::getFeatureState, "getFeatureState"); 109 } 110 111 // RcsPresenceExchangeImplBase specific APIS 112 @Override requestCapabilities(List<Uri> uris, int operationToken)113 public void requestCapabilities(List<Uri> uris, int operationToken) throws RemoteException { 114 executeMethodAsync(() -> mReference.getPresenceExchangeInternal() 115 .requestCapabilities(uris, operationToken), "requestCapabilities"); 116 } 117 @Override updateCapabilities(RcsContactUceCapability capabilities, int operationToken)118 public void updateCapabilities(RcsContactUceCapability capabilities, int operationToken) 119 throws RemoteException { 120 executeMethodAsync(() -> mReference.getPresenceExchangeInternal() 121 .updateCapabilities(capabilities, operationToken), 122 "updateCapabilities"); 123 124 } 125 // RcsSipOptionsImplBase specific APIS 126 @Override sendCapabilityRequest(Uri contactUri, RcsContactUceCapability capabilities, int operationToken)127 public void sendCapabilityRequest(Uri contactUri, RcsContactUceCapability capabilities, 128 int operationToken) throws RemoteException { 129 executeMethodAsync(() -> mReference.getOptionsExchangeInternal() 130 .sendCapabilityRequest(contactUri, capabilities, operationToken), 131 "sendCapabilityRequest"); 132 133 } 134 @Override respondToCapabilityRequest(String contactUri, RcsContactUceCapability ownCapabilities, int operationToken)135 public void respondToCapabilityRequest(String contactUri, 136 RcsContactUceCapability ownCapabilities, int operationToken) 137 throws RemoteException { 138 executeMethodAsync(() -> mReference.getOptionsExchangeInternal() 139 .respondToCapabilityRequest(contactUri, ownCapabilities, 140 operationToken), "respondToCapabilityRequest"); 141 142 } 143 @Override respondToCapabilityRequestWithError(Uri contactUri, int code, String reason, int operationToken)144 public void respondToCapabilityRequestWithError(Uri contactUri, int code, String reason, 145 int operationToken) throws RemoteException { 146 executeMethodAsync(() -> mReference.getOptionsExchangeInternal() 147 .respondToCapabilityRequestWithError(contactUri, code, reason, 148 operationToken), "respondToCapabilityRequestWithError"); 149 } 150 151 // Call the methods with a clean calling identity on the executor and wait indefinitely for 152 // the future to return. executeMethodAsync(Runnable r, String errorLogName)153 private void executeMethodAsync(Runnable r, String errorLogName) 154 throws RemoteException { 155 // call with a clean calling identity on the executor and wait indefinitely for the 156 // future to return. 157 try { 158 CompletableFuture.runAsync( 159 () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join(); 160 } catch (CancellationException | CompletionException e) { 161 Log.w(LOG_TAG, "RcsFeatureBinder - " + errorLogName + " exception: " 162 + e.getMessage()); 163 throw new RemoteException(e.getMessage()); 164 } 165 } 166 executeMethodAsyncForResult(Supplier<T> r, String errorLogName)167 private <T> T executeMethodAsyncForResult(Supplier<T> r, 168 String errorLogName) throws RemoteException { 169 // call with a clean calling identity on the executor and wait indefinitely for the 170 // future to return. 171 CompletableFuture<T> future = CompletableFuture.supplyAsync( 172 () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor); 173 try { 174 return future.get(); 175 } catch (ExecutionException | InterruptedException e) { 176 Log.w(LOG_TAG, "RcsFeatureBinder - " + errorLogName + " exception: " 177 + e.getMessage()); 178 throw new RemoteException(e.getMessage()); 179 } 180 } 181 } 182 183 /** 184 * Contains the capabilities defined and supported by a {@link RcsFeature} in the 185 * form of a bitmask. The capabilities that are used in the RcsFeature are 186 * defined as: 187 * {@link RcsImsCapabilityFlag#CAPABILITY_TYPE_OPTIONS_UCE} 188 * {@link RcsImsCapabilityFlag#CAPABILITY_TYPE_PRESENCE_UCE} 189 * 190 * The enabled capabilities of this RcsFeature will be set by the framework 191 * using {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}. 192 * After the capabilities have been set, the RcsFeature may then perform the necessary bring up 193 * of the capability and notify the capability status as true using 194 * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the 195 * framework that the capability is available for usage. 196 * @hide 197 */ 198 public static class RcsImsCapabilities extends Capabilities { 199 /** @hide*/ 200 @Retention(RetentionPolicy.SOURCE) 201 @IntDef(prefix = "CAPABILITY_TYPE_", flag = true, value = { 202 CAPABILITY_TYPE_NONE, 203 CAPABILITY_TYPE_OPTIONS_UCE, 204 CAPABILITY_TYPE_PRESENCE_UCE 205 }) 206 public @interface RcsImsCapabilityFlag {} 207 208 /** 209 * Undefined capability type for initialization 210 */ 211 public static final int CAPABILITY_TYPE_NONE = 0; 212 213 /** 214 * This carrier supports User Capability Exchange using SIP OPTIONS as defined by the 215 * framework. If set, the RcsFeature should support capability exchange using SIP OPTIONS. 216 * If not set, this RcsFeature should not service capability requests. 217 */ 218 public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1 << 0; 219 220 /** 221 * This carrier supports User Capability Exchange using a presence server as defined by the 222 * framework. If set, the RcsFeature should support capability exchange using a presence 223 * server. If not set, this RcsFeature should not publish capabilities or service capability 224 * requests using presence. 225 */ 226 public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1; 227 RcsImsCapabilities(@csImsCapabilityFlag int capabilities)228 public RcsImsCapabilities(@RcsImsCapabilityFlag int capabilities) { 229 super(capabilities); 230 } 231 RcsImsCapabilities(Capabilities c)232 private RcsImsCapabilities(Capabilities c) { 233 super(c.getMask()); 234 } 235 236 @Override addCapabilities(@csImsCapabilityFlag int capabilities)237 public void addCapabilities(@RcsImsCapabilityFlag int capabilities) { 238 super.addCapabilities(capabilities); 239 } 240 241 @Override removeCapabilities(@csImsCapabilityFlag int capabilities)242 public void removeCapabilities(@RcsImsCapabilityFlag int capabilities) { 243 super.removeCapabilities(capabilities); 244 } 245 246 @Override isCapable(@csImsCapabilityFlag int capabilities)247 public boolean isCapable(@RcsImsCapabilityFlag int capabilities) { 248 return super.isCapable(capabilities); 249 } 250 } 251 252 private final RcsFeatureBinder mImsRcsBinder; 253 private IRcsFeatureListener mListenerBinder; 254 private RcsPresenceExchangeImplBase mPresExchange; 255 private RcsSipOptionsImplBase mSipOptions; 256 257 /** 258 * Create a new RcsFeature. 259 * <p> 260 * Method stubs called from the framework will be called asynchronously. To specify the 261 * {@link Executor} that the methods stubs will be called, use 262 * {@link RcsFeature#RcsFeature(Executor)} instead. 263 */ RcsFeature()264 public RcsFeature() { 265 super(); 266 // Run on the Binder threads that call them. 267 mImsRcsBinder = new RcsFeatureBinder(this, Runnable::run); 268 } 269 270 /** 271 * Create a new RcsFeature using the Executor specified for methods being called by the 272 * framework. 273 * @param executor The executor for the framework to use when making calls to this service. 274 * @hide 275 */ RcsFeature(@onNull Executor executor)276 public RcsFeature(@NonNull Executor executor) { 277 super(); 278 if (executor == null) { 279 throw new IllegalArgumentException("executor can not be null."); 280 } 281 // Run on the Binder thread by default. 282 mImsRcsBinder = new RcsFeatureBinder(this, executor); 283 } 284 285 /** 286 * Query the current {@link RcsImsCapabilities} status set by the RcsFeature. If a capability is 287 * set, the {@link RcsFeature} has brought up the capability and is ready for framework 288 * requests. To change the status of the capabilities 289 * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called. 290 * @hide 291 */ 292 @Override queryCapabilityStatus()293 public @NonNull final RcsImsCapabilities queryCapabilityStatus() { 294 return new RcsImsCapabilities(super.queryCapabilityStatus()); 295 } 296 297 /** 298 * Notify the framework that the capabilities status has changed. If a capability is enabled, 299 * this signals to the framework that the capability has been initialized and is ready. 300 * Call {@link #queryCapabilityStatus()} to return the current capability status. 301 * @hide 302 */ notifyCapabilitiesStatusChanged(@onNull RcsImsCapabilities c)303 public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities c) { 304 if (c == null) { 305 throw new IllegalArgumentException("RcsImsCapabilities must be non-null!"); 306 } 307 super.notifyCapabilitiesStatusChanged(c); 308 } 309 310 /** 311 * Provides the RcsFeature with the ability to return the framework capability configuration set 312 * by the framework. When the framework calls 313 * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} to 314 * enable or disable capability A, this method should return the correct configuration for 315 * capability A afterwards (until it has changed). 316 * @hide 317 */ queryCapabilityConfiguration( @csImsCapabilities.RcsImsCapabilityFlag int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)318 public boolean queryCapabilityConfiguration( 319 @RcsImsCapabilities.RcsImsCapabilityFlag int capability, 320 @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { 321 // Base Implementation - Override to provide functionality 322 return false; 323 } 324 /** 325 * Called from the framework when the {@link RcsImsCapabilities} that have been configured for 326 * this {@link RcsFeature} has changed. 327 * <p> 328 * For each newly enabled capability flag, the corresponding capability should be brought up in 329 * the {@link RcsFeature} and registered on the network. For each newly disabled capability 330 * flag, the corresponding capability should be brought down, and deregistered. Once a new 331 * capability has been initialized and is ready for usage, the status of that capability should 332 * also be set to true using {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This 333 * will notify the framework that the capability is ready. 334 * <p> 335 * If for some reason one or more of these capabilities can not be enabled/disabled, 336 * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError(int, int, int)} should 337 * be called for each capability change that resulted in an error. 338 * @hide 339 */ 340 @Override changeEnabledCapabilities(@onNull CapabilityChangeRequest request, @NonNull CapabilityCallbackProxy c)341 public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request, 342 @NonNull CapabilityCallbackProxy c) { 343 // Base Implementation - Override to provide functionality 344 } 345 346 /** 347 * Retrieve the implementation of SIP OPTIONS for this {@link RcsFeature}. 348 * <p> 349 * Will only be requested by the framework if capability exchange via SIP OPTIONS is 350 * configured as capable during a 351 * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} 352 * operation and the RcsFeature sets the status of the capability to true using 353 * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. 354 * 355 * @return An instance of {@link RcsSipOptionsImplBase} that implements SIP options exchange if 356 * it is supported by the device. 357 * @hide 358 */ getOptionsExchangeImpl()359 public @NonNull RcsSipOptionsImplBase getOptionsExchangeImpl() { 360 // Base Implementation, override to implement functionality 361 return new RcsSipOptionsImplBase(); 362 } 363 364 /** 365 * Retrieve the implementation of UCE presence for this {@link RcsFeature}. 366 * Will only be requested by the framework if presence exchang is configured as capable during 367 * a {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} 368 * operation and the RcsFeature sets the status of the capability to true using 369 * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. 370 * 371 * @return An instance of {@link RcsPresenceExchangeImplBase} that implements presence 372 * exchange if it is supported by the device. 373 * @hide 374 */ getPresenceExchangeImpl()375 public @NonNull RcsPresenceExchangeImplBase getPresenceExchangeImpl() { 376 // Base Implementation, override to implement functionality. 377 return new RcsPresenceExchangeImplBase(); 378 } 379 380 /**{@inheritDoc}*/ 381 @Override onFeatureRemoved()382 public void onFeatureRemoved() { 383 384 } 385 386 /**{@inheritDoc}*/ 387 @Override onFeatureReady()388 public void onFeatureReady() { 389 390 } 391 392 /** 393 * @hide 394 */ 395 @Override getBinder()396 public final IImsRcsFeature getBinder() { 397 return mImsRcsBinder; 398 } 399 400 /**@hide*/ getListener()401 public IRcsFeatureListener getListener() { 402 synchronized (mLock) { 403 return mListenerBinder; 404 } 405 } 406 setListener(IRcsFeatureListener listener)407 private void setListener(IRcsFeatureListener listener) { 408 synchronized (mLock) { 409 mListenerBinder = listener; 410 if (mListenerBinder != null) { 411 onFeatureReady(); 412 } 413 } 414 } 415 getPresenceExchangeInternal()416 private RcsPresenceExchangeImplBase getPresenceExchangeInternal() { 417 synchronized (mLock) { 418 if (mPresExchange == null) { 419 mPresExchange = getPresenceExchangeImpl(); 420 mPresExchange.initialize(this); 421 } 422 return mPresExchange; 423 } 424 } 425 getOptionsExchangeInternal()426 private RcsSipOptionsImplBase getOptionsExchangeInternal() { 427 synchronized (mLock) { 428 if (mSipOptions == null) { 429 mSipOptions = getOptionsExchangeImpl(); 430 mSipOptions.initialize(this); 431 } 432 return mSipOptions; 433 } 434 } 435 } 436