• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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