1 /* 2 * Copyright (c) 2020 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.stub; 18 19 import android.annotation.IntDef; 20 import android.annotation.IntRange; 21 import android.annotation.NonNull; 22 import android.annotation.SuppressLint; 23 import android.annotation.SystemApi; 24 import android.net.Uri; 25 import android.telephony.ims.ImsException; 26 import android.telephony.ims.RcsUceAdapter; 27 import android.telephony.ims.SipDetails; 28 import android.telephony.ims.feature.ImsFeature; 29 import android.telephony.ims.feature.RcsFeature; 30 import android.text.TextUtils; 31 import android.util.Log; 32 import android.util.Pair; 33 34 import java.lang.annotation.Retention; 35 import java.lang.annotation.RetentionPolicy; 36 import java.util.Collection; 37 import java.util.List; 38 import java.util.Set; 39 import java.util.concurrent.Executor; 40 41 /** 42 * Extend this base class to implement RCS User Capability Exchange (UCE) for the AOSP platform 43 * using the vendor ImsService. 44 * <p> 45 * See RCC.07 for more details on UCE as well as how UCE should be implemented. 46 * @hide 47 */ 48 @SystemApi 49 public class RcsCapabilityExchangeImplBase { 50 51 private static final String LOG_TAG = "RcsCapExchangeImplBase"; 52 53 /** 54 * Service is unknown. 55 */ 56 public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0; 57 58 /** 59 * The command failed with an unknown error. 60 */ 61 public static final int COMMAND_CODE_GENERIC_FAILURE = 1; 62 63 /** 64 * Invalid parameter(s). 65 */ 66 public static final int COMMAND_CODE_INVALID_PARAM = 2; 67 68 /** 69 * Fetch error. 70 */ 71 public static final int COMMAND_CODE_FETCH_ERROR = 3; 72 73 /** 74 * Request timed out. 75 */ 76 public static final int COMMAND_CODE_REQUEST_TIMEOUT = 4; 77 78 /** 79 * Failure due to insufficient memory available. 80 */ 81 public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 5; 82 83 /** 84 * Network connection is lost. 85 */ 86 public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 6; 87 88 /** 89 * Requested feature/resource is not supported. 90 */ 91 public static final int COMMAND_CODE_NOT_SUPPORTED = 7; 92 93 /** 94 * Contact or resource is not found. 95 */ 96 public static final int COMMAND_CODE_NOT_FOUND = 8; 97 98 /** 99 * Service is not available. 100 */ 101 public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 9; 102 103 /** 104 * Command resulted in no change in state, ignoring. 105 */ 106 public static final int COMMAND_CODE_NO_CHANGE = 10; 107 108 /**@hide*/ 109 @Retention(RetentionPolicy.SOURCE) 110 @IntDef(prefix = "COMMAND_CODE_", value = { 111 COMMAND_CODE_SERVICE_UNKNOWN, 112 COMMAND_CODE_GENERIC_FAILURE, 113 COMMAND_CODE_INVALID_PARAM, 114 COMMAND_CODE_FETCH_ERROR, 115 COMMAND_CODE_REQUEST_TIMEOUT, 116 COMMAND_CODE_INSUFFICIENT_MEMORY, 117 COMMAND_CODE_LOST_NETWORK_CONNECTION, 118 COMMAND_CODE_NOT_SUPPORTED, 119 COMMAND_CODE_NOT_FOUND, 120 COMMAND_CODE_SERVICE_UNAVAILABLE, 121 COMMAND_CODE_NO_CHANGE 122 }) 123 public @interface CommandCode {} 124 125 /** 126 * Interface used by the framework to receive the response of the publish request. 127 */ 128 public interface PublishResponseCallback { 129 /** 130 * Notify the framework that the command associated with the 131 * {@link #publishCapabilities(String, PublishResponseCallback)} has failed. 132 * 133 * @param code The reason why the associated command has failed. 134 * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is 135 * not currently connected to the framework. This can happen if the {@link RcsFeature} 136 * is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received 137 * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases 138 * when the Telephony stack has crashed. 139 */ onCommandError(@ommandCode int code)140 void onCommandError(@CommandCode int code) throws ImsException; 141 142 /** 143 * Provide the framework with a subsequent network response update to 144 * {@link #publishCapabilities(String, PublishResponseCallback)}. 145 * 146 * If this network response also contains a “Reason” header, then the 147 * {@link #onNetworkResponse(int, String, int, String)} method should be used instead. 148 * 149 * @param sipCode The SIP response code sent from the network for the operation 150 * token specified. 151 * @param reason The optional reason response from the network. If there is a reason header 152 * included in the response, that should take precedence over the reason provided in the 153 * status line. If the network provided no reason with the sip code, the string should be 154 * empty. 155 * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is 156 * not currently connected to the framework. This can happen if the {@link RcsFeature} 157 * is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received 158 * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases 159 * when the Telephony stack has crashed. 160 * 161 * @deprecated Replaced sip information with the newly added 162 * {@link #onNetworkResponse(SipDetails)}. 163 */ 164 @Deprecated onNetworkResponse(@ntRangefrom = 100, to = 699) int sipCode, @NonNull String reason)165 void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode, 166 @NonNull String reason) throws ImsException; 167 168 /** 169 * Provide the framework with a subsequent network response update to 170 * {@link #publishCapabilities(String, PublishResponseCallback)} that also 171 * includes a reason provided in the “reason” header. See RFC3326 for more 172 * information. 173 * 174 * @param sipCode The SIP response code sent from the network. 175 * @param reasonPhrase The optional reason response from the network. If the 176 * network provided no reason with the sip code, the string should be empty. 177 * @param reasonHeaderCause The “cause” parameter of the “reason” header 178 * included in the SIP message. 179 * @param reasonHeaderText The “text” parameter of the “reason” header 180 * included in the SIP message. 181 * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is 182 * not currently connected to the framework. This can happen if the 183 * {@link RcsFeature} is not 184 * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received 185 * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in 186 * rare cases when the Telephony stack has crashed. 187 * 188 * @deprecated Replaced sip information with the newly added 189 * {@link #onNetworkResponse(SipDetails)}. 190 */ 191 @Deprecated onNetworkResponse(@ntRangefrom = 100, to = 699) int sipCode, @NonNull String reasonPhrase, @IntRange(from = 100, to = 699) int reasonHeaderCause, @NonNull String reasonHeaderText)192 void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode, 193 @NonNull String reasonPhrase, 194 @IntRange(from = 100, to = 699) int reasonHeaderCause, 195 @NonNull String reasonHeaderText) throws ImsException; 196 197 /** 198 * Provide the framework with a subsequent network response update to 199 * {@link #publishCapabilities(String, PublishResponseCallback)}. 200 * 201 * @param details The SIP information received in response to a publish operation. 202 * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is 203 * not currently connected to the framework. This can happen if the {@link RcsFeature} 204 * is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received 205 * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases 206 * when the Telephony stack has crashed. 207 */ onNetworkResponse(@onNull SipDetails details)208 default void onNetworkResponse(@NonNull SipDetails details) throws ImsException { 209 if (TextUtils.isEmpty(details.getReasonHeaderText())) { 210 onNetworkResponse(details.getResponseCode(), details.getResponsePhrase()); 211 } else { 212 onNetworkResponse(details.getResponseCode(), details.getResponsePhrase(), 213 details.getReasonHeaderCause(), details.getReasonHeaderText()); 214 } 215 } 216 } 217 218 /** 219 * Interface used by the framework to respond to OPTIONS requests. 220 */ 221 public interface OptionsResponseCallback { 222 /** 223 * Notify the framework that the command associated with this callback has failed. 224 * 225 * @param code The reason why the associated command has failed. 226 * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is 227 * not currently connected to the framework. This can happen if the 228 * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} 229 * has not received the {@link ImsFeature#onFeatureReady()} callback. This may also happen 230 * in rare cases when the Telephony stack has crashed. 231 */ onCommandError(@ommandCode int code)232 void onCommandError(@CommandCode int code) throws ImsException; 233 234 /** 235 * Send the response of a SIP OPTIONS capability exchange to the framework. 236 * @param sipCode The SIP response code that was sent by the network in response 237 * to the request sent by {@link #sendOptionsCapabilityRequest}. 238 * @param reason The optional SIP response reason sent by the network. 239 * If none was sent, this should be an empty string. 240 * @param theirCaps the contact's UCE capabilities associated with the 241 * capability request. 242 * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not 243 * currently connected to the framework. This can happen if the 244 * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the 245 * {@link RcsFeature} has not received the 246 * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare 247 * cases when the Telephony stack has crashed. 248 */ onNetworkResponse(int sipCode, @NonNull String reason, @NonNull List<String> theirCaps)249 void onNetworkResponse(int sipCode, @NonNull String reason, 250 @NonNull List<String> theirCaps) throws ImsException; 251 } 252 253 /** 254 * Interface used by the framework to receive the response of the subscribe request. 255 */ 256 public interface SubscribeResponseCallback { 257 /** 258 * Notify the framework that the command associated with this callback has failed. 259 * <p> 260 * Must only be called when there was an error generating a SUBSCRIBE request due to an 261 * IMS stack error. This is a terminating event, so no other callback event will be 262 * expected after this callback. 263 * 264 * @param code The reason why the associated command has failed. 265 * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is 266 * not currently connected to the framework. This can happen if the 267 * {@link RcsFeature} is not 268 * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received 269 * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in 270 * rare cases when the Telephony stack has crashed. 271 */ onCommandError(@ommandCode int code)272 void onCommandError(@CommandCode int code) throws ImsException; 273 274 /** 275 * Notify the framework of the response to the SUBSCRIBE request from 276 * {@link #subscribeForCapabilities(Collection, SubscribeResponseCallback)}. 277 * <p> 278 * If the carrier network responds to the SUBSCRIBE request with a 2XX response, then the 279 * framework will expect the IMS stack to call {@link #onNotifyCapabilitiesUpdate}, 280 * {@link #onResourceTerminated}, and {@link #onTerminated} as required for the 281 * subsequent NOTIFY responses to the subscription. 282 * 283 * If this network response also contains a “Reason” header, then the 284 * {@link #onNetworkResponse(int, String, int, String)} method should be used instead. 285 * 286 * @param sipCode The SIP response code sent from the network for the operation 287 * token specified. 288 * @param reason The optional reason response from the network. If the network 289 * provided no reason with the sip code, the string should be empty. 290 * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is 291 * not currently connected to the framework. This can happen if the 292 * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the 293 * {@link RcsFeature} has not received the {@link ImsFeature#onFeatureReady()} callback. 294 * This may also happen in rare cases when the Telephony stack has crashed. 295 * 296 * @deprecated Replaced sip information with the newly added 297 * {@link #onNetworkResponse(SipDetails)}. 298 */ 299 @Deprecated onNetworkResponse(@ntRangefrom = 100, to = 699) int sipCode, @NonNull String reason)300 void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode, 301 @NonNull String reason) throws ImsException; 302 303 /** 304 * Notify the framework of the response to the SUBSCRIBE request from 305 * {@link #subscribeForCapabilities(Collection, SubscribeResponseCallback)} that also 306 * includes a reason provided in the “reason” header. See RFC3326 for more 307 * information. 308 * 309 * @param sipCode The SIP response code sent from the network, 310 * @param reasonPhrase The optional reason response from the network. If the 311 * network provided no reason with the sip code, the string should be empty. 312 * @param reasonHeaderCause The “cause” parameter of the “reason” header 313 * included in the SIP message. 314 * @param reasonHeaderText The “text” parameter of the “reason” header 315 * included in the SIP message. 316 * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is 317 * not currently connected to the framework. This can happen if the 318 * {@link RcsFeature} is not 319 * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received 320 * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in 321 * rare cases when the Telephony stack has crashed. 322 * 323 * @deprecated Replaced sip information with the newly added 324 * {@link #onNetworkResponse(SipDetails)}. 325 */ 326 @Deprecated onNetworkResponse(@ntRangefrom = 100, to = 699) int sipCode, @NonNull String reasonPhrase, @IntRange(from = 100, to = 699) int reasonHeaderCause, @NonNull String reasonHeaderText)327 void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode, 328 @NonNull String reasonPhrase, 329 @IntRange(from = 100, to = 699) int reasonHeaderCause, 330 @NonNull String reasonHeaderText) throws ImsException; 331 332 /** 333 * Notify the framework of the response to the SUBSCRIBE request from 334 * {@link #subscribeForCapabilities(Collection, SubscribeResponseCallback)}. 335 * <p> 336 * If the carrier network responds to the SUBSCRIBE request with a 2XX response, then the 337 * framework will expect the IMS stack to call {@link #onNotifyCapabilitiesUpdate}, 338 * {@link #onResourceTerminated}, and {@link #onTerminated} as required for the 339 * subsequent NOTIFY responses to the subscription. 340 * 341 * @param details The SIP information related to this request. 342 * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is 343 * not currently connected to the framework. This can happen if the 344 * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the 345 * {@link RcsFeature} has not received the {@link ImsFeature#onFeatureReady()} callback. 346 * This may also happen in rare cases when the Telephony stack has crashed. 347 */ onNetworkResponse(@onNull SipDetails details)348 default void onNetworkResponse(@NonNull SipDetails details) throws ImsException { 349 if (TextUtils.isEmpty(details.getReasonHeaderText())) { 350 onNetworkResponse(details.getResponseCode(), details.getResponsePhrase()); 351 } else { 352 onNetworkResponse(details.getResponseCode(), details.getResponsePhrase(), 353 details.getReasonHeaderCause(), details.getReasonHeaderText()); 354 } 355 }; 356 357 /** 358 * Notify the framework of the latest XML PIDF documents included in the network response 359 * for the requested contacts' capabilities requested by the Framework using 360 * {@link RcsUceAdapter#requestCapabilities(List, Executor, 361 * RcsUceAdapter.CapabilitiesCallback)}. 362 * <p> 363 * The expected format for the PIDF XML is defined in RFC3861. Each XML document must be a 364 * "application/pidf+xml" object and start with a root <presence> element. For NOTIFY 365 * responses that contain RLMI information and potentially multiple PIDF XMLs, each 366 * PIDF XML should be separated and added as a separate item in the List. This should be 367 * called every time a new NOTIFY event is received with new capability information. 368 * 369 * @param pidfXmls The list of the PIDF XML data for the contact URIs that it subscribed 370 * for. 371 * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is 372 * not currently connected to the framework. 373 * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the 374 * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not 375 * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in 376 * rare cases when the Telephony stack has crashed. 377 */ onNotifyCapabilitiesUpdate(@onNull List<String> pidfXmls)378 void onNotifyCapabilitiesUpdate(@NonNull List<String> pidfXmls) throws ImsException; 379 380 /** 381 * Notify the framework that a resource in the RLMI XML contained in the NOTIFY response 382 * for the ongoing SUBSCRIBE dialog has been terminated. 383 * <p> 384 * This will be used to notify the framework that a contact URI that the IMS stack has 385 * subscribed to on the Resource List Server has been terminated as well as the reason why. 386 * Usually this means that there will not be any capability information for the contact URI 387 * that they subscribed for. See RFC 4662 for more information. 388 * 389 * @param uriTerminatedReason The contact URIs which have been terminated. Each pair in the 390 * list is the contact URI and its terminated reason. 391 * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is 392 * not currently connected to the framework. 393 * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the 394 * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not 395 * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in 396 * rare cases when the Telephony stack has crashed. 397 */ onResourceTerminated( @onNull List<Pair<Uri, String>> uriTerminatedReason)398 void onResourceTerminated( 399 @NonNull List<Pair<Uri, String>> uriTerminatedReason) throws ImsException; 400 401 /** 402 * The subscription associated with a previous 403 * {@link RcsUceAdapter#requestCapabilities(List, Executor, 404 * RcsUceAdapter.CapabilitiesCallback)} 405 * operation has been terminated. This will mostly be due to the network sending a final 406 * NOTIFY response due to the subscription expiring, but this may also happen due to a 407 * network error. 408 * 409 * @param reason The reason for the request being unable to process. 410 * @param retryAfterMilliseconds The time in milliseconds the requesting application should 411 * wait before retrying, if non-zero. 412 * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is 413 * not currently connected to the framework. 414 * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the 415 * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not 416 * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in 417 * rare cases when the Telephony stack has crashed. 418 */ onTerminated(@onNull String reason, long retryAfterMilliseconds)419 void onTerminated(@NonNull String reason, long retryAfterMilliseconds) throws ImsException; 420 } 421 422 /** 423 * Create a new RcsCapabilityExchangeImplBase instance. 424 */ RcsCapabilityExchangeImplBase()425 public RcsCapabilityExchangeImplBase() { 426 } 427 428 /** 429 * The user capabilities of one or multiple contacts have been requested by the framework. 430 * <p> 431 * The implementer must follow up this call with an 432 * {@link SubscribeResponseCallback#onCommandError} call to indicate this operation has failed. 433 * The response from the network to the SUBSCRIBE request must be sent back to the framework 434 * using {@link SubscribeResponseCallback#onNetworkResponse(int, String)}. 435 * As NOTIFY requests come in from the network, the requested contact’s capabilities should be 436 * sent back to the framework using 437 * {@link SubscribeResponseCallback#onNotifyCapabilitiesUpdate(List<String>}) and 438 * {@link SubscribeResponseCallback#onResourceTerminated(List<Pair<Uri, String>>)} 439 * should be called with the presence information for the contacts specified. 440 * <p> 441 * Once the subscription is terminated, 442 * {@link SubscribeResponseCallback#onTerminated(String, long)} must be called for the 443 * framework to finish listening for NOTIFY responses. 444 * 445 * @param uris A {@link Collection} of the {@link Uri}s that the framework is requesting the 446 * UCE capabilities for. 447 * @param cb The callback of the subscribe request. 448 */ 449 // executor used is defined in the constructor. 450 @SuppressLint("ExecutorRegistration") subscribeForCapabilities(@onNull Collection<Uri> uris, @NonNull SubscribeResponseCallback cb)451 public void subscribeForCapabilities(@NonNull Collection<Uri> uris, 452 @NonNull SubscribeResponseCallback cb) { 453 // Stub - to be implemented by service 454 Log.w(LOG_TAG, "subscribeForCapabilities called with no implementation."); 455 try { 456 cb.onCommandError(COMMAND_CODE_NOT_SUPPORTED); 457 } catch (ImsException e) { 458 // Do not do anything, this is a stub implementation. 459 } 460 } 461 462 /** 463 * The capabilities of this device have been updated and should be published to the network. 464 * <p> 465 * If this operation succeeds, network response updates should be sent to the framework using 466 * {@link PublishResponseCallback#onNetworkResponse(int, String)}. 467 * @param pidfXml The XML PIDF document containing the capabilities of this device to be sent 468 * to the carrier’s presence server. 469 * @param cb The callback of the publish request 470 */ 471 // executor used is defined in the constructor. 472 @SuppressLint("ExecutorRegistration") publishCapabilities(@onNull String pidfXml, @NonNull PublishResponseCallback cb)473 public void publishCapabilities(@NonNull String pidfXml, @NonNull PublishResponseCallback cb) { 474 // Stub - to be implemented by service 475 Log.w(LOG_TAG, "publishCapabilities called with no implementation."); 476 try { 477 cb.onCommandError(COMMAND_CODE_NOT_SUPPORTED); 478 } catch (ImsException e) { 479 // Do not do anything, this is a stub implementation. 480 } 481 } 482 483 /** 484 * Push one's own capabilities to a remote user via the SIP OPTIONS presence exchange mechanism 485 * in order to receive the capabilities of the remote user in response. 486 * <p> 487 * The implementer must use {@link OptionsResponseCallback} to send the response of 488 * this query from the network back to the framework. 489 * @param contactUri The URI of the remote user that we wish to get the capabilities of. 490 * @param myCapabilities The capabilities of this device to send to the remote user. 491 * @param callback The callback of this request which is sent from the remote user. 492 */ 493 // executor used is defined in the constructor. 494 @SuppressLint("ExecutorRegistration") sendOptionsCapabilityRequest(@onNull Uri contactUri, @NonNull Set<String> myCapabilities, @NonNull OptionsResponseCallback callback)495 public void sendOptionsCapabilityRequest(@NonNull Uri contactUri, 496 @NonNull Set<String> myCapabilities, @NonNull OptionsResponseCallback callback) { 497 // Stub - to be implemented by service 498 Log.w(LOG_TAG, "sendOptionsCapabilityRequest called with no implementation."); 499 try { 500 callback.onCommandError(COMMAND_CODE_NOT_SUPPORTED); 501 } catch (ImsException e) { 502 // Do not do anything, this is a stub implementation. 503 } 504 } 505 } 506