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