SmsManager.java revision 910825a2ed10bd5cc454b91380b7db0dac2e616e
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