SmsManager.java revision e58ff3bd4399dfe68517b100169e01e6b93dc7b6
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.content.ContentValues;
22import android.net.Uri;
23import android.os.RemoteException;
24import android.os.ServiceManager;
25import android.text.TextUtils;
26import android.util.Log;
27
28import com.android.internal.telephony.ISms;
29import com.android.internal.telephony.SmsRawData;
30import com.android.internal.telephony.IMms;
31import com.android.internal.telephony.uicc.IccConstants;
32
33import java.util.ArrayList;
34import java.util.Arrays;
35import java.util.List;
36
37/*
38 * TODO(code review): Curious question... Why are a lot of these
39 * methods not declared as static, since they do not seem to require
40 * any local object state?  Presumably this cannot be changed without
41 * interfering with the API...
42 */
43
44/**
45 * Manages SMS operations such as sending data, text, and pdu SMS messages.
46 * Get this object by calling the static method {@link #getDefault()}.
47 *
48 * <p>For information about how to behave as the default SMS app on Android 4.4 (API level 19)
49 * and higher, see {@link android.provider.Telephony}.
50 */
51public final class SmsManager {
52    /** Singleton object constructed during class initialization. */
53    private static final SmsManager sInstance = new SmsManager();
54    private static final int DEFAULT_SUB = 0;
55
56    /**
57     * Send a text based SMS.
58     *
59     * <p class="note"><strong>Note:</strong> Using this method requires that your app has the
60     * {@link android.Manifest.permission#SEND_SMS} permission.</p>
61     *
62     * <p class="note"><strong>Note:</strong> Beginning with Android 4.4 (API level 19), if
63     * <em>and only if</em> an app is not selected as the default SMS app, the system automatically
64     * writes messages sent using this method to the SMS Provider (the default SMS app is always
65     * responsible for writing its sent messages to the SMS Provider). For information about
66     * how to behave as the default SMS app, see {@link android.provider.Telephony}.</p>
67     *
68     *
69     * @param destinationAddress the address to send the message to
70     * @param scAddress is the service center address or null to use
71     *  the current default SMSC
72     * @param text the body of the message to send
73     * @param sentIntent if not NULL this <code>PendingIntent</code> is
74     *  broadcast when the message is successfully sent, or failed.
75     *  The result code will be <code>Activity.RESULT_OK</code> for success,
76     *  or one of these errors:<br>
77     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
78     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
79     *  <code>RESULT_ERROR_NULL_PDU</code><br>
80     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
81     *  the extra "errorCode" containing a radio technology specific value,
82     *  generally only useful for troubleshooting.<br>
83     *  The per-application based SMS control checks sentIntent. If sentIntent
84     *  is NULL the caller will be checked against all unknown applications,
85     *  which cause smaller number of SMS to be sent in checking period.
86     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
87     *  broadcast when the message is delivered to the recipient.  The
88     *  raw pdu of the status report is in the extended data ("pdu").
89     *
90     * @throws IllegalArgumentException if destinationAddress or text are empty
91     */
92    public void sendTextMessage(
93            String destinationAddress, String scAddress, String text,
94            PendingIntent sentIntent, PendingIntent deliveryIntent) {
95        sendTextMessage(getPreferredSmsSubscription(), destinationAddress, scAddress, text,
96           sentIntent, deliveryIntent);
97    }
98
99    /**
100     * Send a text based SMS.
101     *
102     * @param destinationAddress the address to send the message to
103     * @param scAddress is the service center address or null to use
104     *  the current default SMSC
105     * @param text the body of the message to send
106     * @param sentIntent if not NULL this <code>PendingIntent</code> is
107     *  broadcast when the message is successfully sent, or failed.
108     *  The result code will be <code>Activity.RESULT_OK</code> for success,
109     *  or one of these errors:<br>
110     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
111     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
112     *  <code>RESULT_ERROR_NULL_PDU</code><br>
113     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
114     *  the extra "errorCode" containing a radio technology specific value,
115     *  generally only useful for troubleshooting.<br>
116     *  The per-application based SMS control checks sentIntent. If sentIntent
117     *  is NULL the caller will be checked against all unknown applications,
118     *  which cause smaller number of SMS to be sent in checking period.
119     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
120     *  broadcast when the message is delivered to the recipient.  The
121     *  raw pdu of the status report is in the extended data ("pdu").
122     * @param subId on which the SMS has to be sent.
123     *
124     * @throws IllegalArgumentException if destinationAddress or text are empty
125     *
126     */
127    /** @hide */
128    public void sendTextMessage(
129            long subId, String destinationAddress, String scAddress, String text,
130            PendingIntent sentIntent, PendingIntent deliveryIntent) {
131        if (TextUtils.isEmpty(destinationAddress)) {
132            throw new IllegalArgumentException("Invalid destinationAddress");
133        }
134
135        if (TextUtils.isEmpty(text)) {
136            throw new IllegalArgumentException("Invalid message body");
137        }
138
139        try {
140            ISms iccISms = getISmsServiceOrThrow();
141            iccISms.sendText(ActivityThread.currentPackageName(), destinationAddress,
142                    scAddress, text, sentIntent, deliveryIntent);
143        } catch (RemoteException ex) {
144            // ignore it
145        }
146    }
147
148    /**
149     * Inject an SMS PDU into the android application framework.
150     *
151     * The caller should have carrier privileges.
152     * @see android.telephony.TelephonyManager.hasCarrierPrivileges
153     *
154     * @param pdu is the byte array of pdu to be injected into android application framework
155     * @param format is the format of SMS pdu (3gpp or 3gpp2)
156     * @param receivedIntent if not NULL this <code>PendingIntent</code> is
157     *  broadcast when the message is successfully received by the
158     *  android application framework. This intent is broadcasted at
159     *  the same time an SMS received from radio is acknowledged back.
160     *
161     *  @throws IllegalArgumentException if format is not one of 3gpp and 3gpp2.
162     */
163    public void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) {
164        if (!format.equals(SmsMessage.FORMAT_3GPP) && !format.equals(SmsMessage.FORMAT_3GPP2)) {
165            // Format must be either 3gpp or 3gpp2.
166            throw new IllegalArgumentException(
167                    "Invalid pdu format. format must be either 3gpp or 3gpp2");
168        }
169        try {
170            ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
171            if (iccISms != null) {
172                iccISms.injectSmsPdu(pdu, format, receivedIntent);
173            }
174        } catch (RemoteException ex) {
175          // ignore it
176        }
177    }
178
179    /**
180     * Update the status of a pending (send-by-IP) SMS message and resend by PSTN if necessary.
181     * This outbound message was handled by the carrier app. If the carrier app fails to send
182     * this message, it would be resent by PSTN.
183     *
184     * The caller should have carrier privileges.
185     * @see android.telephony.TelephonyManager.hasCarrierPrivileges
186     *
187     * @param messageRef the reference number of the SMS message.
188     * @param success True if and only if the message was sent successfully. If its value is
189     *  false, this message should be resent via PSTN.
190     */
191    public void updateSmsSendStatus(int messageRef, boolean success) {
192        try {
193            ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
194            if (iccISms != null) {
195                iccISms.updateSmsSendStatus(messageRef, success);
196            }
197        } catch (RemoteException ex) {
198          // ignore it
199        }
200    }
201
202    /**
203     * Divide a message text into several fragments, none bigger than
204     * the maximum SMS message size.
205     *
206     * @param text the original message.  Must not be null.
207     * @return an <code>ArrayList</code> of strings that, in order,
208     *   comprise the original message
209     *
210     * @throws IllegalArgumentException if text is null
211     */
212    public ArrayList<String> divideMessage(String text) {
213        if (null == text) {
214            throw new IllegalArgumentException("text is null");
215        }
216        return SmsMessage.fragmentText(text);
217    }
218
219    /**
220     * Send a multi-part text based SMS.  The callee should have already
221     * divided the message into correctly sized parts by calling
222     * <code>divideMessage</code>.
223     *
224     * <p class="note"><strong>Note:</strong> Using this method requires that your app has the
225     * {@link android.Manifest.permission#SEND_SMS} permission.</p>
226     *
227     * <p class="note"><strong>Note:</strong> Beginning with Android 4.4 (API level 19), if
228     * <em>and only if</em> an app is not selected as the default SMS app, the system automatically
229     * writes messages sent using this method to the SMS Provider (the default SMS app is always
230     * responsible for writing its sent messages to the SMS Provider). For information about
231     * how to behave as the default SMS app, see {@link android.provider.Telephony}.</p>
232     *
233     * @param destinationAddress the address to send the message to
234     * @param scAddress is the service center address or null to use
235     *   the current default SMSC
236     * @param parts an <code>ArrayList</code> of strings that, in order,
237     *   comprise the original message
238     * @param sentIntents if not null, an <code>ArrayList</code> of
239     *   <code>PendingIntent</code>s (one for each message part) that is
240     *   broadcast when the corresponding message part has been sent.
241     *   The result code will be <code>Activity.RESULT_OK</code> for success,
242     *   or one of these errors:<br>
243     *   <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
244     *   <code>RESULT_ERROR_RADIO_OFF</code><br>
245     *   <code>RESULT_ERROR_NULL_PDU</code><br>
246     *   For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include
247     *   the extra "errorCode" containing a radio technology specific value,
248     *   generally only useful for troubleshooting.<br>
249     *   The per-application based SMS control checks sentIntent. If sentIntent
250     *   is NULL the caller will be checked against all unknown applications,
251     *   which cause smaller number of SMS to be sent in checking period.
252     * @param deliveryIntents if not null, an <code>ArrayList</code> of
253     *   <code>PendingIntent</code>s (one for each message part) that is
254     *   broadcast when the corresponding message part has been delivered
255     *   to the recipient.  The raw pdu of the status report is in the
256     *   extended data ("pdu").
257     *
258     * @throws IllegalArgumentException if destinationAddress or data are empty
259     */
260    public void sendMultipartTextMessage(
261            String destinationAddress, String scAddress, ArrayList<String> parts,
262            ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
263        sendMultipartTextMessage(getPreferredSmsSubscription(), destinationAddress, scAddress, parts, sentIntents,
264            deliveryIntents);
265    }
266
267    /**
268     * Send a multi-part text based SMS.  The callee should have already
269     * divided the message into correctly sized parts by calling
270     * <code>divideMessage</code>.
271     *
272     * @param destinationAddress the address to send the message to
273     * @param scAddress is the service center address or null to use
274     *   the current default SMSC
275     * @param parts an <code>ArrayList</code> of strings that, in order,
276     *   comprise the original message
277     * @param sentIntents if not null, an <code>ArrayList</code> of
278     *   <code>PendingIntent</code>s (one for each message part) that is
279     *   broadcast when the corresponding message part has been sent.
280     *   The result code will be <code>Activity.RESULT_OK</code> for success,
281     *   or one of these errors:<br>
282     *   <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
283     *   <code>RESULT_ERROR_RADIO_OFF</code><br>
284     *   <code>RESULT_ERROR_NULL_PDU</code><br>
285     *   For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include
286     *   the extra "errorCode" containing a radio technology specific value,
287     *   generally only useful for troubleshooting.<br>
288     *   The per-application based SMS control checks sentIntent. If sentIntent
289     *   is NULL the caller will be checked against all unknown applications,
290     *   which cause smaller number of SMS to be sent in checking period.
291     * @param deliveryIntents if not null, an <code>ArrayList</code> of
292     *   <code>PendingIntent</code>s (one for each message part) that is
293     *   broadcast when the corresponding message part has been delivered
294     *   to the recipient.  The raw pdu of the status report is in the
295     *   extended data ("pdu").
296     *   @param subId on which the SMS has to be sent.
297     *
298     * @throws IllegalArgumentException if destinationAddress or data are empty
299     */
300    /** @hide */
301    public void sendMultipartTextMessage(long subId, String destinationAddress, String scAddress,
302            ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
303            ArrayList<PendingIntent> deliveryIntents) {
304        if (TextUtils.isEmpty(destinationAddress)) {
305            throw new IllegalArgumentException("Invalid destinationAddress");
306        }
307        if (parts == null || parts.size() < 1) {
308            throw new IllegalArgumentException("Invalid message body");
309        }
310
311        if (parts.size() > 1) {
312            try {
313                ISms iccISms = getISmsServiceOrThrow();
314                iccISms.sendMultipartText(ActivityThread.currentPackageName(),
315                        destinationAddress, scAddress, parts,
316                        sentIntents, deliveryIntents);
317            } catch (RemoteException ex) {
318                // ignore it
319            }
320        } else {
321            PendingIntent sentIntent = null;
322            PendingIntent deliveryIntent = null;
323            if (sentIntents != null && sentIntents.size() > 0) {
324                sentIntent = sentIntents.get(0);
325            }
326            if (deliveryIntents != null && deliveryIntents.size() > 0) {
327                deliveryIntent = deliveryIntents.get(0);
328            }
329            sendTextMessage(destinationAddress, scAddress, parts.get(0),
330                    sentIntent, deliveryIntent);
331        }
332    }
333
334    /**
335     * Send a data based SMS to a specific application port.
336     *
337     * <p class="note"><strong>Note:</strong> Using this method requires that your app has the
338     * {@link android.Manifest.permission#SEND_SMS} permission.</p>
339     *
340     * @param destinationAddress the address to send the message to
341     * @param scAddress is the service center address or null to use
342     *  the current default SMSC
343     * @param destinationPort the port to deliver the message to
344     * @param data the body of the message to send
345     * @param sentIntent if not NULL this <code>PendingIntent</code> is
346     *  broadcast when the message is successfully sent, or failed.
347     *  The result code will be <code>Activity.RESULT_OK</code> for success,
348     *  or one of these errors:<br>
349     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
350     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
351     *  <code>RESULT_ERROR_NULL_PDU</code><br>
352     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
353     *  the extra "errorCode" containing a radio technology specific value,
354     *  generally only useful for troubleshooting.<br>
355     *  The per-application based SMS control checks sentIntent. If sentIntent
356     *  is NULL the caller will be checked against all unknown applications,
357     *  which cause smaller number of SMS to be sent in checking period.
358     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
359     *  broadcast when the message is delivered to the recipient.  The
360     *  raw pdu of the status report is in the extended data ("pdu").
361     *
362     * @throws IllegalArgumentException if destinationAddress or data are empty
363     */
364    public void sendDataMessage(
365            String destinationAddress, String scAddress, short destinationPort,
366            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
367        sendDataMessage(getPreferredSmsSubscription(),
368            destinationAddress, scAddress, destinationPort,
369            data, sentIntent, deliveryIntent);
370    }
371
372    /**
373     * Send a data based SMS to a specific application port.
374     *
375     * @param destinationAddress the address to send the message to
376     * @param scAddress is the service center address or null to use
377     *  the current default SMSC
378     * @param destinationPort the port to deliver the message to
379     * @param data the body of the message to send
380     * @param sentIntent if not NULL this <code>PendingIntent</code> is
381     *  broadcast when the message is successfully sent, or failed.
382     *  The result code will be <code>Activity.RESULT_OK</code> for success,
383     *  or one of these errors:<br>
384     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
385     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
386     *  <code>RESULT_ERROR_NULL_PDU</code><br>
387     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
388     *  the extra "errorCode" containing a radio technology specific value,
389     *  generally only useful for troubleshooting.<br>
390     *  The per-application based SMS control checks sentIntent. If sentIntent
391     *  is NULL the caller will be checked against all unknown applications,
392     *  which cause smaller number of SMS to be sent in checking period.
393     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
394     *  broadcast when the message is delivered to the recipient.  The
395     *  raw pdu of the status report is in the extended data ("pdu").
396     *  @param subId on which the SMS has to be sent.
397     *
398     * @throws IllegalArgumentException if destinationAddress or data are empty
399     */
400    /** @hide */
401    public void sendDataMessage(long subId,
402            String destinationAddress, String scAddress, short destinationPort,
403            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
404        if (TextUtils.isEmpty(destinationAddress)) {
405            throw new IllegalArgumentException("Invalid destinationAddress");
406        }
407
408        if (data == null || data.length == 0) {
409            throw new IllegalArgumentException("Invalid message data");
410        }
411
412        try {
413            ISms iccISms = getISmsServiceOrThrow();
414            iccISms.sendData(ActivityThread.currentPackageName(),
415                    destinationAddress, scAddress, destinationPort & 0xFFFF,
416                    data, sentIntent, deliveryIntent);
417        } catch (RemoteException ex) {
418            // ignore it
419        }
420    }
421
422    /**
423     * Get the default instance of the SmsManager
424     *
425     * @return the default instance of the SmsManager
426     */
427    public static SmsManager getDefault() {
428        return sInstance;
429    }
430
431    private SmsManager() {
432        //nothing
433    }
434
435    /**
436     * Returns the ISms service, or throws an UnsupportedOperationException if
437     * the service does not exist.
438     */
439    private static ISms getISmsServiceOrThrow() {
440        ISms iccISms = getISmsService();
441        if (iccISms == null) {
442            throw new UnsupportedOperationException("Sms is not supported");
443        }
444        return iccISms;
445    }
446
447    private static ISms getISmsService() {
448        return ISms.Stub.asInterface(ServiceManager.getService("isms"));
449    }
450
451    /**
452     * Copy a raw SMS PDU to the ICC.
453     * ICC (Integrated Circuit Card) is the card of the device.
454     * For example, this can be the SIM or USIM for GSM.
455     *
456     * @param smsc the SMSC for this message, or NULL for the default SMSC
457     * @param pdu the raw PDU to store
458     * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD,
459     *               STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT)
460     * @return true for success
461     *
462     * @throws IllegalArgumentException if pdu is NULL
463     * {@hide}
464     */
465    public boolean copyMessageToIcc(byte[] smsc, byte[] pdu,int status) {
466        return copyMessageToIcc(getPreferredSmsSubscription(), smsc, pdu, status);
467    }
468
469    /**
470     * Copy a raw SMS PDU to the ICC on  subId.
471     * ICC (Integrated Circuit Card) is the card of the device.
472     * For example, this can be the SIM or USIM for GSM.
473     *
474     * @param smsc the SMSC for this message, or NULL for the default SMSC
475     * @param pdu the raw PDU to store
476     * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD,
477     *               STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT)
478     * @param subId from which SMS has to be copied.
479     * @return true for success
480     *
481     * @throws IllegalArgumentException if pdu is NULL
482     * {@hide}
483     */
484
485    /** @hide */
486    public boolean copyMessageToIcc(long subId, byte[] smsc, byte[] pdu, int status) {
487        boolean success = false;
488
489        if (null == pdu) {
490            throw new IllegalArgumentException("pdu is NULL");
491        }
492        try {
493            ISms iccISms = getISmsService();
494            if (iccISms != null) {
495                success = iccISms.copyMessageToIccEf(ActivityThread.currentPackageName(),
496                        status, pdu, smsc);
497            }
498        } catch (RemoteException ex) {
499            // ignore it
500        }
501
502        return success;
503    }
504
505    /**
506     * Delete the specified message from the ICC.
507     * ICC (Integrated Circuit Card) is the card of the device.
508     * For example, this can be the SIM or USIM for GSM.
509     *
510     * @param messageIndex is the record index of the message on ICC
511     * @return true for success
512     *
513     * {@hide}
514     */
515    public boolean
516    deleteMessageFromIcc(int messageIndex) {
517        return deleteMessageFromIcc(getPreferredSmsSubscription(), messageIndex);
518    }
519
520    /**
521     * Delete the specified message from the ICC on  subId.
522     * ICC (Integrated Circuit Card) is the card of the device.
523     * For example, this can be the SIM or USIM for GSM.
524     *
525     * @param messageIndex is the record index of the message on ICC
526     * @param subId from which SMS has to be deleted.
527     * @return true for success
528     *
529     */
530    /** @hide */
531    public boolean
532    deleteMessageFromIcc(long subId, int messageIndex) {
533        boolean success = false;
534        byte[] pdu = new byte[IccConstants.SMS_RECORD_LENGTH-1];
535        Arrays.fill(pdu, (byte)0xff);
536
537        try {
538            ISms iccISms = getISmsService();
539            if (iccISms != null) {
540                success = iccISms.updateMessageOnIccEf(ActivityThread.currentPackageName(),
541                        messageIndex, STATUS_ON_ICC_FREE, pdu);
542            }
543        } catch (RemoteException ex) {
544            // ignore it
545        }
546
547        return success;
548    }
549
550    /**
551     * Update the specified message on the ICC.
552     * ICC (Integrated Circuit Card) is the card of the device.
553     * For example, this can be the SIM or USIM for GSM.
554     *
555     * @param messageIndex record index of message to update
556     * @param newStatus new message status (STATUS_ON_ICC_READ,
557     *                  STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT,
558     *                  STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE)
559     * @param pdu the raw PDU to store
560     * @return true for success
561     *
562     * {@hide}
563     */
564    public boolean updateMessageOnIcc(int messageIndex, int newStatus, byte[] pdu) {
565        return updateMessageOnIcc(getPreferredSmsSubscription(), messageIndex, newStatus, pdu);
566    }
567
568    /**
569     * Update the specified message on the ICC on  subId.
570     * ICC (Integrated Circuit Card) is the card of the device.
571     * For example, this can be the SIM or USIM for GSM.
572     *
573     * @param messageIndex record index of message to update
574     * @param newStatus new message status (STATUS_ON_ICC_READ,
575     *                  STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT,
576     *                  STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE)
577     * @param pdu the raw PDU to store
578     * @param subId on which the SMS had to be updated.
579     * @return true for success
580     *
581     */
582    /** @hide */
583    public boolean updateMessageOnIcc(long subId, int messageIndex, int newStatus,
584                           byte[] pdu)   {
585        boolean success = false;
586
587        try {
588            ISms iccISms = getISmsService();
589            if (iccISms != null) {
590                success = iccISms.updateMessageOnIccEf(ActivityThread.currentPackageName(),
591                        messageIndex, newStatus, pdu);
592            }
593        } catch (RemoteException ex) {
594            // ignore it
595        }
596
597        return success;
598    }
599
600    /**
601     * Retrieves all messages currently stored on ICC.
602     * ICC (Integrated Circuit Card) is the card of the device.
603     * For example, this can be the SIM or USIM for GSM.
604     *
605     * @return <code>ArrayList</code> of <code>SmsMessage</code> objects
606     *
607     * {@hide}
608     */
609    public static ArrayList<SmsMessage> getAllMessagesFromIcc() {
610        return getAllMessagesFromIcc(getPreferredSmsSubscription());
611    }
612
613    /**
614     * Retrieves all messages currently stored on ICC on the default
615     * subId.
616     * ICC (Integrated Circuit Card) is the card of the device.
617     * For example, this can be the SIM or USIM for GSM.
618     *
619     * @param subId from which the messages had to be retrieved.
620     * @return <code>ArrayList</code> of <code>SmsMessage</code> objects
621     *
622     */
623    /** @hide */
624    public static ArrayList<SmsMessage> getAllMessagesFromIcc(long subId) {
625        List<SmsRawData> records = null;
626
627        try {
628            ISms iccISms = getISmsService();
629            if (iccISms != null) {
630                records = iccISms.getAllMessagesFromIccEf(ActivityThread.currentPackageName());
631            }
632        } catch (RemoteException ex) {
633            // ignore it
634        }
635
636        return createMessageListFromRawRecords(records);
637    }
638
639    /**
640     * Enable reception of cell broadcast (SMS-CB) messages with the given
641     * message identifier. Note that if two different clients enable the same
642     * message identifier, they must both disable it for the device to stop
643     * receiving those messages. All received messages will be broadcast in an
644     * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
645     * Note: This call is blocking, callers may want to avoid calling it from
646     * the main thread of an application.
647     *
648     * @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP)
649     * or C.R1001-G (3GPP2)
650     * @return true if successful, false otherwise
651     * @see #disableCellBroadcast(int)
652     *
653     * {@hide}
654     */
655    public boolean enableCellBroadcast(int messageIdentifier) {
656        return enableCellBroadcast(getPreferredSmsSubscription(), messageIdentifier);
657    }
658
659    /**
660     * Enable reception of cell broadcast (SMS-CB) messages with the given
661     * message identifier on a particular subId.
662     * Note that if two different clients enable the same
663     * message identifier, they must both disable it for the device to stop
664     * receiving those messages. All received messages will be broadcast in an
665     * intent with the action "android.provider.telephony.SMS_CB_RECEIVED".
666     * Note: This call is blocking, callers may want to avoid calling it from
667     * the main thread of an application.
668     *
669     * @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP)
670     * or C.R1001-G (3GPP2)
671     * @param subId for which the broadcast has to be enabled
672     * @return true if successful, false otherwise
673     * @see #disableCellBroadcast(int)
674     *
675     */
676     /** @hide */
677    public boolean enableCellBroadcast(long subId, int messageIdentifier) {
678        boolean success = false;
679
680        try {
681            ISms iccISms = getISmsService();
682            if (iccISms != null) {
683                success = iccISms.enableCellBroadcast(messageIdentifier);
684            }
685        } catch (RemoteException ex) {
686            // ignore it
687        }
688
689        return success;
690    }
691
692    /**
693     * Disable reception of cell broadcast (SMS-CB) messages with the given
694     * message identifier. Note that if two different clients enable the same
695     * message identifier, they must both disable it for the device to stop
696     * receiving those messages.
697     * Note: This call is blocking, callers may want to avoid calling it from
698     * the main thread of an application.
699     *
700     * @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP)
701     * or C.R1001-G (3GPP2)
702     * @return true if successful, false otherwise
703     *
704     * @see #enableCellBroadcast(int)
705     *
706     * {@hide}
707     */
708    public boolean disableCellBroadcast(int messageIdentifier) {
709        return disableCellBroadcast(getPreferredSmsSubscription(), messageIdentifier);
710    }
711
712    /**
713     * Disable reception of cell broadcast (SMS-CB) messages with the given
714     * message identifier on a particular subId.
715     * Note that if two different clients enable the same
716     * message identifier, they must both disable it for the device to stop
717     * receiving those messages.
718     * Note: This call is blocking, callers may want to avoid calling it from
719     * the main thread of an application.
720     *
721     * @param messageIdentifier Message identifier as specified in TS 23.041
722     * @param subId for which the broadcast has to be disabled
723     * @return true if successful, false otherwise
724     *
725     * @see #enableCellBroadcast(int)
726     *
727     */
728    /** @hide */
729    public boolean disableCellBroadcast(long subId, int messageIdentifier) {
730        boolean success = false;
731
732        try {
733            ISms iccISms = getISmsService();
734            if (iccISms != null) {
735                success = iccISms.disableCellBroadcast(messageIdentifier);
736            }
737        } catch (RemoteException ex) {
738            // ignore it
739        }
740
741        return success;
742    }
743
744    /**
745     * Enable reception of cell broadcast (SMS-CB) messages with the given
746     * message identifier range. Note that if two different clients enable the same
747     * message identifier, they must both disable it for the device to stop
748     * receiving those messages. All received messages will be broadcast in an
749     * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
750     * Note: This call is blocking, callers may want to avoid calling it from
751     * the main thread of an application.
752     *
753     * @param startMessageId first message identifier as specified in TS 23.041 (3GPP)
754     * or C.R1001-G (3GPP2)
755     * @param endMessageId last message identifier as specified in TS 23.041 (3GPP)
756     * or C.R1001-G (3GPP2)
757     * @return true if successful, false otherwise
758     * @see #disableCellBroadcastRange(int, int)
759     *
760     * @throws IllegalArgumentException if endMessageId < startMessageId
761     * {@hide}
762     */
763    public boolean enableCellBroadcastRange(int startMessageId, int endMessageId) {
764        return enableCellBroadcastRange(getPreferredSmsSubscription(), startMessageId,
765                endMessageId);
766    }
767
768    /**
769     * Enable reception of cell broadcast (SMS-CB) messages with the given
770     * message identifier range on a particular subId.
771     * Note that if two different clients enable the same
772     * message identifier, they must both disable it for the device to stop
773     * receiving those messages. All received messages will be broadcast in an
774     * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
775     * Note: This call is blocking, callers may want to avoid calling it from
776     * the main thread of an application.
777     *
778     * @param startMessageId first message identifier as specified in TS 23.041
779     * @param endMessageId last message identifier as specified in TS 23.041
780     * @return true if successful, false otherwise
781     * @see #disableCellBroadcastRange(int, int)
782     * @throws IllegalArgumentException if endMessageId < startMessageId
783     *
784     */
785    /** @hide */
786    public boolean enableCellBroadcastRange(long subId, int startMessageId,
787            int endMessageId) {
788        boolean success = false;
789
790        if (endMessageId < startMessageId) {
791            throw new IllegalArgumentException("endMessageId < startMessageId");
792        }
793        try {
794            ISms iccISms = getISmsService();
795            if (iccISms != null) {
796                success = iccISms.enableCellBroadcastRange(startMessageId, endMessageId);
797            }
798        } catch (RemoteException ex) {
799            // ignore it
800        }
801
802        return success;
803    }
804
805    /**
806     * Disable reception of cell broadcast (SMS-CB) messages with the given
807     * message identifier range. Note that if two different clients enable the same
808     * message identifier, they must both disable it for the device to stop
809     * receiving those messages.
810     * Note: This call is blocking, callers may want to avoid calling it from
811     * the main thread of an application.
812     *
813     * @param startMessageId first message identifier as specified in TS 23.041 (3GPP)
814     * or C.R1001-G (3GPP2)
815     * @param endMessageId last message identifier as specified in TS 23.041 (3GPP)
816     * or C.R1001-G (3GPP2)
817     * @return true if successful, false otherwise
818     *
819     * @see #enableCellBroadcastRange(int, int)
820     *
821     * @throws IllegalArgumentException if endMessageId < startMessageId
822     * {@hide}
823     */
824    public boolean disableCellBroadcastRange(int startMessageId, int endMessageId) {
825        return disableCellBroadcastRange(getPreferredSmsSubscription(), startMessageId,
826                endMessageId);
827    }
828
829    /**
830     * Disable reception of cdma broadcast messages with the given
831     * message identifier range on a particular subId.
832     * Note that if two different clients enable the same
833     * message identifier range, they must both disable it for the device to stop
834     * receiving those messages.
835     * Note: This call is blocking, callers may want to avoid calling it from
836     * the main thread of an application.
837     *
838     * @param startMessageId first message identifier as specified in TS 23.041
839     * @param endMessageId last message identifier as specified in TS 23.041
840     * @return true if successful, false otherwise
841     *
842     * @see #enableCellBroadcastRange(int, int)
843     * @throws IllegalArgumentException if endMessageId < startMessageId
844     *
845     */
846    /** @hide */
847    public boolean disableCellBroadcastRange(long subId, int startMessageId,
848            int endMessageId) {
849        boolean success = false;
850
851        if (endMessageId < startMessageId) {
852            throw new IllegalArgumentException("endMessageId < startMessageId");
853        }
854        try {
855            ISms iccISms = getISmsService();
856            if (iccISms != null) {
857                success = iccISms.disableCellBroadcastRange(startMessageId, endMessageId);
858            }
859        } catch (RemoteException ex) {
860            // ignore it
861        }
862
863        return success;
864    }
865
866    /**
867     * Create a list of <code>SmsMessage</code>s from a list of RawSmsData
868     * records returned by <code>getAllMessagesFromIcc()</code>
869     *
870     * @param records SMS EF records, returned by
871     *   <code>getAllMessagesFromIcc</code>
872     * @return <code>ArrayList</code> of <code>SmsMessage</code> objects.
873     */
874    private static ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
875        ArrayList<SmsMessage> messages = new ArrayList<SmsMessage>();
876        if (records != null) {
877            int count = records.size();
878            for (int i = 0; i < count; i++) {
879                SmsRawData data = records.get(i);
880                // List contains all records, including "free" records (null)
881                if (data != null) {
882                    SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes());
883                    if (sms != null) {
884                        messages.add(sms);
885                    }
886                }
887            }
888        }
889        return messages;
890    }
891
892    /**
893     * SMS over IMS is supported if IMS is registered and SMS is supported
894     * on IMS.
895     *
896     * @return true if SMS over IMS is supported, false otherwise
897     *
898     * @see #getImsSmsFormat()
899     *
900     * @hide
901     */
902    boolean isImsSmsSupported() {
903        return isImsSmsSupported(getPreferredSmsSubscription());
904    }
905
906    /** @hide */
907    boolean isImsSmsSupported(long subId) {
908        boolean boSupported = false;
909        try {
910            ISms iccISms = getISmsService();
911            if (iccISms != null) {
912                boSupported = iccISms.isImsSmsSupported();
913            }
914        } catch (RemoteException ex) {
915            // ignore it
916        }
917        return boSupported;
918    }
919
920    /**
921     * Gets SMS format supported on IMS.  SMS over IMS format is
922     * either 3GPP or 3GPP2.
923     *
924     * @return SmsMessage.FORMAT_3GPP,
925     *         SmsMessage.FORMAT_3GPP2
926     *      or SmsMessage.FORMAT_UNKNOWN
927     *
928     * @see #isImsSmsSupported()
929     *
930     * @hide
931     */
932    String getImsSmsFormat() {
933        return getImsSmsFormat(getPreferredSmsSubscription());
934    }
935
936    /** @hide */
937    String getImsSmsFormat(long subId) {
938        String format = com.android.internal.telephony.SmsConstants.FORMAT_UNKNOWN;
939        try {
940            ISms iccISms = getISmsService();
941            if (iccISms != null) {
942                format = iccISms.getImsSmsFormat();
943            }
944        } catch (RemoteException ex) {
945            // ignore it
946        }
947        return format;
948    }
949
950    /**
951     * Get the preferred sms subId
952     *
953     * @return the preferred subId
954     * @hide
955     */
956    public static long getPreferredSmsSubscription() {
957        ISms iccISms = null;
958        try {
959            iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
960            return (long) iccISms.getPreferredSmsSubscription();
961        } catch (RemoteException ex) {
962            return DEFAULT_SUB;
963        } catch (NullPointerException ex) {
964            return DEFAULT_SUB;
965        }
966    }
967
968    /**
969     * Get SMS prompt property,  enabled or not
970     *
971     * @return true if enabled, false otherwise
972     * @hide
973     */
974    public boolean isSMSPromptEnabled() {
975        ISms iccISms = null;
976        try {
977            iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
978            return iccISms.isSMSPromptEnabled();
979        } catch (RemoteException ex) {
980            return false;
981        } catch (NullPointerException ex) {
982            return false;
983        }
984    }
985
986    // see SmsMessage.getStatusOnIcc
987
988    /** Free space (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
989    static public final int STATUS_ON_ICC_FREE      = 0;
990
991    /** Received and read (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
992    static public final int STATUS_ON_ICC_READ      = 1;
993
994    /** Received and unread (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
995    static public final int STATUS_ON_ICC_UNREAD    = 3;
996
997    /** Stored and sent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
998    static public final int STATUS_ON_ICC_SENT      = 5;
999
1000    /** Stored and unsent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
1001    static public final int STATUS_ON_ICC_UNSENT    = 7;
1002
1003    // SMS send failure result codes
1004
1005    /** Generic failure cause */
1006    static public final int RESULT_ERROR_GENERIC_FAILURE    = 1;
1007    /** Failed because radio was explicitly turned off */
1008    static public final int RESULT_ERROR_RADIO_OFF          = 2;
1009    /** Failed because no pdu provided */
1010    static public final int RESULT_ERROR_NULL_PDU           = 3;
1011    /** Failed because service is currently unavailable */
1012    static public final int RESULT_ERROR_NO_SERVICE         = 4;
1013    /** Failed because we reached the sending queue limit.  {@hide} */
1014    static public final int RESULT_ERROR_LIMIT_EXCEEDED     = 5;
1015    /** Failed because FDN is enabled. {@hide} */
1016    static public final int RESULT_ERROR_FDN_CHECK_FAILURE  = 6;
1017
1018    /**
1019     * Send an MMS message
1020     *
1021     * @param pdu the MMS message encoded in standard MMS PDU format
1022     * @param locationUrl the optional location url where message should be sent to
1023     * @param sentIntent if not NULL this <code>PendingIntent</code> is
1024     *  broadcast when the message is successfully sent, or failed
1025     * @throws IllegalArgumentException if pdu is empty
1026     */
1027    public void sendMultimediaMessage(byte[] pdu, String locationUrl, PendingIntent sentIntent) {
1028        sendMultimediaMessage(getPreferredSmsSubscription(), pdu, locationUrl, sentIntent);
1029    }
1030
1031    /**
1032     * Send an MMS message
1033     *
1034     * @param subId the SIM id
1035     * @param pdu the MMS message encoded in standard MMS PDU format
1036     * @param locationUrl the optional location url where message should be sent to
1037     * @param sentIntent if not NULL this <code>PendingIntent</code> is
1038     *  broadcast when the message is successfully sent, or failed
1039     * @throws IllegalArgumentException if pdu is empty
1040     */
1041    public void sendMultimediaMessage(long subId, byte[] pdu, String locationUrl,
1042            PendingIntent sentIntent) {
1043        if (pdu == null || pdu.length == 0) {
1044            throw new IllegalArgumentException("Empty or zero length PDU");
1045        }
1046        try {
1047            final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
1048            if (iMms == null) {
1049                return;
1050            }
1051            iMms.sendMessage(subId, ActivityThread.currentPackageName(), pdu, locationUrl,
1052                    sentIntent);
1053        } catch (RemoteException e) {
1054            // Ignore it
1055        }
1056    }
1057
1058    /**
1059     * Download an MMS message from carrier by a given location URL
1060     *
1061     * @param locationUrl the location URL of the MMS message to be downloaded, usually obtained
1062     *  from the MMS WAP push notification
1063     * @param downloadedIntent if not NULL this <code>PendingIntent</code> is
1064     *  broadcast when the message is downloaded, or the download is failed
1065     * @throws IllegalArgumentException if locationUrl is empty
1066     */
1067    public void downloadMultimediaMessage(String locationUrl, PendingIntent downloadedIntent) {
1068        downloadMultimediaMessage(getPreferredSmsSubscription(), locationUrl, downloadedIntent);
1069    }
1070
1071    /**
1072     * Download an MMS message from carrier by a given location URL
1073     *
1074     * @param subId the SIM id
1075     * @param locationUrl the location URL of the MMS message to be downloaded, usually obtained
1076     *  from the MMS WAP push notification
1077     * @param downloadedIntent if not NULL this <code>PendingIntent</code> is
1078     *  broadcast when the message is downloaded, or the download is failed
1079     * @throws IllegalArgumentException if locationUrl is empty
1080     */
1081    public void downloadMultimediaMessage(long subId, String locationUrl,
1082            PendingIntent downloadedIntent) {
1083        if (TextUtils.isEmpty(locationUrl)) {
1084            throw new IllegalArgumentException("Empty MMS location URL");
1085        }
1086        try {
1087            final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
1088            if (iMms == null) {
1089                return;
1090            }
1091            iMms.downloadMessage(subId, ActivityThread.currentPackageName(), locationUrl,
1092                    downloadedIntent);
1093        } catch (RemoteException e) {
1094            // Ignore it
1095        }
1096    }
1097
1098    // MMS send/download failure result codes
1099    public static final int MMS_ERROR_UNSPECIFIED = 1;
1100    public static final int MMS_ERROR_INVALID_APN = 2;
1101    public static final int MMS_ERROR_UNABLE_CONNECT_MMS = 3;
1102    public static final int MMS_ERROR_HTTP_FAILURE = 4;
1103
1104    // Intent extra name for result data
1105    public static final String MMS_EXTRA_DATA = "data";
1106
1107    /**
1108     * Update the status of a pending (send-by-IP) MMS message handled by the carrier app.
1109     * If the carrier app fails to send this message, it would be resent via carrier network.
1110     *
1111     * The caller should have carrier privileges.
1112     * @see android.telephony.TelephonyManager.hasCarrierPrivileges
1113     *
1114     * @param messageRef the reference number of the MMS message.
1115     * @param success True if and only if the message was sent successfully. If its value is
1116     *  false, this message should be resent via carrier network
1117     */
1118    public void updateMmsSendStatus(int messageRef, boolean success) {
1119        try {
1120            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
1121            if (iMms == null) {
1122                return;
1123            }
1124            iMms.updateMmsSendStatus(messageRef, success);
1125        } catch (RemoteException ex) {
1126            // ignore it
1127        }
1128    }
1129
1130    /**
1131     * Update the status of a pending (download-by-IP) MMS message handled by the carrier app.
1132     * If the carrier app fails to download this message, it would be re-downloaded via carrier
1133     * network.
1134     *
1135     * The caller should have carrier privileges.
1136     * @see android.telephony.TelephonyManager.hasCarrierPrivileges
1137     *
1138     * @param messageRef the reference number of the MMS message.
1139     * @param pdu non-empty if downloaded successfully, otherwise, it is empty and the message
1140     *  will be downloaded via carrier network
1141     */
1142    public void updateMmsDownloadStatus(int messageRef, byte[] pdu) {
1143        try {
1144            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
1145            if (iMms == null) {
1146                return;
1147            }
1148            iMms.updateMmsDownloadStatus(messageRef, pdu);
1149        } catch (RemoteException ex) {
1150            // ignore it
1151        }
1152    }
1153
1154    /**
1155     * Import a text message into system's SMS store
1156     *
1157     * Only default SMS apps can import SMS
1158     *
1159     * @param address the destination(source) address of the sent(received) message
1160     * @param type the type of the message
1161     * @param text the message text
1162     * @param timestampMillis the message timestamp in milliseconds
1163     * @param seen if the message is seen
1164     * @param read if the message is read
1165     * @return the message URI, null if failed
1166     */
1167    public Uri importTextMessage(String address, int type, String text, long timestampMillis,
1168            boolean seen, boolean read) {
1169        try {
1170            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
1171            if (iMms != null) {
1172                return iMms.importTextMessage(ActivityThread.currentPackageName(),
1173                        address, type, text, timestampMillis, seen, read);
1174            }
1175        } catch (RemoteException ex) {
1176            // ignore it
1177        }
1178        return null;
1179    }
1180
1181    /** Represents the received SMS message for importing */
1182    public static final int SMS_TYPE_INCOMING = 0;
1183    /** Represents the sent SMS message for importing */
1184    public static final int SMS_TYPE_OUTGOING = 1;
1185
1186    /**
1187     * Import a multimedia message into system's MMS store. Only the following PDU type is
1188     * supported: Retrieve.conf, Send.req, Notification.ind, Delivery.ind, Read-Orig.ind
1189     *
1190     * Only default SMS apps can import MMS
1191     *
1192     * @param pdu the PDU of the message to import
1193     * @param messageId the optional message id. Use null if not specifying
1194     * @param timestampSecs the optional message timestamp. Use -1 if not specifying
1195     * @param seen if the message is seen
1196     * @param read if the message is read
1197     * @return the message URI, null if failed
1198     * @throws IllegalArgumentException if pdu is empty
1199     */
1200    public Uri importMultimediaMessage(byte[] pdu, String messageId, long timestampSecs,
1201            boolean seen, boolean read) {
1202        if (pdu == null || pdu.length == 0) {
1203            throw new IllegalArgumentException("Empty or zero length PDU");
1204        }
1205        try {
1206            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
1207            if (iMms != null) {
1208                return iMms.importMultimediaMessage(ActivityThread.currentPackageName(),
1209                        pdu, messageId, timestampSecs, seen, read);
1210            }
1211        } catch (RemoteException ex) {
1212            // ignore it
1213        }
1214        return null;
1215    }
1216
1217    /**
1218     * Delete a system stored SMS or MMS message
1219     *
1220     * Only default SMS apps can delete system stored SMS and MMS messages
1221     *
1222     * @param messageUri the URI of the stored message
1223     * @return true if deletion is successful, false otherwise
1224     * @throws IllegalArgumentException if messageUri is empty
1225     */
1226    public boolean deleteStoredMessage(Uri messageUri) {
1227        if (messageUri == null) {
1228            throw new IllegalArgumentException("Empty message URI");
1229        }
1230        try {
1231            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
1232            if (iMms != null) {
1233                return iMms.deleteStoredMessage(ActivityThread.currentPackageName(), messageUri);
1234            }
1235        } catch (RemoteException ex) {
1236            // ignore it
1237        }
1238        return false;
1239    }
1240
1241    /**
1242     * Delete a system stored SMS or MMS thread
1243     *
1244     * Only default SMS apps can delete system stored SMS and MMS conversations
1245     *
1246     * @param conversationId the ID of the message conversation
1247     * @return true if deletion is successful, false otherwise
1248     */
1249    public boolean deleteStoredConversation(long conversationId) {
1250        try {
1251            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
1252            if (iMms != null) {
1253                return iMms.deleteStoredConversation(
1254                        ActivityThread.currentPackageName(), conversationId);
1255            }
1256        } catch (RemoteException ex) {
1257            // ignore it
1258        }
1259        return false;
1260    }
1261
1262    /**
1263     * Update the status properties of a system stored SMS or MMS message, e.g.
1264     * the read status of a message, etc.
1265     *
1266     * @param messageUri the URI of the stored message
1267     * @param statusValues a list of status properties in key-value pairs to update
1268     * @return true if update is successful, false otherwise
1269     * @throws IllegalArgumentException if messageUri is empty
1270     */
1271    public boolean updateStoredMessageStatus(Uri messageUri, ContentValues statusValues) {
1272        if (messageUri == null) {
1273            throw new IllegalArgumentException("Empty message URI");
1274        }
1275        try {
1276            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
1277            if (iMms != null) {
1278                return iMms.updateStoredMessageStatus(ActivityThread.currentPackageName(),
1279                        messageUri, statusValues);
1280            }
1281        } catch (RemoteException ex) {
1282            // ignore it
1283        }
1284        return false;
1285    }
1286
1287    /** Message status property: whether the message has been seen. 1 means seen, 0 not*/
1288    public static final String MESSAGE_STATUS_SEEN = "seen";
1289    /** Message status property: whether the message has been read. 1 means read, 0 not*/
1290    public static final String MESSAGE_STATUS_READ = "read";
1291
1292    /**
1293     * Archive or unarchive a stored conversation
1294     *
1295     * @param conversationId the ID of the message conversation
1296     * @param archived true to archive the conversation, false to unarchive
1297     * @return true if update is successful, false otherwise
1298     */
1299    public boolean archiveStoredConversation(long conversationId, boolean archived) {
1300        try {
1301            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
1302            if (iMms != null) {
1303                return iMms.archiveStoredConversation(ActivityThread.currentPackageName(),
1304                        conversationId, archived);
1305            }
1306        } catch (RemoteException ex) {
1307            // ignore it
1308        }
1309        return false;
1310    }
1311
1312    /**
1313     * Add a text message draft to system SMS store
1314     *
1315     * Only default SMS apps can add SMS draft
1316     *
1317     * @param address the destination address of message
1318     * @param text the body of the message to send
1319     * @return the URI of the stored draft message
1320     */
1321    public Uri addTextMessageDraft(String address, String text) {
1322        try {
1323            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
1324            if (iMms != null) {
1325                return iMms.addTextMessageDraft(ActivityThread.currentPackageName(), address, text);
1326            }
1327        } catch (RemoteException ex) {
1328            // ignore it
1329        }
1330        return null;
1331    }
1332
1333    /**
1334     * Add a multimedia message draft to system MMS store
1335     *
1336     * Only default SMS apps can add MMS draft
1337     *
1338     * @param pdu the PDU data of the draft MMS
1339     * @return the URI of the stored draft message
1340     * @throws IllegalArgumentException if pdu is empty
1341     */
1342    public Uri addMultimediaMessageDraft(byte[] pdu) {
1343        if (pdu == null || pdu.length == 0) {
1344            throw new IllegalArgumentException("Empty or zero length PDU");
1345        }
1346        try {
1347            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
1348            if (iMms != null) {
1349                return iMms.addMultimediaMessageDraft(ActivityThread.currentPackageName(), pdu);
1350            }
1351        } catch (RemoteException ex) {
1352            // ignore it
1353        }
1354        return null;
1355    }
1356
1357    /**
1358     * Send a system stored text message.
1359     *
1360     * You can only send a failed text message or a draft text message.
1361     *
1362     * @param messageUri the URI of the stored message
1363     * @param scAddress is the service center address or null to use the current default SMSC
1364     * @param sentIntent if not NULL this <code>PendingIntent</code> is
1365     *  broadcast when the message is successfully sent, or failed.
1366     *  The result code will be <code>Activity.RESULT_OK</code> for success,
1367     *  or one of these errors:<br>
1368     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
1369     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
1370     *  <code>RESULT_ERROR_NULL_PDU</code><br>
1371     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
1372     *  the extra "errorCode" containing a radio technology specific value,
1373     *  generally only useful for troubleshooting.<br>
1374     *  The per-application based SMS control checks sentIntent. If sentIntent
1375     *  is NULL the caller will be checked against all unknown applications,
1376     *  which cause smaller number of SMS to be sent in checking period.
1377     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
1378     *  broadcast when the message is delivered to the recipient.  The
1379     *  raw pdu of the status report is in the extended data ("pdu").
1380     *
1381     * @throws IllegalArgumentException if messageUri is empty
1382     */
1383    public void sendStoredTextMessage(Uri messageUri, String scAddress, PendingIntent sentIntent,
1384            PendingIntent deliveryIntent) {
1385        sendStoredTextMessage(getPreferredSmsSubscription(), messageUri, scAddress, sentIntent,
1386                deliveryIntent);
1387    }
1388
1389    /**
1390     * Send a system stored text message.
1391     *
1392     * You can only send a failed text message or a draft text message.
1393     *
1394     * @param subId the SIM id
1395     * @param messageUri the URI of the stored message
1396     * @param scAddress is the service center address or null to use the current default SMSC
1397     * @param sentIntent if not NULL this <code>PendingIntent</code> is
1398     *  broadcast when the message is successfully sent, or failed.
1399     *  The result code will be <code>Activity.RESULT_OK</code> for success,
1400     *  or one of these errors:<br>
1401     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
1402     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
1403     *  <code>RESULT_ERROR_NULL_PDU</code><br>
1404     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
1405     *  the extra "errorCode" containing a radio technology specific value,
1406     *  generally only useful for troubleshooting.<br>
1407     *  The per-application based SMS control checks sentIntent. If sentIntent
1408     *  is NULL the caller will be checked against all unknown applications,
1409     *  which cause smaller number of SMS to be sent in checking period.
1410     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
1411     *  broadcast when the message is delivered to the recipient.  The
1412     *  raw pdu of the status report is in the extended data ("pdu").
1413     *
1414     * @throws IllegalArgumentException if messageUri is empty
1415     */
1416    public void sendStoredTextMessage(long subId, Uri messageUri, String scAddress,
1417            PendingIntent sentIntent, PendingIntent deliveryIntent) {
1418        if (messageUri == null) {
1419            throw new IllegalArgumentException("Empty message URI");
1420        }
1421        try {
1422            ISms iccISms = getISmsServiceOrThrow();
1423            iccISms.sendStoredText(subId, ActivityThread.currentPackageName(), messageUri,
1424                    scAddress, sentIntent, deliveryIntent);
1425        } catch (RemoteException ex) {
1426            // ignore it
1427        }
1428    }
1429
1430    /**
1431     * Send a system stored multi-part text message.
1432     *
1433     * You can only send a failed text message or a draft text message.
1434     * The provided <code>PendingIntent</code> lists should match the part number of the
1435     * divided text of the stored message by using <code>divideMessage</code>
1436     *
1437     * @param messageUri the URI of the stored message
1438     * @param scAddress is the service center address or null to use
1439     *   the current default SMSC
1440     * @param sentIntents if not null, an <code>ArrayList</code> of
1441     *   <code>PendingIntent</code>s (one for each message part) that is
1442     *   broadcast when the corresponding message part has been sent.
1443     *   The result code will be <code>Activity.RESULT_OK</code> for success,
1444     *   or one of these errors:<br>
1445     *   <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
1446     *   <code>RESULT_ERROR_RADIO_OFF</code><br>
1447     *   <code>RESULT_ERROR_NULL_PDU</code><br>
1448     *   For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include
1449     *   the extra "errorCode" containing a radio technology specific value,
1450     *   generally only useful for troubleshooting.<br>
1451     *   The per-application based SMS control checks sentIntent. If sentIntent
1452     *   is NULL the caller will be checked against all unknown applications,
1453     *   which cause smaller number of SMS to be sent in checking period.
1454     * @param deliveryIntents if not null, an <code>ArrayList</code> of
1455     *   <code>PendingIntent</code>s (one for each message part) that is
1456     *   broadcast when the corresponding message part has been delivered
1457     *   to the recipient.  The raw pdu of the status report is in the
1458     *   extended data ("pdu").
1459     *
1460     * @throws IllegalArgumentException if messageUri is empty
1461     */
1462    public void sendStoredMultipartTextMessage(Uri messageUri, String scAddress,
1463            ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
1464        sendStoredMultipartTextMessage(getPreferredSmsSubscription(), messageUri, scAddress,
1465                sentIntents, deliveryIntents);
1466    }
1467
1468    /**
1469     * Send a system stored multi-part text message.
1470     *
1471     * This is used for sending a previously sent, but failed-to-send, message or
1472     * for sending a text message that has been stored as a draft.
1473     * The provided <code>PendingIntent</code> lists should match the part number of the
1474     * divided text of the stored message by using <code>divideMessage</code>
1475     *
1476     * @param subId the SIM id
1477     * @param messageUri the URI of the stored message
1478     * @param scAddress is the service center address or null to use
1479     *   the current default SMSC
1480     * @param sentIntents if not null, an <code>ArrayList</code> of
1481     *   <code>PendingIntent</code>s (one for each message part) that is
1482     *   broadcast when the corresponding message part has been sent.
1483     *   The result code will be <code>Activity.RESULT_OK</code> for success,
1484     *   or one of these errors:<br>
1485     *   <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
1486     *   <code>RESULT_ERROR_RADIO_OFF</code><br>
1487     *   <code>RESULT_ERROR_NULL_PDU</code><br>
1488     *   For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include
1489     *   the extra "errorCode" containing a radio technology specific value,
1490     *   generally only useful for troubleshooting.<br>
1491     *   The per-application based SMS control checks sentIntent. If sentIntent
1492     *   is NULL the caller will be checked against all unknown applications,
1493     *   which cause smaller number of SMS to be sent in checking period.
1494     * @param deliveryIntents if not null, an <code>ArrayList</code> of
1495     *   <code>PendingIntent</code>s (one for each message part) that is
1496     *   broadcast when the corresponding message part has been delivered
1497     *   to the recipient.  The raw pdu of the status report is in the
1498     *   extended data ("pdu").
1499     *
1500     * @throws IllegalArgumentException if messageUri is empty
1501     */
1502    public void sendStoredMultipartTextMessage(long subId, Uri messageUri, String scAddress,
1503            ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
1504        if (messageUri == null) {
1505            throw new IllegalArgumentException("Empty message URI");
1506        }
1507        try {
1508            ISms iccISms = getISmsServiceOrThrow();
1509            iccISms.sendStoredMultipartText(subId, ActivityThread.currentPackageName(), messageUri,
1510                    scAddress, sentIntents, deliveryIntents);
1511        } catch (RemoteException ex) {
1512            // ignore it
1513        }
1514    }
1515
1516    /**
1517     * Send a system stored MMS message
1518     *
1519     * This is used for sending a previously sent, but failed-to-send, message or
1520     * for sending a text message that has been stored as a draft.
1521     *
1522     * @param messageUri the URI of the stored message
1523     * @param sentIntent if not NULL this <code>PendingIntent</code> is
1524     *  broadcast when the message is successfully sent, or failed
1525     * @throws IllegalArgumentException if messageUri is empty
1526     */
1527    public void sendStoredMultimediaMessage(Uri messageUri, PendingIntent sentIntent) {
1528        sendStoredMultimediaMessage(getPreferredSmsSubscription(), messageUri, sentIntent);
1529    }
1530
1531    /**
1532     * Send a system stored MMS message
1533     *
1534     * This is used for sending a previously sent, but failed-to-send, message or
1535     * for sending a text message that has been stored as a draft.
1536     *
1537     * @param subId the SIM id
1538     * @param messageUri the URI of the stored message
1539     * @param sentIntent if not NULL this <code>PendingIntent</code> is
1540     *  broadcast when the message is successfully sent, or failed
1541     * @throws IllegalArgumentException if messageUri is empty
1542     */
1543    public void sendStoredMultimediaMessage(long subId, Uri messageUri, PendingIntent sentIntent) {
1544        if (messageUri == null) {
1545            throw new IllegalArgumentException("Empty message URI");
1546        }
1547        try {
1548            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
1549            if (iMms != null) {
1550                iMms.sendStoredMessage(subId, ActivityThread.currentPackageName(), messageUri,
1551                        sentIntent);
1552            }
1553        } catch (RemoteException ex) {
1554            // ignore it
1555        }
1556    }
1557
1558    /**
1559     * Turns on/off the flag to automatically write sent/received SMS/MMS messages into system
1560     *
1561     * When this flag is on, all SMS/MMS sent/received are stored by system automatically
1562     * When this flag is off, only SMS/MMS sent by non-default SMS apps are stored by system
1563     * automatically
1564     *
1565     * This flag can only be changed by default SMS apps
1566     *
1567     * @param enabled Whether to enable message auto persisting
1568     */
1569    public void setAutoPersisting(boolean enabled) {
1570        try {
1571            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
1572            if (iMms != null) {
1573                iMms.setAutoPersisting(ActivityThread.currentPackageName(), enabled);
1574            }
1575        } catch (RemoteException ex) {
1576            // ignore it
1577        }
1578    }
1579
1580    /**
1581     * Get the value of the flag to automatically write sent/received SMS/MMS messages into system
1582     *
1583     * When this flag is on, all SMS/MMS sent/received are stored by system automatically
1584     * When this flag is off, only SMS/MMS sent by non-default SMS apps are stored by system
1585     * automatically
1586     *
1587     * @return the current value of the auto persist flag
1588     */
1589    public boolean getAutoPersisting() {
1590        try {
1591            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
1592            if (iMms != null) {
1593                return iMms.getAutoPersisting();
1594            }
1595        } catch (RemoteException ex) {
1596            // ignore it
1597        }
1598        return false;
1599    }
1600}
1601