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.stub; 18 19 import android.annotation.IntDef; 20 import android.annotation.IntRange; 21 import android.annotation.NonNull; 22 import android.annotation.SystemApi; 23 import android.os.RemoteException; 24 import android.telephony.SmsManager; 25 import android.telephony.SmsMessage; 26 import android.telephony.ims.aidl.IImsSmsListener; 27 import android.util.Log; 28 29 import java.lang.annotation.Retention; 30 import java.lang.annotation.RetentionPolicy; 31 import java.util.concurrent.Executor; 32 33 /** 34 * Base implementation for SMS over IMS. 35 * 36 * Any service wishing to provide SMS over IMS should extend this class and implement all methods 37 * that the service supports. 38 * 39 * @hide 40 */ 41 @SystemApi 42 public class ImsSmsImplBase { 43 private static final String LOG_TAG = "SmsImplBase"; 44 45 /** @hide */ 46 @IntDef({ 47 SEND_STATUS_OK, 48 SEND_STATUS_ERROR, 49 SEND_STATUS_ERROR_RETRY, 50 SEND_STATUS_ERROR_FALLBACK 51 }) 52 @Retention(RetentionPolicy.SOURCE) 53 public @interface SendStatusResult {} 54 /** 55 * Message was sent successfully. 56 */ 57 public static final int SEND_STATUS_OK = 1; 58 59 /** 60 * IMS provider failed to send the message and platform should not retry falling back to sending 61 * the message using the radio. 62 */ 63 public static final int SEND_STATUS_ERROR = 2; 64 65 /** 66 * IMS provider failed to send the message and platform should retry again after setting TP-RD 67 * bit to high. 68 */ 69 public static final int SEND_STATUS_ERROR_RETRY = 3; 70 71 /** 72 * IMS provider failed to send the message and platform should retry falling back to sending 73 * the message using the radio. 74 */ 75 public static final int SEND_STATUS_ERROR_FALLBACK = 4; 76 77 /** @hide */ 78 @IntDef({ 79 DELIVER_STATUS_OK, 80 DELIVER_STATUS_ERROR_GENERIC, 81 DELIVER_STATUS_ERROR_NO_MEMORY, 82 DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED 83 }) 84 @Retention(RetentionPolicy.SOURCE) 85 public @interface DeliverStatusResult {} 86 /** 87 * Message was delivered successfully. 88 */ 89 public static final int DELIVER_STATUS_OK = 1; 90 91 /** 92 * Message was not delivered. 93 */ 94 public static final int DELIVER_STATUS_ERROR_GENERIC = 2; 95 96 /** 97 * Message was not delivered due to lack of memory. 98 */ 99 public static final int DELIVER_STATUS_ERROR_NO_MEMORY = 3; 100 101 /** 102 * Message was not delivered as the request is not supported. 103 */ 104 public static final int DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED = 4; 105 106 /** @hide */ 107 @IntDef({ 108 STATUS_REPORT_STATUS_OK, 109 STATUS_REPORT_STATUS_ERROR 110 }) 111 @Retention(RetentionPolicy.SOURCE) 112 public @interface StatusReportResult {} 113 114 /** 115 * Status Report was set successfully. 116 */ 117 public static final int STATUS_REPORT_STATUS_OK = 1; 118 119 /** 120 * Error while setting status report. 121 */ 122 public static final int STATUS_REPORT_STATUS_ERROR = 2; 123 124 /** 125 * No network error was generated while processing the SMS message. 126 */ 127 // Should match SmsResponse.NO_ERROR_CODE 128 public static final int RESULT_NO_NETWORK_ERROR = -1; 129 130 // Lock for feature synchronization 131 private final Object mLock = new Object(); 132 private IImsSmsListener mListener; 133 private Executor mExecutor; 134 135 /** 136 * Create a new ImsSmsImplBase using the Executor set in MmTelFeature 137 */ ImsSmsImplBase()138 public ImsSmsImplBase() { 139 } 140 141 /** 142 * Create a new ImsSmsImplBase with specified executor. 143 * <p> 144 * @param executor Default executor for ImsSmsImplBase 145 */ ImsSmsImplBase(@onNull Executor executor)146 public ImsSmsImplBase(@NonNull Executor executor) { 147 mExecutor = executor; 148 } 149 150 /** 151 * Registers a listener responsible for handling tasks like delivering messages. 152 * 153 * @param listener listener to register. 154 * 155 * @hide 156 */ registerSmsListener(IImsSmsListener listener)157 public final void registerSmsListener(IImsSmsListener listener) { 158 synchronized (mLock) { 159 mListener = listener; 160 } 161 } 162 163 /** 164 * This method will be triggered by the platform when the user attempts to send an SMS. This 165 * method should be implemented by the IMS providers to provide implementation of sending an SMS 166 * over IMS. 167 * 168 * @param token unique token generated by the platform that should be used when triggering 169 * callbacks for this specific message. 170 * @param messageRef the message reference, which may be 1 byte if it is in 171 * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in 172 * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B). 173 * @param format the format of the message. 174 * @param smsc the Short Message Service Center address. 175 * @param isRetry whether it is a retry of an already attempted message or not. 176 * @param pdu PDU representing the contents of the message. 177 */ sendSms(int token, @IntRange(from = 0, to = 65535) int messageRef, @SmsMessage.Format String format, String smsc, boolean isRetry, byte[] pdu)178 public void sendSms(int token, @IntRange(from = 0, to = 65535) int messageRef, 179 @SmsMessage.Format String format, String smsc, boolean isRetry, 180 byte[] pdu) { 181 // Base implementation returns error. Should be overridden. 182 try { 183 onSendSmsResult(token, messageRef, SEND_STATUS_ERROR, 184 SmsManager.RESULT_ERROR_GENERIC_FAILURE); 185 } catch (RuntimeException e) { 186 Log.e(LOG_TAG, "Can not send sms: " + e.getMessage()); 187 } 188 } 189 190 /** 191 * This method will be triggered by the platform when memory becomes available to receive SMS 192 * after a memory full event. This method should be implemented by IMS providers to 193 * send RP-SMMA notification from SMS Relay Layer to server over IMS as per section 7.3.2 of 194 * TS 124.11. Once the RP-SMMA Notification is sent to the network. The network will deliver all 195 * the pending messages which failed due to Unavailability of Memory. 196 * 197 * @param token unique token generated in {@link ImsSmsDispatcher#onMemoryAvailable(void)} that 198 * should be used when triggering callbacks for this specific message. 199 * 200 * @hide 201 */ onMemoryAvailable(int token)202 public void onMemoryAvailable(int token) { 203 // Base Implementation - Should be overridden 204 } 205 206 /** 207 * This method will be triggered by the platform after 208 * {@link #onSmsReceived(int, String, byte[])} has been called to deliver the result to the IMS 209 * provider. 210 * 211 * If the framework needs to provide the PDU used to acknowledge the SMS, 212 * {@link #acknowledgeSms(int, int, int, byte[])} will be called. 213 * 214 * @param token token provided in {@link #onSmsReceived(int, String, byte[])} 215 * @param messageRef the message reference, which may be 1 byte if it is in 216 * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in 217 * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B). 218 * @param result result of delivering the message. 219 */ acknowledgeSms(int token, @IntRange(from = 0, to = 65535) int messageRef, @DeliverStatusResult int result)220 public void acknowledgeSms(int token, @IntRange(from = 0, to = 65535) int messageRef, 221 @DeliverStatusResult int result) { 222 Log.e(LOG_TAG, "acknowledgeSms() not implemented."); 223 } 224 225 /** 226 * This method will be called by the platform after 227 * {@link #onSmsReceived(int, String, byte[])} has been called to acknowledge an incoming SMS. 228 * 229 * This method is only called in cases where the framework needs to provide the PDU such as the 230 * case where we provide the Short Message Transfer Layer PDU (see 3GPP TS 23.040). Otherwise, 231 * {@link #acknowledgeSms(int, int, int)} will be used. 232 * 233 * @param token token provided in {@link #onSmsReceived(int, String, byte[])} 234 * @param messageRef the message reference, which may be 1 byte if it is in 235 * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in 236 * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B). 237 * @param result result of delivering the message. 238 * @param pdu PDU representing the contents of the message. 239 */ acknowledgeSms(int token, @IntRange(from = 0, to = 65535) int messageRef, @DeliverStatusResult int result, @NonNull byte[] pdu)240 public void acknowledgeSms(int token, @IntRange(from = 0, to = 65535) int messageRef, 241 @DeliverStatusResult int result, @NonNull byte[] pdu) { 242 Log.e(LOG_TAG, "acknowledgeSms() not implemented. acknowledgeSms(int, int, int) called."); 243 acknowledgeSms(token, messageRef, result); 244 } 245 246 /** 247 * This method will be triggered by the platform after 248 * {@link #onSmsStatusReportReceived(int, int, String, byte[])} or 249 * {@link #onSmsStatusReportReceived(int, String, byte[])} has been called to provide the 250 * result to the IMS provider. 251 * 252 * @param token token provided in {@link #onSmsStatusReportReceived(int, int, String, byte[])} 253 * or {@link #onSmsStatusReportReceived(int, String, byte[])} 254 * @param messageRef the message reference, which may be 1 byte if it is in 255 * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in 256 * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B). 257 * @param result result of delivering the message. 258 */ acknowledgeSmsReport(int token, @IntRange(from = 0, to = 65535) int messageRef, @StatusReportResult int result)259 public void acknowledgeSmsReport(int token, @IntRange(from = 0, to = 65535) int messageRef, 260 @StatusReportResult int result) { 261 Log.e(LOG_TAG, "acknowledgeSmsReport() not implemented."); 262 } 263 264 /** 265 * This method should be triggered by the IMS providers when there is an incoming message. The 266 * platform will deliver the message to the messages database and notify the IMS provider of the 267 * result by calling {@link #acknowledgeSms(int, int, int)}. 268 * 269 * This method must not be called before {@link #onReady()} is called or the call will fail. If 270 * the platform is not available, {@link #acknowledgeSms(int, int, int)} will be called with the 271 * {@link #DELIVER_STATUS_ERROR_GENERIC} result code. 272 * @param token unique token generated by IMS providers that the platform will use to trigger 273 * callbacks for this message. 274 * @param format the format of the message. 275 * @param pdu PDU representing the contents of the message. 276 * @throws RuntimeException if called before {@link #onReady()} is triggered. 277 */ onSmsReceived(int token, @SmsMessage.Format String format, byte[] pdu)278 public final void onSmsReceived(int token, @SmsMessage.Format String format, byte[] pdu) 279 throws RuntimeException { 280 IImsSmsListener listener = null; 281 synchronized (mLock) { 282 listener = mListener; 283 } 284 285 if (listener == null) { 286 throw new RuntimeException("Feature not ready."); 287 } 288 try { 289 listener.onSmsReceived(token, format, pdu); 290 } catch (RemoteException e) { 291 Log.e(LOG_TAG, "Can not deliver sms: " + e.getMessage()); 292 SmsMessage message = SmsMessage.createFromPdu(pdu, format); 293 if (message != null && message.mWrappedSmsMessage != null) { 294 acknowledgeSms(token, message.mWrappedSmsMessage.mMessageRef, 295 DELIVER_STATUS_ERROR_GENERIC); 296 } else { 297 Log.w(LOG_TAG, "onSmsReceived: Invalid pdu entered."); 298 acknowledgeSms(token, 0, DELIVER_STATUS_ERROR_GENERIC); 299 } 300 } 301 } 302 303 /** 304 * This method should be triggered by the IMS providers when an outgoing SMS message has been 305 * sent successfully. 306 * 307 * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])} 308 * @param messageRef the message reference, which may be 1 byte if it is in 309 * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in 310 * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B). 311 * 312 * @throws RuntimeException if called before {@link #onReady()} is triggered or if the 313 * connection to the framework is not available. If this happens attempting to send the SMS 314 * should be aborted. 315 */ onSendSmsResultSuccess(int token, @IntRange(from = 0, to = 65535) int messageRef)316 public final void onSendSmsResultSuccess(int token, 317 @IntRange(from = 0, to = 65535) int messageRef) throws RuntimeException { 318 IImsSmsListener listener = null; 319 synchronized (mLock) { 320 listener = mListener; 321 } 322 323 if (listener == null) { 324 throw new RuntimeException("Feature not ready."); 325 } 326 try { 327 listener.onSendSmsResult(token, messageRef, SEND_STATUS_OK, 328 SmsManager.RESULT_ERROR_NONE, RESULT_NO_NETWORK_ERROR); 329 } catch (RemoteException e) { 330 e.rethrowFromSystemServer(); 331 } 332 } 333 334 /** 335 * This method should be triggered by the IMS providers to pass the result of the sent message 336 * to the platform. 337 * 338 * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])} 339 * @param messageRef the message reference, which may be 1 byte if it is in 340 * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in 341 * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B). 342 * @param status result of sending the SMS. 343 * @param reason reason in case status is failure. 344 * 345 * @throws RuntimeException if called before {@link #onReady()} is triggered or if the 346 * connection to the framework is not available. If this happens attempting to send the SMS 347 * should be aborted. 348 * @deprecated Use {@link #onSendSmsResultSuccess(int, int)} or 349 * {@link #onSendSmsResultError(int, int, int, int, int)} to notify the framework of the SMS 350 * send result. 351 */ 352 @Deprecated onSendSmsResult(int token, @IntRange(from = 0, to = 65535) int messageRef, @SendStatusResult int status, @SmsManager.Result int reason)353 public final void onSendSmsResult(int token, @IntRange(from = 0, to = 65535) int messageRef, 354 @SendStatusResult int status, @SmsManager.Result int reason) throws RuntimeException { 355 IImsSmsListener listener = null; 356 synchronized (mLock) { 357 listener = mListener; 358 } 359 360 if (listener == null) { 361 throw new RuntimeException("Feature not ready."); 362 } 363 try { 364 listener.onSendSmsResult(token, messageRef, status, reason, 365 RESULT_NO_NETWORK_ERROR); 366 } catch (RemoteException e) { 367 e.rethrowFromSystemServer(); 368 } 369 } 370 371 /** 372 * This method should be triggered by the IMS providers when an outgoing message fails to be 373 * sent due to an error generated while processing the message or after being sent to the 374 * network. 375 * 376 * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])} 377 * @param messageRef the message reference, which may be 1 byte if it is in 378 * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in 379 * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B). 380 * @param status result of sending the SMS. 381 * @param networkErrorCode the error code reported by the carrier network if sending this SMS 382 * has resulted in an error or {@link #RESULT_NO_NETWORK_ERROR} if no network error was 383 * generated. See 3GPP TS 24.011 Section 7.3.4 for valid error codes and more information. 384 * 385 * @throws RuntimeException if called before {@link #onReady()} is triggered or if the 386 * connection to the framework is not available. If this happens attempting to send the SMS 387 * should be aborted. 388 */ onSendSmsResultError(int token, @IntRange(from = 0, to = 65535) int messageRef, @SendStatusResult int status, @SmsManager.Result int reason, int networkErrorCode)389 public final void onSendSmsResultError(int token, 390 @IntRange(from = 0, to = 65535) int messageRef, @SendStatusResult int status, 391 @SmsManager.Result int reason, int networkErrorCode) throws RuntimeException { 392 IImsSmsListener listener = null; 393 synchronized (mLock) { 394 listener = mListener; 395 } 396 397 if (listener == null) { 398 throw new RuntimeException("Feature not ready."); 399 } 400 try { 401 listener.onSendSmsResult(token, messageRef, status, reason, networkErrorCode); 402 } catch (RemoteException e) { 403 e.rethrowFromSystemServer(); 404 } 405 } 406 407 /** 408 * This API is used to report the result of sending 409 * RP-SMMA to framework based on received network responses(RP-ACK, 410 * RP-ERROR or SIP error). 411 * 412 * @param token provided in {@link #onMemoryAvailable()}. 413 * @param result based on RP-ACK or RP_ERROR 414 * @param networkErrorCode the error code reported by the carrier 415 * network if sending this SMS has resulted in an error or 416 * {@link #RESULT_NO_NETWORK_ERROR} if no network error was generated. See 417 * 3GPP TS 24.011 Section 7.3.4 for valid error codes and more 418 * information. 419 * 420 * @hide 421 */ onMemoryAvailableResult(int token, @SendStatusResult int result, int networkErrorCode)422 public final void onMemoryAvailableResult(int token, @SendStatusResult int result, 423 int networkErrorCode) throws RuntimeException { 424 IImsSmsListener listener = null; 425 synchronized (mLock) { 426 listener = mListener; 427 } 428 429 if (listener == null) { 430 throw new RuntimeException("Feature not ready."); 431 } 432 try { 433 listener.onMemoryAvailableResult(token, result, networkErrorCode); 434 } catch (RemoteException e) { 435 e.rethrowFromSystemServer(); 436 } 437 } 438 439 /** 440 * This method should be triggered by the IMS providers when the status report of the sent 441 * message is received. The platform will handle the report and notify the IMS provider of the 442 * result by calling {@link #acknowledgeSmsReport(int, int, int)}. 443 * 444 * This method must not be called before {@link #onReady()} is called or the call will fail. If 445 * the platform is not available, {@link #acknowledgeSmsReport(int, int, int)} will be called 446 * with the {@link #STATUS_REPORT_STATUS_ERROR} result code. 447 * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])} 448 * @param messageRef the message reference, which may be 1 byte if it is in 449 * {@link SmsMessage#FORMAT_3GPP} format or 2 bytes if it is in 450 * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B). 451 * @param format the format of the message. 452 * @param pdu PDU representing the content of the status report. 453 * @throws RuntimeException if called before {@link #onReady()} is triggered 454 * 455 * @deprecated Use {@link #onSmsStatusReportReceived(int, String, byte[])} instead without the 456 * message reference. 457 */ 458 @Deprecated onSmsStatusReportReceived(int token, @IntRange(from = 0, to = 65535) int messageRef, @SmsMessage.Format String format, byte[] pdu)459 public final void onSmsStatusReportReceived(int token, 460 @IntRange(from = 0, to = 65535) int messageRef, @SmsMessage.Format String format, 461 byte[] pdu) throws RuntimeException { 462 IImsSmsListener listener = null; 463 synchronized (mLock) { 464 listener = mListener; 465 } 466 467 if (listener == null) { 468 throw new RuntimeException("Feature not ready."); 469 } 470 try { 471 listener.onSmsStatusReportReceived(token, format, pdu); 472 } catch (RemoteException e) { 473 Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage()); 474 acknowledgeSmsReport(token, messageRef, STATUS_REPORT_STATUS_ERROR); 475 } 476 } 477 478 /** 479 * This method should be triggered by the IMS providers when the status report of the sent 480 * message is received. The platform will handle the report and notify the IMS provider of the 481 * result by calling {@link #acknowledgeSmsReport(int, int, int)}. 482 * 483 * This method must not be called before {@link #onReady()} is called or the call will fail. If 484 * the platform is not available, {@link #acknowledgeSmsReport(int, int, int)} will be called 485 * with the {@link #STATUS_REPORT_STATUS_ERROR} result code. 486 * @param token unique token generated by IMS providers that the platform will use to trigger 487 * callbacks for this message. 488 * @param format the format of the message. 489 * @param pdu PDU representing the content of the status report. 490 * @throws RuntimeException if called before {@link #onReady()} is triggered 491 */ onSmsStatusReportReceived(int token, @SmsMessage.Format String format, byte[] pdu)492 public final void onSmsStatusReportReceived(int token, @SmsMessage.Format String format, 493 byte[] pdu) throws RuntimeException { 494 IImsSmsListener listener = null; 495 synchronized (mLock) { 496 listener = mListener; 497 } 498 499 if (listener == null) { 500 throw new RuntimeException("Feature not ready."); 501 } 502 try { 503 listener.onSmsStatusReportReceived(token, format, pdu); 504 } catch (RemoteException e) { 505 Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage()); 506 SmsMessage message = SmsMessage.createFromPdu(pdu, format); 507 if (message != null && message.mWrappedSmsMessage != null) { 508 acknowledgeSmsReport( 509 token, 510 message.mWrappedSmsMessage.mMessageRef, 511 STATUS_REPORT_STATUS_ERROR); 512 } else { 513 Log.w(LOG_TAG, "onSmsStatusReportReceived: Invalid pdu entered."); 514 acknowledgeSmsReport(token, 0, STATUS_REPORT_STATUS_ERROR); 515 } 516 } 517 } 518 519 /** 520 * Returns the SMS format that the ImsService expects. 521 * 522 * @return The expected format of the SMS messages. 523 */ getSmsFormat()524 public @SmsMessage.Format String getSmsFormat() { 525 return SmsMessage.FORMAT_3GPP; 526 } 527 528 /** 529 * Called when ImsSmsImpl has been initialized and communication with the framework is set up. 530 * Any attempt by this class to access the framework before this method is called will return 531 * with a {@link RuntimeException}. 532 */ onReady()533 public void onReady() { 534 // Base Implementation - Should be overridden 535 } 536 537 /** 538 * Set default Executor for ImsSmsImplBase. 539 * 540 * @param executor The default executor for the framework to use when executing the methods 541 * overridden by the implementation of ImsSms. 542 * @hide 543 */ setDefaultExecutor(@onNull Executor executor)544 public final void setDefaultExecutor(@NonNull Executor executor) { 545 if (mExecutor == null) { 546 mExecutor = executor; 547 } 548 } 549 550 /** 551 * Get Executor from ImsSmsImplBase. 552 * If there is no settings for the executor, all ImsSmsImplBase method calls will use 553 * Runnable::run as default 554 * 555 * @return an Executor used to execute methods in ImsSms called remotely by the framework. 556 * @hide 557 */ getExecutor()558 public @NonNull Executor getExecutor() { 559 return mExecutor != null ? mExecutor : Runnable::run; 560 } 561 } 562