1 /* 2 * Copyright (C) 2008 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; 18 19 import android.app.ActivityThread; 20 import android.app.PendingIntent; 21 import android.os.RemoteException; 22 import android.os.ServiceManager; 23 import android.text.TextUtils; 24 25 import com.android.internal.telephony.ISms; 26 import com.android.internal.telephony.SmsRawData; 27 import com.android.internal.telephony.uicc.IccConstants; 28 29 import java.util.ArrayList; 30 import java.util.Arrays; 31 import java.util.List; 32 33 /* 34 * TODO(code review): Curious question... Why are a lot of these 35 * methods not declared as static, since they do not seem to require 36 * any local object state? Presumably this cannot be changed without 37 * interfering with the API... 38 */ 39 40 /** 41 * Manages SMS operations such as sending data, text, and pdu SMS messages. 42 * Get this object by calling the static method {@link #getDefault()}. 43 * 44 * <p>For information about how to behave as the default SMS app on Android 4.4 (API level 19) 45 * and higher, see {@link android.provider.Telephony}. 46 */ 47 public final class SmsManager { 48 /** Singleton object constructed during class initialization. */ 49 private static final SmsManager sInstance = new SmsManager(); 50 51 /** 52 * Send a text based SMS. 53 * 54 * <p class="note"><strong>Note:</strong> Using this method requires that your app has the 55 * {@link android.Manifest.permission#SEND_SMS} permission.</p> 56 * 57 * <p class="note"><strong>Note:</strong> Beginning with Android 4.4 (API level 19), if 58 * <em>and only if</em> an app is not selected as the default SMS app, the system automatically 59 * writes messages sent using this method to the SMS Provider (the default SMS app is always 60 * responsible for writing its sent messages to the SMS Provider). For information about 61 * how to behave as the default SMS app, see {@link android.provider.Telephony}.</p> 62 * 63 * 64 * @param destinationAddress the address to send the message to 65 * @param scAddress is the service center address or null to use 66 * the current default SMSC 67 * @param text the body of the message to send 68 * @param sentIntent if not NULL this <code>PendingIntent</code> is 69 * broadcast when the message is successfully sent, or failed. 70 * The result code will be <code>Activity.RESULT_OK</code> for success, 71 * or one of these errors:<br> 72 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 73 * <code>RESULT_ERROR_RADIO_OFF</code><br> 74 * <code>RESULT_ERROR_NULL_PDU</code><br> 75 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include 76 * the extra "errorCode" containing a radio technology specific value, 77 * generally only useful for troubleshooting.<br> 78 * The per-application based SMS control checks sentIntent. If sentIntent 79 * is NULL the caller will be checked against all unknown applications, 80 * which cause smaller number of SMS to be sent in checking period. 81 * @param deliveryIntent if not NULL this <code>PendingIntent</code> is 82 * broadcast when the message is delivered to the recipient. The 83 * raw pdu of the status report is in the extended data ("pdu"). 84 * 85 * @throws IllegalArgumentException if destinationAddress or text are empty 86 */ 87 public void sendTextMessage( 88 String destinationAddress, String scAddress, String text, 89 PendingIntent sentIntent, PendingIntent deliveryIntent) { 90 if (TextUtils.isEmpty(destinationAddress)) { 91 throw new IllegalArgumentException("Invalid destinationAddress"); 92 } 93 94 if (TextUtils.isEmpty(text)) { 95 throw new IllegalArgumentException("Invalid message body"); 96 } 97 98 try { 99 ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); 100 if (iccISms != null) { 101 iccISms.sendText(ActivityThread.currentPackageName(), destinationAddress, 102 scAddress, text, sentIntent, deliveryIntent); 103 } 104 } catch (RemoteException ex) { 105 // ignore it 106 } 107 } 108 109 /** 110 * Divide a message text into several fragments, none bigger than 111 * the maximum SMS message size. 112 * 113 * @param text the original message. Must not be null. 114 * @return an <code>ArrayList</code> of strings that, in order, 115 * comprise the original message 116 * 117 * @throws IllegalArgumentException if text is null 118 */ 119 public ArrayList<String> divideMessage(String text) { 120 if (null == text) { 121 throw new IllegalArgumentException("text is null"); 122 } 123 return SmsMessage.fragmentText(text); 124 } 125 126 /** 127 * Send a multi-part text based SMS. The callee should have already 128 * divided the message into correctly sized parts by calling 129 * <code>divideMessage</code>. 130 * 131 * <p class="note"><strong>Note:</strong> Using this method requires that your app has the 132 * {@link android.Manifest.permission#SEND_SMS} permission.</p> 133 * 134 * <p class="note"><strong>Note:</strong> Beginning with Android 4.4 (API level 19), if 135 * <em>and only if</em> an app is not selected as the default SMS app, the system automatically 136 * writes messages sent using this method to the SMS Provider (the default SMS app is always 137 * responsible for writing its sent messages to the SMS Provider). For information about 138 * how to behave as the default SMS app, see {@link android.provider.Telephony}.</p> 139 * 140 * @param destinationAddress the address to send the message to 141 * @param scAddress is the service center address or null to use 142 * the current default SMSC 143 * @param parts an <code>ArrayList</code> of strings that, in order, 144 * comprise the original message 145 * @param sentIntents if not null, an <code>ArrayList</code> of 146 * <code>PendingIntent</code>s (one for each message part) that is 147 * broadcast when the corresponding message part has been sent. 148 * The result code will be <code>Activity.RESULT_OK</code> for success, 149 * or one of these errors:<br> 150 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 151 * <code>RESULT_ERROR_RADIO_OFF</code><br> 152 * <code>RESULT_ERROR_NULL_PDU</code><br> 153 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include 154 * the extra "errorCode" containing a radio technology specific value, 155 * generally only useful for troubleshooting.<br> 156 * The per-application based SMS control checks sentIntent. If sentIntent 157 * is NULL the caller will be checked against all unknown applications, 158 * which cause smaller number of SMS to be sent in checking period. 159 * @param deliveryIntents if not null, an <code>ArrayList</code> of 160 * <code>PendingIntent</code>s (one for each message part) that is 161 * broadcast when the corresponding message part has been delivered 162 * to the recipient. The raw pdu of the status report is in the 163 * extended data ("pdu"). 164 * 165 * @throws IllegalArgumentException if destinationAddress or data are empty 166 */ 167 public void sendMultipartTextMessage( 168 String destinationAddress, String scAddress, ArrayList<String> parts, 169 ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) { 170 if (TextUtils.isEmpty(destinationAddress)) { 171 throw new IllegalArgumentException("Invalid destinationAddress"); 172 } 173 if (parts == null || parts.size() < 1) { 174 throw new IllegalArgumentException("Invalid message body"); 175 } 176 177 if (parts.size() > 1) { 178 try { 179 ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); 180 if (iccISms != null) { 181 iccISms.sendMultipartText(ActivityThread.currentPackageName(), 182 destinationAddress, scAddress, parts, 183 sentIntents, deliveryIntents); 184 } 185 } catch (RemoteException ex) { 186 // ignore it 187 } 188 } else { 189 PendingIntent sentIntent = null; 190 PendingIntent deliveryIntent = null; 191 if (sentIntents != null && sentIntents.size() > 0) { 192 sentIntent = sentIntents.get(0); 193 } 194 if (deliveryIntents != null && deliveryIntents.size() > 0) { 195 deliveryIntent = deliveryIntents.get(0); 196 } 197 sendTextMessage(destinationAddress, scAddress, parts.get(0), 198 sentIntent, deliveryIntent); 199 } 200 } 201 202 /** 203 * Send a data based SMS to a specific application port. 204 * 205 * <p class="note"><strong>Note:</strong> Using this method requires that your app has the 206 * {@link android.Manifest.permission#SEND_SMS} permission.</p> 207 * 208 * @param destinationAddress the address to send the message to 209 * @param scAddress is the service center address or null to use 210 * the current default SMSC 211 * @param destinationPort the port to deliver the message to 212 * @param data the body of the message to send 213 * @param sentIntent if not NULL this <code>PendingIntent</code> is 214 * broadcast when the message is successfully sent, or failed. 215 * The result code will be <code>Activity.RESULT_OK</code> for success, 216 * or one of these errors:<br> 217 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 218 * <code>RESULT_ERROR_RADIO_OFF</code><br> 219 * <code>RESULT_ERROR_NULL_PDU</code><br> 220 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include 221 * the extra "errorCode" containing a radio technology specific value, 222 * generally only useful for troubleshooting.<br> 223 * The per-application based SMS control checks sentIntent. If sentIntent 224 * is NULL the caller will be checked against all unknown applications, 225 * which cause smaller number of SMS to be sent in checking period. 226 * @param deliveryIntent if not NULL this <code>PendingIntent</code> is 227 * broadcast when the message is delivered to the recipient. The 228 * raw pdu of the status report is in the extended data ("pdu"). 229 * 230 * @throws IllegalArgumentException if destinationAddress or data are empty 231 */ 232 public void sendDataMessage( 233 String destinationAddress, String scAddress, short destinationPort, 234 byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) { 235 if (TextUtils.isEmpty(destinationAddress)) { 236 throw new IllegalArgumentException("Invalid destinationAddress"); 237 } 238 239 if (data == null || data.length == 0) { 240 throw new IllegalArgumentException("Invalid message data"); 241 } 242 243 try { 244 ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); 245 if (iccISms != null) { 246 iccISms.sendData(ActivityThread.currentPackageName(), 247 destinationAddress, scAddress, destinationPort & 0xFFFF, 248 data, sentIntent, deliveryIntent); 249 } 250 } catch (RemoteException ex) { 251 // ignore it 252 } 253 } 254 255 /** 256 * Get the default instance of the SmsManager 257 * 258 * @return the default instance of the SmsManager 259 */ 260 public static SmsManager getDefault() { 261 return sInstance; 262 } 263 264 private SmsManager() { 265 //nothing 266 } 267 268 /** 269 * Copy a raw SMS PDU to the ICC. 270 * ICC (Integrated Circuit Card) is the card of the device. 271 * For example, this can be the SIM or USIM for GSM. 272 * 273 * @param smsc the SMSC for this message, or NULL for the default SMSC 274 * @param pdu the raw PDU to store 275 * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD, 276 * STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT) 277 * @return true for success 278 * 279 * @throws IllegalArgumentException if pdu is NULL 280 * {@hide} 281 */ 282 public boolean copyMessageToIcc(byte[] smsc, byte[] pdu, int status) { 283 boolean success = false; 284 285 if (null == pdu) { 286 throw new IllegalArgumentException("pdu is NULL"); 287 } 288 try { 289 ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); 290 if (iccISms != null) { 291 success = iccISms.copyMessageToIccEf(ActivityThread.currentPackageName(), 292 status, pdu, smsc); 293 } 294 } catch (RemoteException ex) { 295 // ignore it 296 } 297 298 return success; 299 } 300 301 /** 302 * Delete the specified message from the ICC. 303 * ICC (Integrated Circuit Card) is the card of the device. 304 * For example, this can be the SIM or USIM for GSM. 305 * 306 * @param messageIndex is the record index of the message on ICC 307 * @return true for success 308 * 309 * {@hide} 310 */ 311 public boolean 312 deleteMessageFromIcc(int messageIndex) { 313 boolean success = false; 314 byte[] pdu = new byte[IccConstants.SMS_RECORD_LENGTH-1]; 315 Arrays.fill(pdu, (byte)0xff); 316 317 try { 318 ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); 319 if (iccISms != null) { 320 success = iccISms.updateMessageOnIccEf(ActivityThread.currentPackageName(), 321 messageIndex, STATUS_ON_ICC_FREE, pdu); 322 } 323 } catch (RemoteException ex) { 324 // ignore it 325 } 326 327 return success; 328 } 329 330 /** 331 * Update the specified message on the ICC. 332 * ICC (Integrated Circuit Card) is the card of the device. 333 * For example, this can be the SIM or USIM for GSM. 334 * 335 * @param messageIndex record index of message to update 336 * @param newStatus new message status (STATUS_ON_ICC_READ, 337 * STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT, 338 * STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE) 339 * @param pdu the raw PDU to store 340 * @return true for success 341 * 342 * {@hide} 343 */ 344 public boolean updateMessageOnIcc(int messageIndex, int newStatus, byte[] pdu) { 345 boolean success = false; 346 347 try { 348 ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); 349 if (iccISms != null) { 350 success = iccISms.updateMessageOnIccEf(ActivityThread.currentPackageName(), 351 messageIndex, newStatus, pdu); 352 } 353 } catch (RemoteException ex) { 354 // ignore it 355 } 356 357 return success; 358 } 359 360 /** 361 * Retrieves all messages currently stored on ICC. 362 * ICC (Integrated Circuit Card) is the card of the device. 363 * For example, this can be the SIM or USIM for GSM. 364 * 365 * @return <code>ArrayList</code> of <code>SmsMessage</code> objects 366 * 367 * {@hide} 368 */ 369 public static ArrayList<SmsMessage> getAllMessagesFromIcc() { 370 List<SmsRawData> records = null; 371 372 try { 373 ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); 374 if (iccISms != null) { 375 records = iccISms.getAllMessagesFromIccEf(ActivityThread.currentPackageName()); 376 } 377 } catch (RemoteException ex) { 378 // ignore it 379 } 380 381 return createMessageListFromRawRecords(records); 382 } 383 384 /** 385 * Enable reception of cell broadcast (SMS-CB) messages with the given 386 * message identifier. Note that if two different clients enable the same 387 * message identifier, they must both disable it for the device to stop 388 * receiving those messages. All received messages will be broadcast in an 389 * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED". 390 * Note: This call is blocking, callers may want to avoid calling it from 391 * the main thread of an application. 392 * 393 * @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP) 394 * or C.R1001-G (3GPP2) 395 * @return true if successful, false otherwise 396 * @see #disableCellBroadcast(int) 397 * 398 * {@hide} 399 */ 400 public boolean enableCellBroadcast(int messageIdentifier) { 401 boolean success = false; 402 403 try { 404 ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); 405 if (iccISms != null) { 406 success = iccISms.enableCellBroadcast(messageIdentifier); 407 } 408 } catch (RemoteException ex) { 409 // ignore it 410 } 411 412 return success; 413 } 414 415 /** 416 * Disable reception of cell broadcast (SMS-CB) messages with the given 417 * message identifier. Note that if two different clients enable the same 418 * message identifier, they must both disable it for the device to stop 419 * receiving those messages. 420 * Note: This call is blocking, callers may want to avoid calling it from 421 * the main thread of an application. 422 * 423 * @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP) 424 * or C.R1001-G (3GPP2) 425 * @return true if successful, false otherwise 426 * 427 * @see #enableCellBroadcast(int) 428 * 429 * {@hide} 430 */ 431 public boolean disableCellBroadcast(int messageIdentifier) { 432 boolean success = false; 433 434 try { 435 ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); 436 if (iccISms != null) { 437 success = iccISms.disableCellBroadcast(messageIdentifier); 438 } 439 } catch (RemoteException ex) { 440 // ignore it 441 } 442 443 return success; 444 } 445 446 /** 447 * Enable reception of cell broadcast (SMS-CB) messages with the given 448 * message identifier range. Note that if two different clients enable the same 449 * message identifier, they must both disable it for the device to stop 450 * receiving those messages. All received messages will be broadcast in an 451 * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED". 452 * Note: This call is blocking, callers may want to avoid calling it from 453 * the main thread of an application. 454 * 455 * @param startMessageId first message identifier as specified in TS 23.041 (3GPP) 456 * or C.R1001-G (3GPP2) 457 * @param endMessageId last message identifier as specified in TS 23.041 (3GPP) 458 * or C.R1001-G (3GPP2) 459 * @return true if successful, false otherwise 460 * @see #disableCellBroadcastRange(int, int) 461 * 462 * @throws IllegalArgumentException if endMessageId < startMessageId 463 * {@hide} 464 */ 465 public boolean enableCellBroadcastRange(int startMessageId, int endMessageId) { 466 boolean success = false; 467 468 if (endMessageId < startMessageId) { 469 throw new IllegalArgumentException("endMessageId < startMessageId"); 470 } 471 try { 472 ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); 473 if (iccISms != null) { 474 success = iccISms.enableCellBroadcastRange(startMessageId, endMessageId); 475 } 476 } catch (RemoteException ex) { 477 // ignore it 478 } 479 480 return success; 481 } 482 483 /** 484 * Disable reception of cell broadcast (SMS-CB) messages with the given 485 * message identifier range. Note that if two different clients enable the same 486 * message identifier, they must both disable it for the device to stop 487 * receiving those messages. 488 * Note: This call is blocking, callers may want to avoid calling it from 489 * the main thread of an application. 490 * 491 * @param startMessageId first message identifier as specified in TS 23.041 (3GPP) 492 * or C.R1001-G (3GPP2) 493 * @param endMessageId last message identifier as specified in TS 23.041 (3GPP) 494 * or C.R1001-G (3GPP2) 495 * @return true if successful, false otherwise 496 * 497 * @see #enableCellBroadcastRange(int, int) 498 * 499 * @throws IllegalArgumentException if endMessageId < startMessageId 500 * {@hide} 501 */ 502 public boolean disableCellBroadcastRange(int startMessageId, int endMessageId) { 503 boolean success = false; 504 505 if (endMessageId < startMessageId) { 506 throw new IllegalArgumentException("endMessageId < startMessageId"); 507 } 508 try { 509 ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); 510 if (iccISms != null) { 511 success = iccISms.disableCellBroadcastRange(startMessageId, endMessageId); 512 } 513 } catch (RemoteException ex) { 514 // ignore it 515 } 516 517 return success; 518 } 519 520 /** 521 * Create a list of <code>SmsMessage</code>s from a list of RawSmsData 522 * records returned by <code>getAllMessagesFromIcc()</code> 523 * 524 * @param records SMS EF records, returned by 525 * <code>getAllMessagesFromIcc</code> 526 * @return <code>ArrayList</code> of <code>SmsMessage</code> objects. 527 */ 528 private static ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) { 529 ArrayList<SmsMessage> messages = new ArrayList<SmsMessage>(); 530 if (records != null) { 531 int count = records.size(); 532 for (int i = 0; i < count; i++) { 533 SmsRawData data = records.get(i); 534 // List contains all records, including "free" records (null) 535 if (data != null) { 536 SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes()); 537 if (sms != null) { 538 messages.add(sms); 539 } 540 } 541 } 542 } 543 return messages; 544 } 545 546 /** 547 * SMS over IMS is supported if IMS is registered and SMS is supported 548 * on IMS. 549 * 550 * @return true if SMS over IMS is supported, false otherwise 551 * 552 * @see #getImsSmsFormat() 553 * 554 * @hide 555 */ 556 boolean isImsSmsSupported() { 557 boolean boSupported = false; 558 try { 559 ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); 560 if (iccISms != null) { 561 boSupported = iccISms.isImsSmsSupported(); 562 } 563 } catch (RemoteException ex) { 564 // ignore it 565 } 566 return boSupported; 567 } 568 569 /** 570 * Gets SMS format supported on IMS. SMS over IMS format is 571 * either 3GPP or 3GPP2. 572 * 573 * @return SmsMessage.FORMAT_3GPP, 574 * SmsMessage.FORMAT_3GPP2 575 * or SmsMessage.FORMAT_UNKNOWN 576 * 577 * @see #isImsSmsSupported() 578 * 579 * @hide 580 */ 581 String getImsSmsFormat() { 582 String format = com.android.internal.telephony.SmsConstants.FORMAT_UNKNOWN; 583 try { 584 ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); 585 if (iccISms != null) { 586 format = iccISms.getImsSmsFormat(); 587 } 588 } catch (RemoteException ex) { 589 // ignore it 590 } 591 return format; 592 } 593 594 // see SmsMessage.getStatusOnIcc 595 596 /** Free space (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */ 597 static public final int STATUS_ON_ICC_FREE = 0; 598 599 /** Received and read (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */ 600 static public final int STATUS_ON_ICC_READ = 1; 601 602 /** Received and unread (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */ 603 static public final int STATUS_ON_ICC_UNREAD = 3; 604 605 /** Stored and sent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */ 606 static public final int STATUS_ON_ICC_SENT = 5; 607 608 /** Stored and unsent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */ 609 static public final int STATUS_ON_ICC_UNSENT = 7; 610 611 // SMS send failure result codes 612 613 /** Generic failure cause */ 614 static public final int RESULT_ERROR_GENERIC_FAILURE = 1; 615 /** Failed because radio was explicitly turned off */ 616 static public final int RESULT_ERROR_RADIO_OFF = 2; 617 /** Failed because no pdu provided */ 618 static public final int RESULT_ERROR_NULL_PDU = 3; 619 /** Failed because service is currently unavailable */ 620 static public final int RESULT_ERROR_NO_SERVICE = 4; 621 /** Failed because we reached the sending queue limit. {@hide} */ 622 static public final int RESULT_ERROR_LIMIT_EXCEEDED = 5; 623 /** Failed because FDN is enabled. {@hide} */ 624 static public final int RESULT_ERROR_FDN_CHECK_FAILURE = 6; 625 } 626