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.EncodeException; 25 import com.android.internal.telephony.ISms; 26 import com.android.internal.telephony.IccConstants; 27 import com.android.internal.telephony.SmsRawData; 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? Assumedly 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 private static SmsManager sInstance; 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 sucessfully 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 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 applicaitons, 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 */ 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 sucessfully 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 applicaitons, 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 */ 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 */ 226 public static SmsManager getDefault() { 227 if (sInstance == null) { 228 sInstance = new SmsManager(); 229 } 230 return sInstance; 231 } 232 233 private SmsManager() { 234 //nothing 235 } 236 237 /** 238 * Copy a raw SMS PDU to the ICC. 239 * ICC (Integrated Circuit Card) is the card of the device. 240 * For example, this can be the SIM or USIM for GSM. 241 * 242 * @param smsc the SMSC for this message, or NULL for the default SMSC 243 * @param pdu the raw PDU to store 244 * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD, 245 * STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT) 246 * @return true for success 247 * 248 * {@hide} 249 */ 250 public boolean copyMessageToIcc(byte[] smsc, byte[] pdu, int status) { 251 boolean success = false; 252 253 try { 254 ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); 255 if (iccISms != null) { 256 success = iccISms.copyMessageToIccEf(status, pdu, smsc); 257 } 258 } catch (RemoteException ex) { 259 // ignore it 260 } 261 262 return success; 263 } 264 265 /** 266 * Delete the specified message from the ICC. 267 * ICC (Integrated Circuit Card) is the card of the device. 268 * For example, this can be the SIM or USIM for GSM. 269 * 270 * @param messageIndex is the record index of the message on ICC 271 * @return true for success 272 * 273 * {@hide} 274 */ 275 public boolean 276 deleteMessageFromIcc(int messageIndex) { 277 boolean success = false; 278 byte[] pdu = new byte[IccConstants.SMS_RECORD_LENGTH-1]; 279 Arrays.fill(pdu, (byte)0xff); 280 281 try { 282 ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); 283 if (iccISms != null) { 284 success = iccISms.updateMessageOnIccEf(messageIndex, STATUS_ON_ICC_FREE, pdu); 285 } 286 } catch (RemoteException ex) { 287 // ignore it 288 } 289 290 return success; 291 } 292 293 /** 294 * Update the specified message on the ICC. 295 * ICC (Integrated Circuit Card) is the card of the device. 296 * For example, this can be the SIM or USIM for GSM. 297 * 298 * @param messageIndex record index of message to update 299 * @param newStatus new message status (STATUS_ON_ICC_READ, 300 * STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT, 301 * STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE) 302 * @param pdu the raw PDU to store 303 * @return true for success 304 * 305 * {@hide} 306 */ 307 public boolean updateMessageOnIcc(int messageIndex, int newStatus, byte[] pdu) { 308 boolean success = false; 309 310 try { 311 ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); 312 if (iccISms != null) { 313 success = iccISms.updateMessageOnIccEf(messageIndex, newStatus, pdu); 314 } 315 } catch (RemoteException ex) { 316 // ignore it 317 } 318 319 return success; 320 } 321 322 /** 323 * Retrieves all messages currently stored on ICC. 324 * ICC (Integrated Circuit Card) is the card of the device. 325 * For example, this can be the SIM or USIM for GSM. 326 * 327 * @return <code>ArrayList</code> of <code>SmsMessage</code> objects 328 * 329 * {@hide} 330 */ 331 public ArrayList<SmsMessage> getAllMessagesFromIcc() { 332 List<SmsRawData> records = null; 333 334 try { 335 ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); 336 if (iccISms != null) { 337 records = iccISms.getAllMessagesFromIccEf(); 338 } 339 } catch (RemoteException ex) { 340 // ignore it 341 } 342 343 return createMessageListFromRawRecords(records); 344 } 345 346 /** 347 * Create a list of <code>SmsMessage</code>s from a list of RawSmsData 348 * records returned by <code>getAllMessagesFromIcc()</code> 349 * 350 * @param records SMS EF records, returned by 351 * <code>getAllMessagesFromIcc</code> 352 * @return <code>ArrayList</code> of <code>SmsMessage</code> objects. 353 */ 354 private ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) { 355 ArrayList<SmsMessage> messages = new ArrayList<SmsMessage>(); 356 if (records != null) { 357 int count = records.size(); 358 for (int i = 0; i < count; i++) { 359 SmsRawData data = records.get(i); 360 // List contains all records, including "free" records (null) 361 if (data != null) { 362 SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes()); 363 if (sms != null) { 364 messages.add(sms); 365 } 366 } 367 } 368 } 369 return messages; 370 } 371 372 // see SmsMessage.getStatusOnIcc 373 374 /** Free space (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */ 375 static public final int STATUS_ON_ICC_FREE = 0; 376 377 /** Received and read (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */ 378 static public final int STATUS_ON_ICC_READ = 1; 379 380 /** Received and unread (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */ 381 static public final int STATUS_ON_ICC_UNREAD = 3; 382 383 /** Stored and sent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */ 384 static public final int STATUS_ON_ICC_SENT = 5; 385 386 /** Stored and unsent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */ 387 static public final int STATUS_ON_ICC_UNSENT = 7; 388 389 // SMS send failure result codes 390 391 /** Generic failure cause */ 392 static public final int RESULT_ERROR_GENERIC_FAILURE = 1; 393 /** Failed because radio was explicitly turned off */ 394 static public final int RESULT_ERROR_RADIO_OFF = 2; 395 /** Failed because no pdu provided */ 396 static public final int RESULT_ERROR_NULL_PDU = 3; 397 /** Failed because service is currently unavailable */ 398 static public final int RESULT_ERROR_NO_SERVICE = 4; 399 /** Failed because we reached the sending queue limit. {@hide} */ 400 static public final int RESULT_ERROR_LIMIT_EXCEEDED = 5; 401 /** Failed because FDN is enabled. {@hide} */ 402 static public final int RESULT_ERROR_FDN_CHECK_FAILURE = 6; 403 } 404