SmsManager.java revision d6bf802a1afd6dbd99b35804ec0ec565f2ac8e9e
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.ActivityThread;
20import android.app.PendingIntent;
21import android.os.RemoteException;
22import android.os.ServiceManager;
23import android.text.TextUtils;
24import android.util.Log;
25
26import com.android.internal.telephony.ISms;
27import com.android.internal.telephony.SmsRawData;
28import com.android.internal.telephony.IMms;
29import com.android.internal.telephony.uicc.IccConstants;
30
31import java.util.ArrayList;
32import java.util.Arrays;
33import java.util.List;
34
35/*
36 * TODO(code review): Curious question... Why are a lot of these
37 * methods not declared as static, since they do not seem to require
38 * any local object state?  Presumably this cannot be changed without
39 * interfering with the API...
40 */
41
42/**
43 * Manages SMS operations such as sending data, text, and pdu SMS messages.
44 * Get this object by calling the static method {@link #getDefault()}.
45 *
46 * <p>For information about how to behave as the default SMS app on Android 4.4 (API level 19)
47 * and higher, see {@link android.provider.Telephony}.
48 */
49public final class SmsManager {
50    /** Singleton object constructed during class initialization. */
51    private static final SmsManager sInstance = new SmsManager();
52    private static final int DEFAULT_SUB = 0;
53
54    /**
55     * Send a text based SMS.
56     *
57     * <p class="note"><strong>Note:</strong> Using this method requires that your app has the
58     * {@link android.Manifest.permission#SEND_SMS} permission.</p>
59     *
60     * <p class="note"><strong>Note:</strong> Beginning with Android 4.4 (API level 19), if
61     * <em>and only if</em> an app is not selected as the default SMS app, the system automatically
62     * writes messages sent using this method to the SMS Provider (the default SMS app is always
63     * responsible for writing its sent messages to the SMS Provider). For information about
64     * how to behave as the default SMS app, see {@link android.provider.Telephony}.</p>
65     *
66     *
67     * @param destinationAddress the address to send the message to
68     * @param scAddress is the service center address or null to use
69     *  the current default SMSC
70     * @param text the body of the message to send
71     * @param sentIntent if not NULL this <code>PendingIntent</code> is
72     *  broadcast when the message is successfully sent, or failed.
73     *  The result code will be <code>Activity.RESULT_OK</code> for success,
74     *  or one of these errors:<br>
75     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
76     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
77     *  <code>RESULT_ERROR_NULL_PDU</code><br>
78     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
79     *  the extra "errorCode" containing a radio technology specific value,
80     *  generally only useful for troubleshooting.<br>
81     *  The per-application based SMS control checks sentIntent. If sentIntent
82     *  is NULL the caller will be checked against all unknown applications,
83     *  which cause smaller number of SMS to be sent in checking period.
84     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
85     *  broadcast when the message is delivered to the recipient.  The
86     *  raw pdu of the status report is in the extended data ("pdu").
87     *
88     * @throws IllegalArgumentException if destinationAddress or text are empty
89     */
90    public void sendTextMessage(
91            String destinationAddress, String scAddress, String text,
92            PendingIntent sentIntent, PendingIntent deliveryIntent) {
93        sendTextMessage(getPreferredSmsSubscription(), destinationAddress, scAddress, text,
94           sentIntent, deliveryIntent);
95    }
96
97    /**
98     * Send a text based SMS.
99     *
100     * @param destinationAddress the address to send the message to
101     * @param scAddress is the service center address or null to use
102     *  the current default SMSC
103     * @param text the body of the message to send
104     * @param sentIntent if not NULL this <code>PendingIntent</code> is
105     *  broadcast when the message is successfully sent, or failed.
106     *  The result code will be <code>Activity.RESULT_OK</code> for success,
107     *  or one of these errors:<br>
108     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
109     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
110     *  <code>RESULT_ERROR_NULL_PDU</code><br>
111     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
112     *  the extra "errorCode" containing a radio technology specific value,
113     *  generally only useful for troubleshooting.<br>
114     *  The per-application based SMS control checks sentIntent. If sentIntent
115     *  is NULL the caller will be checked against all unknown applications,
116     *  which cause smaller number of SMS to be sent in checking period.
117     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
118     *  broadcast when the message is delivered to the recipient.  The
119     *  raw pdu of the status report is in the extended data ("pdu").
120     * @param subId on which the SMS has to be sent.
121     *
122     * @throws IllegalArgumentException if destinationAddress or text are empty
123     *
124     */
125    /** @hide */
126    public void sendTextMessage(
127            long subId, String destinationAddress, String scAddress, String text,
128            PendingIntent sentIntent, PendingIntent deliveryIntent) {
129        if (TextUtils.isEmpty(destinationAddress)) {
130            throw new IllegalArgumentException("Invalid destinationAddress");
131        }
132
133        if (TextUtils.isEmpty(text)) {
134            throw new IllegalArgumentException("Invalid message body");
135        }
136
137        try {
138            ISms iccISms = getISmsServiceOrThrow();
139            iccISms.sendText(ActivityThread.currentPackageName(), destinationAddress,
140                    scAddress, text, sentIntent, deliveryIntent);
141        } catch (RemoteException ex) {
142            // ignore it
143        }
144    }
145
146    /**
147     * TODO Move this to new CarrierSmsManager class.
148     *
149     * Inject an SMS PDU into the android application framework.
150     *
151     * @param pdu is the byte array of pdu to be injected into android application framework
152     * @param format is the format of SMS pdu (3gpp or 3gpp2)
153     * @param receivedIntent if not NULL this <code>PendingIntent</code> is
154     *  broadcast when the message is successfully received by the
155     *  android application framework. This intent is broadcasted at
156     *  the same time an SMS received from radio is acknowledged back.
157     *
158     *  @throws IllegalArgumentException if format is not one of 3gpp and 3gpp2.
159     *  {@hide}
160     */
161    public void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) {
162        if (!format.equals(SmsMessage.FORMAT_3GPP) && !format.equals(SmsMessage.FORMAT_3GPP2)) {
163            // Format must be either 3gpp or 3gpp2.
164            throw new IllegalArgumentException(
165                    "Invalid pdu format. format must be either 3gpp or 3gpp2");
166        }
167        try {
168            ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
169            if (iccISms != null) {
170                iccISms.injectSmsPdu(pdu, format, receivedIntent);
171            }
172        } catch (RemoteException ex) {
173          // ignore it
174        }
175    }
176
177    /**
178     * Update the status of a pending (send-by-IP) SMS message and resend by PSTN if necessary.
179     * This outbound message was handled by the carrier app. If the carrier app fails to send
180     * this message, it would be resent by PSTN.
181     *
182     * @param messageRef the reference number of the SMS message.
183     * @param success True if and only if the message was sent successfully. If its value is
184     *  false, this message should be resent via PSTN.
185     * {@hide}
186     */
187    public void updateSmsSendStatus(int messageRef, boolean success) {
188        try {
189            ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
190            if (iccISms != null) {
191                iccISms.updateSmsSendStatus(messageRef, success);
192            }
193        } catch (RemoteException ex) {
194          // ignore it
195        }
196    }
197
198    /**
199     * Divide a message text into several fragments, none bigger than
200     * the maximum SMS message size.
201     *
202     * @param text the original message.  Must not be null.
203     * @return an <code>ArrayList</code> of strings that, in order,
204     *   comprise the original message
205     *
206     * @throws IllegalArgumentException if text is null
207     */
208    public ArrayList<String> divideMessage(String text) {
209        if (null == text) {
210            throw new IllegalArgumentException("text is null");
211        }
212        return SmsMessage.fragmentText(text);
213    }
214
215    /**
216     * Send a multi-part text based SMS.  The callee should have already
217     * divided the message into correctly sized parts by calling
218     * <code>divideMessage</code>.
219     *
220     * <p class="note"><strong>Note:</strong> Using this method requires that your app has the
221     * {@link android.Manifest.permission#SEND_SMS} permission.</p>
222     *
223     * <p class="note"><strong>Note:</strong> Beginning with Android 4.4 (API level 19), if
224     * <em>and only if</em> an app is not selected as the default SMS app, the system automatically
225     * writes messages sent using this method to the SMS Provider (the default SMS app is always
226     * responsible for writing its sent messages to the SMS Provider). For information about
227     * how to behave as the default SMS app, see {@link android.provider.Telephony}.</p>
228     *
229     * @param destinationAddress the address to send the message to
230     * @param scAddress is the service center address or null to use
231     *   the current default SMSC
232     * @param parts an <code>ArrayList</code> of strings that, in order,
233     *   comprise the original message
234     * @param sentIntents if not null, an <code>ArrayList</code> of
235     *   <code>PendingIntent</code>s (one for each message part) that is
236     *   broadcast when the corresponding message part has been sent.
237     *   The result code will be <code>Activity.RESULT_OK</code> for success,
238     *   or one of these errors:<br>
239     *   <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
240     *   <code>RESULT_ERROR_RADIO_OFF</code><br>
241     *   <code>RESULT_ERROR_NULL_PDU</code><br>
242     *   For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include
243     *   the extra "errorCode" containing a radio technology specific value,
244     *   generally only useful for troubleshooting.<br>
245     *   The per-application based SMS control checks sentIntent. If sentIntent
246     *   is NULL the caller will be checked against all unknown applications,
247     *   which cause smaller number of SMS to be sent in checking period.
248     * @param deliveryIntents if not null, an <code>ArrayList</code> of
249     *   <code>PendingIntent</code>s (one for each message part) that is
250     *   broadcast when the corresponding message part has been delivered
251     *   to the recipient.  The raw pdu of the status report is in the
252     *   extended data ("pdu").
253     *
254     * @throws IllegalArgumentException if destinationAddress or data are empty
255     */
256    public void sendMultipartTextMessage(
257            String destinationAddress, String scAddress, ArrayList<String> parts,
258            ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
259        sendMultipartTextMessage(getPreferredSmsSubscription(), destinationAddress, scAddress, parts, sentIntents,
260            deliveryIntents);
261    }
262
263    /**
264     * Send a multi-part text based SMS.  The callee should have already
265     * divided the message into correctly sized parts by calling
266     * <code>divideMessage</code>.
267     *
268     * @param destinationAddress the address to send the message to
269     * @param scAddress is the service center address or null to use
270     *   the current default SMSC
271     * @param parts an <code>ArrayList</code> of strings that, in order,
272     *   comprise the original message
273     * @param sentIntents if not null, an <code>ArrayList</code> of
274     *   <code>PendingIntent</code>s (one for each message part) that is
275     *   broadcast when the corresponding message part has been sent.
276     *   The result code will be <code>Activity.RESULT_OK</code> for success,
277     *   or one of these errors:<br>
278     *   <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
279     *   <code>RESULT_ERROR_RADIO_OFF</code><br>
280     *   <code>RESULT_ERROR_NULL_PDU</code><br>
281     *   For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include
282     *   the extra "errorCode" containing a radio technology specific value,
283     *   generally only useful for troubleshooting.<br>
284     *   The per-application based SMS control checks sentIntent. If sentIntent
285     *   is NULL the caller will be checked against all unknown applications,
286     *   which cause smaller number of SMS to be sent in checking period.
287     * @param deliveryIntents if not null, an <code>ArrayList</code> of
288     *   <code>PendingIntent</code>s (one for each message part) that is
289     *   broadcast when the corresponding message part has been delivered
290     *   to the recipient.  The raw pdu of the status report is in the
291     *   extended data ("pdu").
292     *   @param subId on which the SMS has to be sent.
293     *
294     * @throws IllegalArgumentException if destinationAddress or data are empty
295     */
296    /** @hide */
297    public void sendMultipartTextMessage(long subId, String destinationAddress, String scAddress,
298            ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
299            ArrayList<PendingIntent> deliveryIntents) {
300        if (TextUtils.isEmpty(destinationAddress)) {
301            throw new IllegalArgumentException("Invalid destinationAddress");
302        }
303        if (parts == null || parts.size() < 1) {
304            throw new IllegalArgumentException("Invalid message body");
305        }
306
307        if (parts.size() > 1) {
308            try {
309                ISms iccISms = getISmsServiceOrThrow();
310                iccISms.sendMultipartText(ActivityThread.currentPackageName(),
311                        destinationAddress, scAddress, parts,
312                        sentIntents, deliveryIntents);
313            } catch (RemoteException ex) {
314                // ignore it
315            }
316        } else {
317            PendingIntent sentIntent = null;
318            PendingIntent deliveryIntent = null;
319            if (sentIntents != null && sentIntents.size() > 0) {
320                sentIntent = sentIntents.get(0);
321            }
322            if (deliveryIntents != null && deliveryIntents.size() > 0) {
323                deliveryIntent = deliveryIntents.get(0);
324            }
325            sendTextMessage(destinationAddress, scAddress, parts.get(0),
326                    sentIntent, deliveryIntent);
327        }
328    }
329
330    /**
331     * Send a data based SMS to a specific application port.
332     *
333     * <p class="note"><strong>Note:</strong> Using this method requires that your app has the
334     * {@link android.Manifest.permission#SEND_SMS} permission.</p>
335     *
336     * @param destinationAddress the address to send the message to
337     * @param scAddress is the service center address or null to use
338     *  the current default SMSC
339     * @param destinationPort the port to deliver the message to
340     * @param data the body of the message to send
341     * @param sentIntent if not NULL this <code>PendingIntent</code> is
342     *  broadcast when the message is successfully sent, or failed.
343     *  The result code will be <code>Activity.RESULT_OK</code> for success,
344     *  or one of these errors:<br>
345     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
346     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
347     *  <code>RESULT_ERROR_NULL_PDU</code><br>
348     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
349     *  the extra "errorCode" containing a radio technology specific value,
350     *  generally only useful for troubleshooting.<br>
351     *  The per-application based SMS control checks sentIntent. If sentIntent
352     *  is NULL the caller will be checked against all unknown applications,
353     *  which cause smaller number of SMS to be sent in checking period.
354     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
355     *  broadcast when the message is delivered to the recipient.  The
356     *  raw pdu of the status report is in the extended data ("pdu").
357     *
358     * @throws IllegalArgumentException if destinationAddress or data are empty
359     */
360    public void sendDataMessage(
361            String destinationAddress, String scAddress, short destinationPort,
362            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
363        sendDataMessage(getPreferredSmsSubscription(),
364            destinationAddress, scAddress, destinationPort,
365            data, sentIntent, deliveryIntent);
366    }
367
368    /**
369     * Send a data based SMS to a specific application port.
370     *
371     * @param destinationAddress the address to send the message to
372     * @param scAddress is the service center address or null to use
373     *  the current default SMSC
374     * @param destinationPort the port to deliver the message to
375     * @param data the body of the message to send
376     * @param sentIntent if not NULL this <code>PendingIntent</code> is
377     *  broadcast when the message is successfully sent, or failed.
378     *  The result code will be <code>Activity.RESULT_OK</code> for success,
379     *  or one of these errors:<br>
380     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
381     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
382     *  <code>RESULT_ERROR_NULL_PDU</code><br>
383     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
384     *  the extra "errorCode" containing a radio technology specific value,
385     *  generally only useful for troubleshooting.<br>
386     *  The per-application based SMS control checks sentIntent. If sentIntent
387     *  is NULL the caller will be checked against all unknown applications,
388     *  which cause smaller number of SMS to be sent in checking period.
389     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
390     *  broadcast when the message is delivered to the recipient.  The
391     *  raw pdu of the status report is in the extended data ("pdu").
392     *  @param subId on which the SMS has to be sent.
393     *
394     * @throws IllegalArgumentException if destinationAddress or data are empty
395     */
396    /** @hide */
397    public void sendDataMessage(long subId,
398            String destinationAddress, String scAddress, short destinationPort,
399            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
400        if (TextUtils.isEmpty(destinationAddress)) {
401            throw new IllegalArgumentException("Invalid destinationAddress");
402        }
403
404        if (data == null || data.length == 0) {
405            throw new IllegalArgumentException("Invalid message data");
406        }
407
408        try {
409            ISms iccISms = getISmsServiceOrThrow();
410            iccISms.sendData(ActivityThread.currentPackageName(),
411                    destinationAddress, scAddress, destinationPort & 0xFFFF,
412                    data, sentIntent, deliveryIntent);
413        } catch (RemoteException ex) {
414            // ignore it
415        }
416    }
417
418    /**
419     * Get the default instance of the SmsManager
420     *
421     * @return the default instance of the SmsManager
422     */
423    public static SmsManager getDefault() {
424        return sInstance;
425    }
426
427    private SmsManager() {
428        //nothing
429    }
430
431    /**
432     * Returns the ISms service, or throws an UnsupportedOperationException if
433     * the service does not exist.
434     */
435    private static ISms getISmsServiceOrThrow() {
436        ISms iccISms = getISmsService();
437        if (iccISms == null) {
438            throw new UnsupportedOperationException("Sms is not supported");
439        }
440        return iccISms;
441    }
442
443    private static ISms getISmsService() {
444        return ISms.Stub.asInterface(ServiceManager.getService("isms"));
445    }
446
447    /**
448     * Copy a raw SMS PDU to the ICC.
449     * ICC (Integrated Circuit Card) is the card of the device.
450     * For example, this can be the SIM or USIM for GSM.
451     *
452     * @param smsc the SMSC for this message, or NULL for the default SMSC
453     * @param pdu the raw PDU to store
454     * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD,
455     *               STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT)
456     * @return true for success
457     *
458     * @throws IllegalArgumentException if pdu is NULL
459     * {@hide}
460     */
461    public boolean copyMessageToIcc(byte[] smsc, byte[] pdu,int status) {
462        return copyMessageToIcc(getPreferredSmsSubscription(), smsc, pdu, status);
463    }
464
465    /**
466     * Copy a raw SMS PDU to the ICC on  subId.
467     * ICC (Integrated Circuit Card) is the card of the device.
468     * For example, this can be the SIM or USIM for GSM.
469     *
470     * @param smsc the SMSC for this message, or NULL for the default SMSC
471     * @param pdu the raw PDU to store
472     * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD,
473     *               STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT)
474     * @param subId from which SMS has to be copied.
475     * @return true for success
476     *
477     * @throws IllegalArgumentException if pdu is NULL
478     * {@hide}
479     */
480
481    /** @hide */
482    public boolean copyMessageToIcc(long subId, byte[] smsc, byte[] pdu, int status) {
483        boolean success = false;
484
485        if (null == pdu) {
486            throw new IllegalArgumentException("pdu is NULL");
487        }
488        try {
489            ISms iccISms = getISmsService();
490            if (iccISms != null) {
491                success = iccISms.copyMessageToIccEf(ActivityThread.currentPackageName(),
492                        status, pdu, smsc);
493            }
494        } catch (RemoteException ex) {
495            // ignore it
496        }
497
498        return success;
499    }
500
501    /**
502     * Delete the specified message from the ICC.
503     * ICC (Integrated Circuit Card) is the card of the device.
504     * For example, this can be the SIM or USIM for GSM.
505     *
506     * @param messageIndex is the record index of the message on ICC
507     * @return true for success
508     *
509     * {@hide}
510     */
511    public boolean
512    deleteMessageFromIcc(int messageIndex) {
513        return deleteMessageFromIcc(getPreferredSmsSubscription(), messageIndex);
514    }
515
516    /**
517     * Delete the specified message from the ICC on  subId.
518     * ICC (Integrated Circuit Card) is the card of the device.
519     * For example, this can be the SIM or USIM for GSM.
520     *
521     * @param messageIndex is the record index of the message on ICC
522     * @param subId from which SMS has to be deleted.
523     * @return true for success
524     *
525     */
526    /** @hide */
527    public boolean
528    deleteMessageFromIcc(long subId, int messageIndex) {
529        boolean success = false;
530        byte[] pdu = new byte[IccConstants.SMS_RECORD_LENGTH-1];
531        Arrays.fill(pdu, (byte)0xff);
532
533        try {
534            ISms iccISms = getISmsService();
535            if (iccISms != null) {
536                success = iccISms.updateMessageOnIccEf(ActivityThread.currentPackageName(),
537                        messageIndex, STATUS_ON_ICC_FREE, pdu);
538            }
539        } catch (RemoteException ex) {
540            // ignore it
541        }
542
543        return success;
544    }
545
546    /**
547     * Update the specified message on the ICC.
548     * ICC (Integrated Circuit Card) is the card of the device.
549     * For example, this can be the SIM or USIM for GSM.
550     *
551     * @param messageIndex record index of message to update
552     * @param newStatus new message status (STATUS_ON_ICC_READ,
553     *                  STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT,
554     *                  STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE)
555     * @param pdu the raw PDU to store
556     * @return true for success
557     *
558     * {@hide}
559     */
560    public boolean updateMessageOnIcc(int messageIndex, int newStatus, byte[] pdu) {
561        return updateMessageOnIcc(getPreferredSmsSubscription(), messageIndex, newStatus, pdu);
562    }
563
564    /**
565     * Update the specified message on the ICC on  subId.
566     * ICC (Integrated Circuit Card) is the card of the device.
567     * For example, this can be the SIM or USIM for GSM.
568     *
569     * @param messageIndex record index of message to update
570     * @param newStatus new message status (STATUS_ON_ICC_READ,
571     *                  STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT,
572     *                  STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE)
573     * @param pdu the raw PDU to store
574     * @param subId on which the SMS had to be updated.
575     * @return true for success
576     *
577     */
578    /** @hide */
579    public boolean updateMessageOnIcc(long subId, int messageIndex, int newStatus,
580                           byte[] pdu)   {
581        boolean success = false;
582
583        try {
584            ISms iccISms = getISmsService();
585            if (iccISms != null) {
586                success = iccISms.updateMessageOnIccEf(ActivityThread.currentPackageName(),
587                        messageIndex, newStatus, pdu);
588            }
589        } catch (RemoteException ex) {
590            // ignore it
591        }
592
593        return success;
594    }
595
596    /**
597     * Retrieves all messages currently stored on ICC.
598     * ICC (Integrated Circuit Card) is the card of the device.
599     * For example, this can be the SIM or USIM for GSM.
600     *
601     * @return <code>ArrayList</code> of <code>SmsMessage</code> objects
602     *
603     * {@hide}
604     */
605    public static ArrayList<SmsMessage> getAllMessagesFromIcc() {
606        return getAllMessagesFromIcc(getPreferredSmsSubscription());
607    }
608
609    /**
610     * Retrieves all messages currently stored on ICC on the default
611     * subId.
612     * ICC (Integrated Circuit Card) is the card of the device.
613     * For example, this can be the SIM or USIM for GSM.
614     *
615     * @param subId from which the messages had to be retrieved.
616     * @return <code>ArrayList</code> of <code>SmsMessage</code> objects
617     *
618     */
619    /** @hide */
620    public static ArrayList<SmsMessage> getAllMessagesFromIcc(long subId) {
621        List<SmsRawData> records = null;
622
623        try {
624            ISms iccISms = getISmsService();
625            if (iccISms != null) {
626                records = iccISms.getAllMessagesFromIccEf(ActivityThread.currentPackageName());
627            }
628        } catch (RemoteException ex) {
629            // ignore it
630        }
631
632        return createMessageListFromRawRecords(records);
633    }
634
635    /**
636     * Enable reception of cell broadcast (SMS-CB) messages with the given
637     * message identifier. Note that if two different clients enable the same
638     * message identifier, they must both disable it for the device to stop
639     * receiving those messages. All received messages will be broadcast in an
640     * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
641     * Note: This call is blocking, callers may want to avoid calling it from
642     * the main thread of an application.
643     *
644     * @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP)
645     * or C.R1001-G (3GPP2)
646     * @return true if successful, false otherwise
647     * @see #disableCellBroadcast(int)
648     *
649     * {@hide}
650     */
651    public boolean enableCellBroadcast(int messageIdentifier) {
652        return enableCellBroadcast(getPreferredSmsSubscription(), messageIdentifier);
653    }
654
655    /**
656     * Enable reception of cell broadcast (SMS-CB) messages with the given
657     * message identifier on a particular subId.
658     * Note that if two different clients enable the same
659     * message identifier, they must both disable it for the device to stop
660     * receiving those messages. All received messages will be broadcast in an
661     * intent with the action "android.provider.telephony.SMS_CB_RECEIVED".
662     * Note: This call is blocking, callers may want to avoid calling it from
663     * the main thread of an application.
664     *
665     * @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP)
666     * or C.R1001-G (3GPP2)
667     * @param subId for which the broadcast has to be enabled
668     * @return true if successful, false otherwise
669     * @see #disableCellBroadcast(int)
670     *
671     */
672     /** @hide */
673    public boolean enableCellBroadcast(long subId, int messageIdentifier) {
674        boolean success = false;
675
676        try {
677            ISms iccISms = getISmsService();
678            if (iccISms != null) {
679                success = iccISms.enableCellBroadcast(messageIdentifier);
680            }
681        } catch (RemoteException ex) {
682            // ignore it
683        }
684
685        return success;
686    }
687
688    /**
689     * Disable reception of cell broadcast (SMS-CB) messages with the given
690     * message identifier. Note that if two different clients enable the same
691     * message identifier, they must both disable it for the device to stop
692     * receiving those messages.
693     * Note: This call is blocking, callers may want to avoid calling it from
694     * the main thread of an application.
695     *
696     * @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP)
697     * or C.R1001-G (3GPP2)
698     * @return true if successful, false otherwise
699     *
700     * @see #enableCellBroadcast(int)
701     *
702     * {@hide}
703     */
704    public boolean disableCellBroadcast(int messageIdentifier) {
705        return disableCellBroadcast(getPreferredSmsSubscription(), messageIdentifier);
706    }
707
708    /**
709     * Disable reception of cell broadcast (SMS-CB) messages with the given
710     * message identifier on a particular subId.
711     * Note that if two different clients enable the same
712     * message identifier, they must both disable it for the device to stop
713     * receiving those messages.
714     * Note: This call is blocking, callers may want to avoid calling it from
715     * the main thread of an application.
716     *
717     * @param messageIdentifier Message identifier as specified in TS 23.041
718     * @param subId for which the broadcast has to be disabled
719     * @return true if successful, false otherwise
720     *
721     * @see #enableCellBroadcast(int)
722     *
723     */
724    /** @hide */
725    public boolean disableCellBroadcast(long subId, int messageIdentifier) {
726        boolean success = false;
727
728        try {
729            ISms iccISms = getISmsService();
730            if (iccISms != null) {
731                success = iccISms.disableCellBroadcast(messageIdentifier);
732            }
733        } catch (RemoteException ex) {
734            // ignore it
735        }
736
737        return success;
738    }
739
740    /**
741     * Enable reception of cell broadcast (SMS-CB) messages with the given
742     * message identifier range. Note that if two different clients enable the same
743     * message identifier, they must both disable it for the device to stop
744     * receiving those messages. All received messages will be broadcast in an
745     * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
746     * Note: This call is blocking, callers may want to avoid calling it from
747     * the main thread of an application.
748     *
749     * @param startMessageId first message identifier as specified in TS 23.041 (3GPP)
750     * or C.R1001-G (3GPP2)
751     * @param endMessageId last message identifier as specified in TS 23.041 (3GPP)
752     * or C.R1001-G (3GPP2)
753     * @return true if successful, false otherwise
754     * @see #disableCellBroadcastRange(int, int)
755     *
756     * @throws IllegalArgumentException if endMessageId < startMessageId
757     * {@hide}
758     */
759    public boolean enableCellBroadcastRange(int startMessageId, int endMessageId) {
760        return enableCellBroadcastRange(getPreferredSmsSubscription(), startMessageId,
761                endMessageId);
762    }
763
764    /**
765     * Enable reception of cell broadcast (SMS-CB) messages with the given
766     * message identifier range on a particular subId.
767     * Note that if two different clients enable the same
768     * message identifier, they must both disable it for the device to stop
769     * receiving those messages. All received messages will be broadcast in an
770     * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
771     * Note: This call is blocking, callers may want to avoid calling it from
772     * the main thread of an application.
773     *
774     * @param startMessageId first message identifier as specified in TS 23.041
775     * @param endMessageId last message identifier as specified in TS 23.041
776     * @return true if successful, false otherwise
777     * @see #disableCellBroadcastRange(int, int)
778     * @throws IllegalArgumentException if endMessageId < startMessageId
779     *
780     */
781    /** @hide */
782    public boolean enableCellBroadcastRange(long subId, int startMessageId,
783            int endMessageId) {
784        boolean success = false;
785
786        if (endMessageId < startMessageId) {
787            throw new IllegalArgumentException("endMessageId < startMessageId");
788        }
789        try {
790            ISms iccISms = getISmsService();
791            if (iccISms != null) {
792                success = iccISms.enableCellBroadcastRange(startMessageId, endMessageId);
793            }
794        } catch (RemoteException ex) {
795            // ignore it
796        }
797
798        return success;
799    }
800
801    /**
802     * Disable reception of cell broadcast (SMS-CB) messages with the given
803     * message identifier range. Note that if two different clients enable the same
804     * message identifier, they must both disable it for the device to stop
805     * receiving those messages.
806     * Note: This call is blocking, callers may want to avoid calling it from
807     * the main thread of an application.
808     *
809     * @param startMessageId first message identifier as specified in TS 23.041 (3GPP)
810     * or C.R1001-G (3GPP2)
811     * @param endMessageId last message identifier as specified in TS 23.041 (3GPP)
812     * or C.R1001-G (3GPP2)
813     * @return true if successful, false otherwise
814     *
815     * @see #enableCellBroadcastRange(int, int)
816     *
817     * @throws IllegalArgumentException if endMessageId < startMessageId
818     * {@hide}
819     */
820    public boolean disableCellBroadcastRange(int startMessageId, int endMessageId) {
821        return disableCellBroadcastRange(getPreferredSmsSubscription(), startMessageId,
822                endMessageId);
823    }
824
825    /**
826     * Disable reception of cdma broadcast messages with the given
827     * message identifier range on a particular subId.
828     * Note that if two different clients enable the same
829     * message identifier range, they must both disable it for the device to stop
830     * receiving those messages.
831     * Note: This call is blocking, callers may want to avoid calling it from
832     * the main thread of an application.
833     *
834     * @param startMessageId first message identifier as specified in TS 23.041
835     * @param endMessageId last message identifier as specified in TS 23.041
836     * @return true if successful, false otherwise
837     *
838     * @see #enableCellBroadcastRange(int, int)
839     * @throws IllegalArgumentException if endMessageId < startMessageId
840     *
841     */
842    /** @hide */
843    public boolean disableCellBroadcastRange(long subId, int startMessageId,
844            int endMessageId) {
845        boolean success = false;
846
847        if (endMessageId < startMessageId) {
848            throw new IllegalArgumentException("endMessageId < startMessageId");
849        }
850        try {
851            ISms iccISms = getISmsService();
852            if (iccISms != null) {
853                success = iccISms.disableCellBroadcastRange(startMessageId, endMessageId);
854            }
855        } catch (RemoteException ex) {
856            // ignore it
857        }
858
859        return success;
860    }
861
862    /**
863     * Create a list of <code>SmsMessage</code>s from a list of RawSmsData
864     * records returned by <code>getAllMessagesFromIcc()</code>
865     *
866     * @param records SMS EF records, returned by
867     *   <code>getAllMessagesFromIcc</code>
868     * @return <code>ArrayList</code> of <code>SmsMessage</code> objects.
869     */
870    private static ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
871        ArrayList<SmsMessage> messages = new ArrayList<SmsMessage>();
872        if (records != null) {
873            int count = records.size();
874            for (int i = 0; i < count; i++) {
875                SmsRawData data = records.get(i);
876                // List contains all records, including "free" records (null)
877                if (data != null) {
878                    SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes());
879                    if (sms != null) {
880                        messages.add(sms);
881                    }
882                }
883            }
884        }
885        return messages;
886    }
887
888    /**
889     * SMS over IMS is supported if IMS is registered and SMS is supported
890     * on IMS.
891     *
892     * @return true if SMS over IMS is supported, false otherwise
893     *
894     * @see #getImsSmsFormat()
895     *
896     * @hide
897     */
898    boolean isImsSmsSupported() {
899        return isImsSmsSupported(getPreferredSmsSubscription());
900    }
901
902    /** @hide */
903    boolean isImsSmsSupported(long subId) {
904        boolean boSupported = false;
905        try {
906            ISms iccISms = getISmsService();
907            if (iccISms != null) {
908                boSupported = iccISms.isImsSmsSupported();
909            }
910        } catch (RemoteException ex) {
911            // ignore it
912        }
913        return boSupported;
914    }
915
916    /**
917     * Gets SMS format supported on IMS.  SMS over IMS format is
918     * either 3GPP or 3GPP2.
919     *
920     * @return SmsMessage.FORMAT_3GPP,
921     *         SmsMessage.FORMAT_3GPP2
922     *      or SmsMessage.FORMAT_UNKNOWN
923     *
924     * @see #isImsSmsSupported()
925     *
926     * @hide
927     */
928    String getImsSmsFormat() {
929        return getImsSmsFormat(getPreferredSmsSubscription());
930    }
931
932    /** @hide */
933    String getImsSmsFormat(long subId) {
934        String format = com.android.internal.telephony.SmsConstants.FORMAT_UNKNOWN;
935        try {
936            ISms iccISms = getISmsService();
937            if (iccISms != null) {
938                format = iccISms.getImsSmsFormat();
939            }
940        } catch (RemoteException ex) {
941            // ignore it
942        }
943        return format;
944    }
945
946    /**
947     * Get the preferred sms subId
948     *
949     * @return the preferred subId
950     * @hide
951     */
952    public static long getPreferredSmsSubscription() {
953        ISms iccISms = null;
954        try {
955            iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
956            return (long) iccISms.getPreferredSmsSubscription();
957        } catch (RemoteException ex) {
958            return DEFAULT_SUB;
959        } catch (NullPointerException ex) {
960            return DEFAULT_SUB;
961        }
962    }
963
964    /**
965     * Get SMS prompt property,  enabled or not
966     *
967     * @return true if enabled, false otherwise
968     * @hide
969     */
970    public boolean isSMSPromptEnabled() {
971        ISms iccISms = null;
972        try {
973            iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
974            return iccISms.isSMSPromptEnabled();
975        } catch (RemoteException ex) {
976            return false;
977        } catch (NullPointerException ex) {
978            return false;
979        }
980    }
981
982    // see SmsMessage.getStatusOnIcc
983
984    /** Free space (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
985    static public final int STATUS_ON_ICC_FREE      = 0;
986
987    /** Received and read (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
988    static public final int STATUS_ON_ICC_READ      = 1;
989
990    /** Received and unread (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
991    static public final int STATUS_ON_ICC_UNREAD    = 3;
992
993    /** Stored and sent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
994    static public final int STATUS_ON_ICC_SENT      = 5;
995
996    /** Stored and unsent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
997    static public final int STATUS_ON_ICC_UNSENT    = 7;
998
999    // SMS send failure result codes
1000
1001    /** Generic failure cause */
1002    static public final int RESULT_ERROR_GENERIC_FAILURE    = 1;
1003    /** Failed because radio was explicitly turned off */
1004    static public final int RESULT_ERROR_RADIO_OFF          = 2;
1005    /** Failed because no pdu provided */
1006    static public final int RESULT_ERROR_NULL_PDU           = 3;
1007    /** Failed because service is currently unavailable */
1008    static public final int RESULT_ERROR_NO_SERVICE         = 4;
1009    /** Failed because we reached the sending queue limit.  {@hide} */
1010    static public final int RESULT_ERROR_LIMIT_EXCEEDED     = 5;
1011    /** Failed because FDN is enabled. {@hide} */
1012    static public final int RESULT_ERROR_FDN_CHECK_FAILURE  = 6;
1013
1014    /**
1015     * Send an MMS message
1016     *
1017     * @param pdu the MMS message encoded in standard MMS PDU format
1018     * @param locationUrl the optional location url where message should be sent to
1019     * @param sentIntent if not NULL this <code>PendingIntent</code> is
1020     *  broadcast when the message is successfully sent, or failed
1021     * @hide
1022     */
1023    public void sendMultimediaMessage(byte[] pdu, String locationUrl, PendingIntent sentIntent) {
1024        if (pdu == null || pdu.length == 0) {
1025            throw new IllegalArgumentException("Empty or zero length PDU");
1026        }
1027        try {
1028            final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
1029            if (iMms == null) {
1030                return;
1031            }
1032            iMms.sendMessage(ActivityThread.currentPackageName(), pdu, locationUrl, sentIntent);
1033        } catch (RemoteException e) {
1034            // Ignore it
1035        }
1036    }
1037
1038    /**
1039     * Download an MMS message from carrier by a given location URL
1040     *
1041     * @param locationUrl the location URL of the MMS message to be downloaded, usually obtained
1042     *  from the MMS WAP push notification
1043     * @param downloadedIntent if not NULL this <code>PendingIntent</code> is
1044     *  broadcast when the message is downloaded, or the download is failed
1045     * @hide
1046     */
1047    public void downloadMultimediaMessage(String locationUrl, PendingIntent downloadedIntent) {
1048        if (TextUtils.isEmpty(locationUrl)) {
1049            throw new IllegalArgumentException("Empty MMS location URL");
1050        }
1051        try {
1052            final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
1053            if (iMms == null) {
1054                return;
1055            }
1056            iMms.downloadMessage(ActivityThread.currentPackageName(), locationUrl,
1057                    downloadedIntent);
1058        } catch (RemoteException e) {
1059            // Ignore it
1060        }
1061    }
1062
1063    // MMS send/download failure result codes
1064    /**@hide*/
1065    public static final int MMS_ERROR_UNSPECIFIED = 1;
1066    /**@hide*/
1067    public static final int MMS_ERROR_INVALID_APN = 2;
1068    /**@hide*/
1069    public static final int MMS_ERROR_UNABLE_CONNECT_MMS = 3;
1070    /**@hide*/
1071    public static final int MMS_ERROR_HTTP_FAILURE = 4;
1072
1073    // Intent extra name for result data
1074    /**@hide*/
1075    public static final String MMS_EXTRA_DATA = "data";
1076
1077}
1078