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