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.annotation.TestApi; 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 32 /** 33 * Base implementation for SMS over IMS. 34 * 35 * Any service wishing to provide SMS over IMS should extend this class and implement all methods 36 * that the service supports. 37 * 38 * @hide 39 */ 40 @SystemApi 41 @TestApi 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 134 /** 135 * Registers a listener responsible for handling tasks like delivering messages. 136 * 137 * @param listener listener to register. 138 * 139 * @hide 140 */ registerSmsListener(IImsSmsListener listener)141 public final void registerSmsListener(IImsSmsListener listener) { 142 synchronized (mLock) { 143 mListener = listener; 144 } 145 } 146 147 /** 148 * This method will be triggered by the platform when the user attempts to send an SMS. This 149 * method should be implemented by the IMS providers to provide implementation of sending an SMS 150 * over IMS. 151 * 152 * @param token unique token generated by the platform that should be used when triggering 153 * callbacks for this specific message. 154 * @param messageRef the message reference, which may be 1 byte if it is in 155 * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in 156 * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B). 157 * @param format the format of the message. 158 * @param smsc the Short Message Service Center address. 159 * @param isRetry whether it is a retry of an already attempted message or not. 160 * @param pdu PDU representing the contents of the message. 161 */ sendSms(int token, @IntRange(from = 0, to = 65535) int messageRef, @SmsMessage.Format String format, String smsc, boolean isRetry, byte[] pdu)162 public void sendSms(int token, @IntRange(from = 0, to = 65535) int messageRef, 163 @SmsMessage.Format String format, String smsc, boolean isRetry, 164 byte[] pdu) { 165 // Base implementation returns error. Should be overridden. 166 try { 167 onSendSmsResult(token, messageRef, SEND_STATUS_ERROR, 168 SmsManager.RESULT_ERROR_GENERIC_FAILURE); 169 } catch (RuntimeException e) { 170 Log.e(LOG_TAG, "Can not send sms: " + e.getMessage()); 171 } 172 } 173 174 /** 175 * This method will be triggered by the platform after 176 * {@link #onSmsReceived(int, String, byte[])} has been called to deliver the result to the IMS 177 * provider. 178 * 179 * @param token token provided in {@link #onSmsReceived(int, String, byte[])} 180 * @param messageRef the message reference, which may be 1 byte if it is in 181 * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in 182 * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B). 183 * @param result result of delivering the message. 184 */ acknowledgeSms(int token, @IntRange(from = 0, to = 65535) int messageRef, @DeliverStatusResult int result)185 public void acknowledgeSms(int token, @IntRange(from = 0, to = 65535) int messageRef, 186 @DeliverStatusResult int result) { 187 Log.e(LOG_TAG, "acknowledgeSms() not implemented."); 188 } 189 190 /** 191 * This method will be triggered by the platform after 192 * {@link #onSmsStatusReportReceived(int, int, String, byte[])} or 193 * {@link #onSmsStatusReportReceived(int, String, byte[])} has been called to provide the 194 * result to the IMS provider. 195 * 196 * @param token token provided in {@link #onSmsStatusReportReceived(int, int, String, byte[])} 197 * or {@link #onSmsStatusReportReceived(int, String, byte[])} 198 * @param messageRef the message reference, which may be 1 byte if it is in 199 * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in 200 * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B). 201 * @param result result of delivering the message. 202 */ acknowledgeSmsReport(int token, @IntRange(from = 0, to = 65535) int messageRef, @StatusReportResult int result)203 public void acknowledgeSmsReport(int token, @IntRange(from = 0, to = 65535) int messageRef, 204 @StatusReportResult int result) { 205 Log.e(LOG_TAG, "acknowledgeSmsReport() not implemented."); 206 } 207 208 /** 209 * This method should be triggered by the IMS providers when there is an incoming message. The 210 * platform will deliver the message to the messages database and notify the IMS provider of the 211 * result by calling {@link #acknowledgeSms(int, int, int)}. 212 * 213 * This method must not be called before {@link #onReady()} is called or the call will fail. If 214 * the platform is not available, {@link #acknowledgeSms(int, int, int)} will be called with the 215 * {@link #DELIVER_STATUS_ERROR_GENERIC} result code. 216 * @param token unique token generated by IMS providers that the platform will use to trigger 217 * callbacks for this message. 218 * @param format the format of the message. 219 * @param pdu PDU representing the contents of the message. 220 * @throws RuntimeException if called before {@link #onReady()} is triggered. 221 */ onSmsReceived(int token, @SmsMessage.Format String format, byte[] pdu)222 public final void onSmsReceived(int token, @SmsMessage.Format String format, byte[] pdu) 223 throws RuntimeException { 224 synchronized (mLock) { 225 if (mListener == null) { 226 throw new RuntimeException("Feature not ready."); 227 } 228 try { 229 mListener.onSmsReceived(token, format, pdu); 230 } catch (RemoteException e) { 231 Log.e(LOG_TAG, "Can not deliver sms: " + e.getMessage()); 232 SmsMessage message = SmsMessage.createFromPdu(pdu, format); 233 if (message != null && message.mWrappedSmsMessage != null) { 234 acknowledgeSms(token, message.mWrappedSmsMessage.mMessageRef, 235 DELIVER_STATUS_ERROR_GENERIC); 236 } else { 237 Log.w(LOG_TAG, "onSmsReceived: Invalid pdu entered."); 238 acknowledgeSms(token, 0, DELIVER_STATUS_ERROR_GENERIC); 239 } 240 } 241 } 242 } 243 244 /** 245 * This method should be triggered by the IMS providers when an outgoing SMS message has been 246 * sent successfully. 247 * 248 * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])} 249 * @param messageRef the message reference, which may be 1 byte if it is in 250 * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in 251 * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B). 252 * 253 * @throws RuntimeException if called before {@link #onReady()} is triggered or if the 254 * connection to the framework is not available. If this happens attempting to send the SMS 255 * should be aborted. 256 */ onSendSmsResultSuccess(int token, @IntRange(from = 0, to = 65535) int messageRef)257 public final void onSendSmsResultSuccess(int token, 258 @IntRange(from = 0, to = 65535) int messageRef) throws RuntimeException { 259 synchronized (mLock) { 260 if (mListener == null) { 261 throw new RuntimeException("Feature not ready."); 262 } 263 try { 264 mListener.onSendSmsResult(token, messageRef, SEND_STATUS_OK, 265 SmsManager.RESULT_ERROR_NONE, RESULT_NO_NETWORK_ERROR); 266 } catch (RemoteException e) { 267 e.rethrowFromSystemServer(); 268 } 269 } 270 } 271 272 /** 273 * This method should be triggered by the IMS providers to pass the result of the sent message 274 * to the platform. 275 * 276 * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])} 277 * @param messageRef the message reference, which may be 1 byte if it is in 278 * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in 279 * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B). 280 * @param status result of sending the SMS. 281 * @param reason reason in case status is failure. 282 * 283 * @throws RuntimeException if called before {@link #onReady()} is triggered or if the 284 * connection to the framework is not available. If this happens attempting to send the SMS 285 * should be aborted. 286 * @deprecated Use {@link #onSendSmsResultSuccess(int, int)} or 287 * {@link #onSendSmsResultError(int, int, int, int, int)} to notify the framework of the SMS 288 * send result. 289 */ 290 @Deprecated onSendSmsResult(int token, @IntRange(from = 0, to = 65535) int messageRef, @SendStatusResult int status, @SmsManager.Result int reason)291 public final void onSendSmsResult(int token, @IntRange(from = 0, to = 65535) int messageRef, 292 @SendStatusResult int status, @SmsManager.Result int reason) throws RuntimeException { 293 synchronized (mLock) { 294 if (mListener == null) { 295 throw new RuntimeException("Feature not ready."); 296 } 297 try { 298 mListener.onSendSmsResult(token, messageRef, status, reason, 299 RESULT_NO_NETWORK_ERROR); 300 } catch (RemoteException e) { 301 e.rethrowFromSystemServer(); 302 } 303 } 304 } 305 306 /** 307 * This method should be triggered by the IMS providers when an outgoing message fails to be 308 * sent due to an error generated while processing the message or after being sent to the 309 * network. 310 * 311 * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])} 312 * @param messageRef the message reference, which may be 1 byte if it is in 313 * {@link SmsMessage#FORMAT_3GPP} format (see TS.123.040) or 2 bytes if it is in 314 * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B). 315 * @param status result of sending the SMS. 316 * @param networkErrorCode the error code reported by the carrier network if sending this SMS 317 * has resulted in an error or {@link #RESULT_NO_NETWORK_ERROR} if no network error was 318 * generated. See 3GPP TS 24.011 Section 7.3.4 for valid error codes and more information. 319 * 320 * @throws RuntimeException if called before {@link #onReady()} is triggered or if the 321 * connection to the framework is not available. If this happens attempting to send the SMS 322 * should be aborted. 323 */ onSendSmsResultError(int token, @IntRange(from = 0, to = 65535) int messageRef, @SendStatusResult int status, @SmsManager.Result int reason, int networkErrorCode)324 public final void onSendSmsResultError(int token, 325 @IntRange(from = 0, to = 65535) int messageRef, @SendStatusResult int status, 326 @SmsManager.Result int reason, int networkErrorCode) throws RuntimeException { 327 synchronized (mLock) { 328 if (mListener == null) { 329 throw new RuntimeException("Feature not ready."); 330 } 331 try { 332 mListener.onSendSmsResult(token, messageRef, status, reason, networkErrorCode); 333 } catch (RemoteException e) { 334 e.rethrowFromSystemServer(); 335 } 336 } 337 } 338 339 /** 340 * This method should be triggered by the IMS providers when the status report of the sent 341 * message is received. The platform will handle the report and notify the IMS provider of the 342 * result by calling {@link #acknowledgeSmsReport(int, int, int)}. 343 * 344 * This method must not be called before {@link #onReady()} is called or the call will fail. If 345 * the platform is not available, {@link #acknowledgeSmsReport(int, int, int)} will be called 346 * with the {@link #STATUS_REPORT_STATUS_ERROR} result code. 347 * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])} 348 * @param messageRef the message reference, which may be 1 byte if it is in 349 * {@link SmsMessage#FORMAT_3GPP} format or 2 bytes if it is in 350 * {@link SmsMessage#FORMAT_3GPP2} format (see 3GPP2 C.S0015-B). 351 * @param format the format of the message. 352 * @param pdu PDU representing the content of the status report. 353 * @throws RuntimeException if called before {@link #onReady()} is triggered 354 * 355 * @deprecated Use {@link #onSmsStatusReportReceived(int, String, byte[])} instead without the 356 * message reference. 357 */ 358 @Deprecated onSmsStatusReportReceived(int token, @IntRange(from = 0, to = 65535) int messageRef, @SmsMessage.Format String format, byte[] pdu)359 public final void onSmsStatusReportReceived(int token, 360 @IntRange(from = 0, to = 65535) int messageRef, @SmsMessage.Format String format, 361 byte[] pdu) throws RuntimeException { 362 synchronized (mLock) { 363 if (mListener == null) { 364 throw new RuntimeException("Feature not ready."); 365 } 366 try { 367 mListener.onSmsStatusReportReceived(token, format, pdu); 368 } catch (RemoteException e) { 369 Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage()); 370 acknowledgeSmsReport(token, messageRef, STATUS_REPORT_STATUS_ERROR); 371 } 372 } 373 } 374 375 /** 376 * This method should be triggered by the IMS providers when the status report of the sent 377 * message is received. The platform will handle the report and notify the IMS provider of the 378 * result by calling {@link #acknowledgeSmsReport(int, int, int)}. 379 * 380 * This method must not be called before {@link #onReady()} is called or the call will fail. If 381 * the platform is not available, {@link #acknowledgeSmsReport(int, int, int)} will be called 382 * with the {@link #STATUS_REPORT_STATUS_ERROR} result code. 383 * @param token unique token generated by IMS providers that the platform will use to trigger 384 * callbacks for this message. 385 * @param format the format of the message. 386 * @param pdu PDU representing the content of the status report. 387 * @throws RuntimeException if called before {@link #onReady()} is triggered 388 */ onSmsStatusReportReceived(int token, @SmsMessage.Format String format, byte[] pdu)389 public final void onSmsStatusReportReceived(int token, @SmsMessage.Format String format, 390 byte[] pdu) throws RuntimeException { 391 synchronized (mLock) { 392 if (mListener == null) { 393 throw new RuntimeException("Feature not ready."); 394 } 395 try { 396 mListener.onSmsStatusReportReceived(token, format, pdu); 397 } catch (RemoteException e) { 398 Log.e(LOG_TAG, "Can not process sms status report: " + e.getMessage()); 399 SmsMessage message = SmsMessage.createFromPdu(pdu, format); 400 if (message != null && message.mWrappedSmsMessage != null) { 401 acknowledgeSmsReport( 402 token, 403 message.mWrappedSmsMessage.mMessageRef, 404 STATUS_REPORT_STATUS_ERROR); 405 } else { 406 Log.w(LOG_TAG, "onSmsStatusReportReceived: Invalid pdu entered."); 407 acknowledgeSmsReport(token, 0, STATUS_REPORT_STATUS_ERROR); 408 } 409 } 410 } 411 } 412 413 /** 414 * Returns the SMS format that the ImsService expects. 415 * 416 * @return The expected format of the SMS messages. 417 */ getSmsFormat()418 public @SmsMessage.Format String getSmsFormat() { 419 return SmsMessage.FORMAT_3GPP; 420 } 421 422 /** 423 * Called when ImsSmsImpl has been initialized and communication with the framework is set up. 424 * Any attempt by this class to access the framework before this method is called will return 425 * with a {@link RuntimeException}. 426 */ onReady()427 public void onReady() { 428 // Base Implementation - Should be overridden 429 } 430 } 431