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