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