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