/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.telephony; import android.app.ActivityThread; import android.app.PendingIntent; import android.content.ActivityNotFoundException; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.BaseBundle; import android.os.Bundle; import android.os.RemoteException; import android.os.ServiceManager; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import com.android.internal.telephony.IMms; import com.android.internal.telephony.ISms; import com.android.internal.telephony.SmsRawData; import com.android.internal.telephony.uicc.IccConstants; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; /* * TODO(code review): Curious question... Why are a lot of these * methods not declared as static, since they do not seem to require * any local object state? Presumably this cannot be changed without * interfering with the API... */ /** * Manages SMS operations such as sending data, text, and pdu SMS messages. * Get this object by calling the static method {@link #getDefault()}. * *

For information about how to behave as the default SMS app on Android 4.4 (API level 19) * and higher, see {@link android.provider.Telephony}. */ public final class SmsManager { private static final String TAG = "SmsManager"; /** * A psuedo-subId that represents the default subId at any given time. The actual subId it * represents changes as the default subId is changed. */ private static final int DEFAULT_SUBSCRIPTION_ID = -1002; /** Singleton object constructed during class initialization. */ private static final SmsManager sInstance = new SmsManager(DEFAULT_SUBSCRIPTION_ID); private static final Object sLockObject = new Object(); /** @hide */ public static final int CELL_BROADCAST_RAN_TYPE_GSM = 0; /** @hide */ public static final int CELL_BROADCAST_RAN_TYPE_CDMA = 1; private static final Map sSubInstances = new ArrayMap(); /** A concrete subscription id, or the pseudo DEFAULT_SUBSCRIPTION_ID */ private int mSubId; /* * Key for the various carrier-dependent configuration values. * Some of the values are used by the system in processing SMS or MMS messages. Others * are provided for the convenience of SMS applications. */ /** * Whether to append transaction id to MMS WAP Push M-Notification.ind's content location URI * when constructing the download URL of a new MMS (boolean type) */ public static final String MMS_CONFIG_APPEND_TRANSACTION_ID = CarrierConfigManager.KEY_MMS_APPEND_TRANSACTION_ID_BOOL; /** * Whether MMS is enabled for the current carrier (boolean type) */ public static final String MMS_CONFIG_MMS_ENABLED = CarrierConfigManager.KEY_MMS_MMS_ENABLED_BOOL; /** * Whether group MMS is enabled for the current carrier (boolean type) */ public static final String MMS_CONFIG_GROUP_MMS_ENABLED = CarrierConfigManager.KEY_MMS_GROUP_MMS_ENABLED_BOOL; /** * If this is enabled, M-NotifyResp.ind should be sent to the WAP Push content location instead * of the default MMSC (boolean type) */ public static final String MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED = CarrierConfigManager.KEY_MMS_NOTIFY_WAP_MMSC_ENABLED_BOOL; /** * Whether alias is enabled (boolean type) */ public static final String MMS_CONFIG_ALIAS_ENABLED = CarrierConfigManager.KEY_MMS_ALIAS_ENABLED_BOOL; /** * Whether audio is allowed to be attached for MMS messages (boolean type) */ public static final String MMS_CONFIG_ALLOW_ATTACH_AUDIO = CarrierConfigManager.KEY_MMS_ALLOW_ATTACH_AUDIO_BOOL; /** * Whether multipart SMS is enabled (boolean type) */ public static final String MMS_CONFIG_MULTIPART_SMS_ENABLED = CarrierConfigManager.KEY_MMS_MULTIPART_SMS_ENABLED_BOOL; /** * Whether SMS delivery report is enabled (boolean type) */ public static final String MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED = CarrierConfigManager.KEY_MMS_SMS_DELIVERY_REPORT_ENABLED_BOOL; /** * Whether content-disposition field should be expected in an MMS PDU (boolean type) */ public static final String MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION = CarrierConfigManager.KEY_MMS_SUPPORT_MMS_CONTENT_DISPOSITION_BOOL; /** * Whether multipart SMS should be sent as separate messages */ public static final String MMS_CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES = CarrierConfigManager.KEY_MMS_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES_BOOL; /** * Whether MMS read report is enabled (boolean type) */ public static final String MMS_CONFIG_MMS_READ_REPORT_ENABLED = CarrierConfigManager.KEY_MMS_MMS_READ_REPORT_ENABLED_BOOL; /** * Whether MMS delivery report is enabled (boolean type) */ public static final String MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED = CarrierConfigManager.KEY_MMS_MMS_DELIVERY_REPORT_ENABLED_BOOL; /** * Max MMS message size in bytes (int type) */ public static final String MMS_CONFIG_MAX_MESSAGE_SIZE = CarrierConfigManager.KEY_MMS_MAX_MESSAGE_SIZE_INT; /** * Max MMS image width (int type) */ public static final String MMS_CONFIG_MAX_IMAGE_WIDTH = CarrierConfigManager.KEY_MMS_MAX_IMAGE_WIDTH_INT; /** * Max MMS image height (int type) */ public static final String MMS_CONFIG_MAX_IMAGE_HEIGHT = CarrierConfigManager.KEY_MMS_MAX_IMAGE_HEIGHT_INT; /** * Limit of recipients of MMS messages (int type) */ public static final String MMS_CONFIG_RECIPIENT_LIMIT = CarrierConfigManager.KEY_MMS_RECIPIENT_LIMIT_INT; /** * Min alias character count (int type) */ public static final String MMS_CONFIG_ALIAS_MIN_CHARS = CarrierConfigManager.KEY_MMS_ALIAS_MIN_CHARS_INT; /** * Max alias character count (int type) */ public static final String MMS_CONFIG_ALIAS_MAX_CHARS = CarrierConfigManager.KEY_MMS_ALIAS_MAX_CHARS_INT; /** * When the number of parts of a multipart SMS reaches this threshold, it should be converted * into an MMS (int type) */ public static final String MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD = CarrierConfigManager.KEY_MMS_SMS_TO_MMS_TEXT_THRESHOLD_INT; /** * Some carriers require SMS to be converted into MMS when text length reaches this threshold * (int type) */ public static final String MMS_CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD = CarrierConfigManager.KEY_MMS_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD_INT; /** * Max message text size (int type) */ public static final String MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE = CarrierConfigManager.KEY_MMS_MESSAGE_TEXT_MAX_SIZE_INT; /** * Max message subject length (int type) */ public static final String MMS_CONFIG_SUBJECT_MAX_LENGTH = CarrierConfigManager.KEY_MMS_SUBJECT_MAX_LENGTH_INT; /** * MMS HTTP socket timeout in milliseconds (int type) */ public static final String MMS_CONFIG_HTTP_SOCKET_TIMEOUT = CarrierConfigManager.KEY_MMS_HTTP_SOCKET_TIMEOUT_INT; /** * The name of the UA Prof URL HTTP header for MMS HTTP request (String type) */ public static final String MMS_CONFIG_UA_PROF_TAG_NAME = CarrierConfigManager.KEY_MMS_UA_PROF_TAG_NAME_STRING; /** * The User-Agent header value for MMS HTTP request (String type) */ public static final String MMS_CONFIG_USER_AGENT = CarrierConfigManager.KEY_MMS_USER_AGENT_STRING; /** * The UA Profile URL header value for MMS HTTP request (String type) */ public static final String MMS_CONFIG_UA_PROF_URL = CarrierConfigManager.KEY_MMS_UA_PROF_URL_STRING; /** * A list of HTTP headers to add to MMS HTTP request, separated by "|" (String type) */ public static final String MMS_CONFIG_HTTP_PARAMS = CarrierConfigManager.KEY_MMS_HTTP_PARAMS_STRING; /** * Email gateway number (String type) */ public static final String MMS_CONFIG_EMAIL_GATEWAY_NUMBER = CarrierConfigManager.KEY_MMS_EMAIL_GATEWAY_NUMBER_STRING; /** * The suffix to append to the NAI header value for MMS HTTP request (String type) */ public static final String MMS_CONFIG_NAI_SUFFIX = CarrierConfigManager.KEY_MMS_NAI_SUFFIX_STRING; /** * If true, show the cell broadcast (amber alert) in the SMS settings. Some carriers don't want * this shown. (Boolean type) */ public static final String MMS_CONFIG_SHOW_CELL_BROADCAST_APP_LINKS = CarrierConfigManager.KEY_MMS_SHOW_CELL_BROADCAST_APP_LINKS_BOOL; /** * Whether the carrier MMSC supports charset field in Content-Type header. If this is false, * then we don't add "charset" to "Content-Type" */ public static final String MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER = CarrierConfigManager.KEY_MMS_SUPPORT_HTTP_CHARSET_HEADER_BOOL; /** * If true, add "Connection: close" header to MMS HTTP requests so the connection * is immediately closed (disabling keep-alive). (Boolean type) * @hide */ public static final String MMS_CONFIG_CLOSE_CONNECTION = CarrierConfigManager.KEY_MMS_CLOSE_CONNECTION_BOOL; /* * Forwarded constants from SimDialogActivity. */ private static String DIALOG_TYPE_KEY = "dialog_type"; private static final int SMS_PICK = 2; /** * Send a text based SMS. * *

Note: Using this method requires that your app has the * {@link android.Manifest.permission#SEND_SMS} permission.

* *

Note: Beginning with Android 4.4 (API level 19), if * and only if an app is not selected as the default SMS app, the system automatically * writes messages sent using this method to the SMS Provider (the default SMS app is always * responsible for writing its sent messages to the SMS Provider). For information about * how to behave as the default SMS app, see {@link android.provider.Telephony}.

* * * @param destinationAddress the address to send the message to * @param scAddress is the service center address or null to use * the current default SMSC * @param text the body of the message to send * @param sentIntent if not NULL this PendingIntent is * broadcast when the message is successfully sent, or failed. * The result code will be Activity.RESULT_OK for success, * or one of these errors:
* RESULT_ERROR_GENERIC_FAILURE
* RESULT_ERROR_RADIO_OFF
* RESULT_ERROR_NULL_PDU
* For RESULT_ERROR_GENERIC_FAILURE the sentIntent may include * the extra "errorCode" containing a radio technology specific value, * generally only useful for troubleshooting.
* The per-application based SMS control checks sentIntent. If sentIntent * is NULL the caller will be checked against all unknown applications, * which cause smaller number of SMS to be sent in checking period. * @param deliveryIntent if not NULL this PendingIntent is * broadcast when the message is delivered to the recipient. The * raw pdu of the status report is in the extended data ("pdu"). * * @throws IllegalArgumentException if destinationAddress or text are empty */ public void sendTextMessage( String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) { sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent, true /* persistMessageForCarrierApp*/); } private void sendTextMessageInternal(String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessageForCarrierApp) { if (TextUtils.isEmpty(destinationAddress)) { throw new IllegalArgumentException("Invalid destinationAddress"); } if (TextUtils.isEmpty(text)) { throw new IllegalArgumentException("Invalid message body"); } try { ISms iccISms = getISmsServiceOrThrow(); iccISms.sendTextForSubscriber(getSubscriptionId(), ActivityThread.currentPackageName(), destinationAddress, scAddress, text, sentIntent, deliveryIntent, persistMessageForCarrierApp); } catch (RemoteException ex) { // ignore it } } /** * Send a text based SMS without writing it into the SMS Provider. * *

Only the carrier app can call this method.

* * @see #sendTextMessage(String, String, String, PendingIntent, PendingIntent) * @hide */ public void sendTextMessageWithoutPersisting( String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) { sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent, false /* persistMessageForCarrierApp*/); } /** * A variant of {@link SmsManager#sendTextMessage} that allows self to be the caller. This is * for internal use only. * * @param persistMessage whether to persist the sent message in the SMS app. the caller must be * the Phone process if set to false. * * @hide */ public void sendTextMessageWithSelfPermissions( String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessage) { if (TextUtils.isEmpty(destinationAddress)) { throw new IllegalArgumentException("Invalid destinationAddress"); } if (TextUtils.isEmpty(text)) { throw new IllegalArgumentException("Invalid message body"); } try { ISms iccISms = getISmsServiceOrThrow(); iccISms.sendTextForSubscriberWithSelfPermissions(getSubscriptionId(), ActivityThread.currentPackageName(), destinationAddress, scAddress, text, sentIntent, deliveryIntent, persistMessage); } catch (RemoteException ex) { // ignore it } } /** * Inject an SMS PDU into the android application framework. * * The caller should have carrier privileges. * @see android.telephony.TelephonyManager#hasCarrierPrivileges * * @param pdu is the byte array of pdu to be injected into android application framework * @param format is the format of SMS pdu (3gpp or 3gpp2) * @param receivedIntent if not NULL this PendingIntent is * broadcast when the message is successfully received by the * android application framework, or failed. This intent is broadcasted at * the same time an SMS received from radio is acknowledged back. * The result code will be RESULT_SMS_HANDLED for success, or * RESULT_SMS_GENERIC_ERROR for error. * * @throws IllegalArgumentException if format is not one of 3gpp and 3gpp2. */ public void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) { if (!format.equals(SmsMessage.FORMAT_3GPP) && !format.equals(SmsMessage.FORMAT_3GPP2)) { // Format must be either 3gpp or 3gpp2. throw new IllegalArgumentException( "Invalid pdu format. format must be either 3gpp or 3gpp2"); } try { ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); if (iccISms != null) { iccISms.injectSmsPduForSubscriber( getSubscriptionId(), pdu, format, receivedIntent); } } catch (RemoteException ex) { // ignore it } } /** * Divide a message text into several fragments, none bigger than * the maximum SMS message size. * * @param text the original message. Must not be null. * @return an ArrayList of strings that, in order, * comprise the original message * * @throws IllegalArgumentException if text is null */ public ArrayList divideMessage(String text) { if (null == text) { throw new IllegalArgumentException("text is null"); } return SmsMessage.fragmentText(text); } /** * Send a multi-part text based SMS. The callee should have already * divided the message into correctly sized parts by calling * divideMessage. * *

Note: Using this method requires that your app has the * {@link android.Manifest.permission#SEND_SMS} permission.

* *

Note: Beginning with Android 4.4 (API level 19), if * and only if an app is not selected as the default SMS app, the system automatically * writes messages sent using this method to the SMS Provider (the default SMS app is always * responsible for writing its sent messages to the SMS Provider). For information about * how to behave as the default SMS app, see {@link android.provider.Telephony}.

* * @param destinationAddress the address to send the message to * @param scAddress is the service center address or null to use * the current default SMSC * @param parts an ArrayList of strings that, in order, * comprise the original message * @param sentIntents if not null, an ArrayList of * PendingIntents (one for each message part) that is * broadcast when the corresponding message part has been sent. * The result code will be Activity.RESULT_OK for success, * or one of these errors:
* RESULT_ERROR_GENERIC_FAILURE
* RESULT_ERROR_RADIO_OFF
* RESULT_ERROR_NULL_PDU
* For RESULT_ERROR_GENERIC_FAILURE each sentIntent may include * the extra "errorCode" containing a radio technology specific value, * generally only useful for troubleshooting.
* The per-application based SMS control checks sentIntent. If sentIntent * is NULL the caller will be checked against all unknown applications, * which cause smaller number of SMS to be sent in checking period. * @param deliveryIntents if not null, an ArrayList of * PendingIntents (one for each message part) that is * broadcast when the corresponding message part has been delivered * to the recipient. The raw pdu of the status report is in the * extended data ("pdu"). * * @throws IllegalArgumentException if destinationAddress or data are empty */ public void sendMultipartTextMessage( String destinationAddress, String scAddress, ArrayList parts, ArrayList sentIntents, ArrayList deliveryIntents) { sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents, deliveryIntents, true /* persistMessageForCarrierApp*/); } private void sendMultipartTextMessageInternal( String destinationAddress, String scAddress, ArrayList parts, ArrayList sentIntents, ArrayList deliveryIntents, boolean persistMessageForCarrierApp) { if (TextUtils.isEmpty(destinationAddress)) { throw new IllegalArgumentException("Invalid destinationAddress"); } if (parts == null || parts.size() < 1) { throw new IllegalArgumentException("Invalid message body"); } if (parts.size() > 1) { try { ISms iccISms = getISmsServiceOrThrow(); iccISms.sendMultipartTextForSubscriber(getSubscriptionId(), ActivityThread.currentPackageName(), destinationAddress, scAddress, parts, sentIntents, deliveryIntents, persistMessageForCarrierApp); } catch (RemoteException ex) { // ignore it } } else { PendingIntent sentIntent = null; PendingIntent deliveryIntent = null; if (sentIntents != null && sentIntents.size() > 0) { sentIntent = sentIntents.get(0); } if (deliveryIntents != null && deliveryIntents.size() > 0) { deliveryIntent = deliveryIntents.get(0); } sendTextMessage(destinationAddress, scAddress, parts.get(0), sentIntent, deliveryIntent); } } /** * Send a multi-part text based SMS without writing it into the SMS Provider. * *

Only the carrier app can call this method.

* * @see #sendMultipartTextMessage(String, String, ArrayList, ArrayList, ArrayList) * @hide **/ public void sendMultipartTextMessageWithoutPersisting( String destinationAddress, String scAddress, ArrayList parts, ArrayList sentIntents, ArrayList deliveryIntents) { sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents, deliveryIntents, false /* persistMessageForCarrierApp*/); } /** * Send a data based SMS to a specific application port. * *

Note: Using this method requires that your app has the * {@link android.Manifest.permission#SEND_SMS} permission.

* * @param destinationAddress the address to send the message to * @param scAddress is the service center address or null to use * the current default SMSC * @param destinationPort the port to deliver the message to * @param data the body of the message to send * @param sentIntent if not NULL this PendingIntent is * broadcast when the message is successfully sent, or failed. * The result code will be Activity.RESULT_OK for success, * or one of these errors:
* RESULT_ERROR_GENERIC_FAILURE
* RESULT_ERROR_RADIO_OFF
* RESULT_ERROR_NULL_PDU
* For RESULT_ERROR_GENERIC_FAILURE the sentIntent may include * the extra "errorCode" containing a radio technology specific value, * generally only useful for troubleshooting.
* The per-application based SMS control checks sentIntent. If sentIntent * is NULL the caller will be checked against all unknown applications, * which cause smaller number of SMS to be sent in checking period. * @param deliveryIntent if not NULL this PendingIntent is * broadcast when the message is delivered to the recipient. The * raw pdu of the status report is in the extended data ("pdu"). * * @throws IllegalArgumentException if destinationAddress or data are empty */ public void sendDataMessage( String destinationAddress, String scAddress, short destinationPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) { if (TextUtils.isEmpty(destinationAddress)) { throw new IllegalArgumentException("Invalid destinationAddress"); } if (data == null || data.length == 0) { throw new IllegalArgumentException("Invalid message data"); } try { ISms iccISms = getISmsServiceOrThrow(); iccISms.sendDataForSubscriber(getSubscriptionId(), ActivityThread.currentPackageName(), destinationAddress, scAddress, destinationPort & 0xFFFF, data, sentIntent, deliveryIntent); } catch (RemoteException ex) { // ignore it } } /** * A variant of {@link SmsManager#sendDataMessage} that allows self to be the caller. This is * for internal use only. * * @hide */ public void sendDataMessageWithSelfPermissions( String destinationAddress, String scAddress, short destinationPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) { if (TextUtils.isEmpty(destinationAddress)) { throw new IllegalArgumentException("Invalid destinationAddress"); } if (data == null || data.length == 0) { throw new IllegalArgumentException("Invalid message data"); } try { ISms iccISms = getISmsServiceOrThrow(); iccISms.sendDataForSubscriberWithSelfPermissions(getSubscriptionId(), ActivityThread.currentPackageName(), destinationAddress, scAddress, destinationPort & 0xFFFF, data, sentIntent, deliveryIntent); } catch (RemoteException ex) { // ignore it } } /** * Get the SmsManager associated with the default subscription id. The instance will always be * associated with the default subscription id, even if the default subscription id is changed. * * @return the SmsManager associated with the default subscription id */ public static SmsManager getDefault() { return sInstance; } /** * Get the the instance of the SmsManager associated with a particular subscription id * * @param subId an SMS subscription id, typically accessed using * {@link android.telephony.SubscriptionManager} * @return the instance of the SmsManager associated with subId */ public static SmsManager getSmsManagerForSubscriptionId(int subId) { // TODO(shri): Add javadoc link once SubscriptionManager is made public api synchronized(sLockObject) { SmsManager smsManager = sSubInstances.get(subId); if (smsManager == null) { smsManager = new SmsManager(subId); sSubInstances.put(subId, smsManager); } return smsManager; } } private SmsManager(int subId) { mSubId = subId; } /** * Get the associated subscription id. If the instance was returned by {@link #getDefault()}, * then this method may return different values at different points in time (if the user * changes the default subscription id). It will return < 0 if the default subscription id * cannot be determined. * * Additionally, to support legacy applications that are not multi-SIM aware, * if the following are true: * - We are using a multi-SIM device * - A default SMS SIM has not been selected * - At least one SIM subscription is available * then ask the user to set the default SMS SIM. * * @return associated subscription id */ public int getSubscriptionId() { final int subId = (mSubId == DEFAULT_SUBSCRIPTION_ID) ? getDefaultSmsSubscriptionId() : mSubId; boolean isSmsSimPickActivityNeeded = false; final Context context = ActivityThread.currentApplication().getApplicationContext(); try { ISms iccISms = getISmsService(); if (iccISms != null) { isSmsSimPickActivityNeeded = iccISms.isSmsSimPickActivityNeeded(subId); } } catch (RemoteException ex) { Log.e(TAG, "Exception in getSubscriptionId"); } if (isSmsSimPickActivityNeeded) { Log.d(TAG, "getSubscriptionId isSmsSimPickActivityNeeded is true"); // ask the user for a default SMS SIM. Intent intent = new Intent(); intent.setClassName("com.android.settings", "com.android.settings.sim.SimDialogActivity"); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra(DIALOG_TYPE_KEY, SMS_PICK); try { context.startActivity(intent); } catch (ActivityNotFoundException anfe) { // If Settings is not installed, only log the error as we do not want to break // legacy applications. Log.e(TAG, "Unable to launch Settings application."); } } return subId; } /** * Returns the ISms service, or throws an UnsupportedOperationException if * the service does not exist. */ private static ISms getISmsServiceOrThrow() { ISms iccISms = getISmsService(); if (iccISms == null) { throw new UnsupportedOperationException("Sms is not supported"); } return iccISms; } private static ISms getISmsService() { return ISms.Stub.asInterface(ServiceManager.getService("isms")); } /** * Copy a raw SMS PDU to the ICC. * ICC (Integrated Circuit Card) is the card of the device. * For example, this can be the SIM or USIM for GSM. * * @param smsc the SMSC for this message, or NULL for the default SMSC * @param pdu the raw PDU to store * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD, * STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT) * @return true for success * * @throws IllegalArgumentException if pdu is NULL * {@hide} */ public boolean copyMessageToIcc(byte[] smsc, byte[] pdu,int status) { boolean success = false; if (null == pdu) { throw new IllegalArgumentException("pdu is NULL"); } try { ISms iccISms = getISmsService(); if (iccISms != null) { success = iccISms.copyMessageToIccEfForSubscriber(getSubscriptionId(), ActivityThread.currentPackageName(), status, pdu, smsc); } } catch (RemoteException ex) { // ignore it } return success; } /** * Delete the specified message from the ICC. * ICC (Integrated Circuit Card) is the card of the device. * For example, this can be the SIM or USIM for GSM. * * @param messageIndex is the record index of the message on ICC * @return true for success * * {@hide} */ public boolean deleteMessageFromIcc(int messageIndex) { boolean success = false; byte[] pdu = new byte[IccConstants.SMS_RECORD_LENGTH-1]; Arrays.fill(pdu, (byte)0xff); try { ISms iccISms = getISmsService(); if (iccISms != null) { success = iccISms.updateMessageOnIccEfForSubscriber(getSubscriptionId(), ActivityThread.currentPackageName(), messageIndex, STATUS_ON_ICC_FREE, pdu); } } catch (RemoteException ex) { // ignore it } return success; } /** * Update the specified message on the ICC. * ICC (Integrated Circuit Card) is the card of the device. * For example, this can be the SIM or USIM for GSM. * * @param messageIndex record index of message to update * @param newStatus new message status (STATUS_ON_ICC_READ, * STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT, * STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE) * @param pdu the raw PDU to store * @return true for success * * {@hide} */ public boolean updateMessageOnIcc(int messageIndex, int newStatus, byte[] pdu) { boolean success = false; try { ISms iccISms = getISmsService(); if (iccISms != null) { success = iccISms.updateMessageOnIccEfForSubscriber(getSubscriptionId(), ActivityThread.currentPackageName(), messageIndex, newStatus, pdu); } } catch (RemoteException ex) { // ignore it } return success; } /** * Retrieves all messages currently stored on ICC. * ICC (Integrated Circuit Card) is the card of the device. * For example, this can be the SIM or USIM for GSM. * * @return ArrayList of SmsMessage objects * * {@hide} */ public ArrayList getAllMessagesFromIcc() { List records = null; try { ISms iccISms = getISmsService(); if (iccISms != null) { records = iccISms.getAllMessagesFromIccEfForSubscriber( getSubscriptionId(), ActivityThread.currentPackageName()); } } catch (RemoteException ex) { // ignore it } return createMessageListFromRawRecords(records); } /** * Enable reception of cell broadcast (SMS-CB) messages with the given * message identifier and RAN type. The RAN type specify this message ID * belong to 3GPP (GSM) or 3GPP2(CDMA).Note that if two different clients * enable the same message identifier, they must both disable it for the device to stop * receiving those messages. All received messages will be broadcast in an * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED". * Note: This call is blocking, callers may want to avoid calling it from * the main thread of an application. * * @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP) * or C.R1001-G (3GPP2) * @param ranType as defined in class SmsManager, the value can be one of these: * android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_GSM * android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_CDMA * @return true if successful, false otherwise * @see #disableCellBroadcast(int, int) * * {@hide} */ public boolean enableCellBroadcast(int messageIdentifier, int ranType) { boolean success = false; try { ISms iccISms = getISmsService(); if (iccISms != null) { success = iccISms.enableCellBroadcastForSubscriber( getSubscriptionId(), messageIdentifier, ranType); } } catch (RemoteException ex) { // ignore it } return success; } /** * Disable reception of cell broadcast (SMS-CB) messages with the given * message identifier and RAN type. The RAN type specify this message ID * belong to 3GPP (GSM) or 3GPP2(CDMA). Note that if two different clients * enable the same message identifier, they must both disable it for the * device to stop receiving those messages. * Note: This call is blocking, callers may want to avoid calling it from * the main thread of an application. * * @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP) * or C.R1001-G (3GPP2) * @param ranType as defined in class SmsManager, the value can be one of these: * android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_GSM * android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_CDMA * @return true if successful, false otherwise * * @see #enableCellBroadcast(int, int) * * {@hide} */ public boolean disableCellBroadcast(int messageIdentifier, int ranType) { boolean success = false; try { ISms iccISms = getISmsService(); if (iccISms != null) { success = iccISms.disableCellBroadcastForSubscriber( getSubscriptionId(), messageIdentifier, ranType); } } catch (RemoteException ex) { // ignore it } return success; } /** * Enable reception of cell broadcast (SMS-CB) messages with the given * message identifier range and RAN type. The RAN type specify this message ID * belong to 3GPP (GSM) or 3GPP2(CDMA). Note that if two different clients enable * the same message identifier, they must both disable it for the device to stop * receiving those messages. All received messages will be broadcast in an * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED". * Note: This call is blocking, callers may want to avoid calling it from * the main thread of an application. * * @param startMessageId first message identifier as specified in TS 23.041 (3GPP) * or C.R1001-G (3GPP2) * @param endMessageId last message identifier as specified in TS 23.041 (3GPP) * or C.R1001-G (3GPP2) * @param ranType as defined in class SmsManager, the value can be one of these: * android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_GSM * android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_CDMA * @return true if successful, false otherwise * @see #disableCellBroadcastRange(int, int, int) * * @throws IllegalArgumentException if endMessageId < startMessageId * {@hide} */ public boolean enableCellBroadcastRange(int startMessageId, int endMessageId, int ranType) { boolean success = false; if (endMessageId < startMessageId) { throw new IllegalArgumentException("endMessageId < startMessageId"); } try { ISms iccISms = getISmsService(); if (iccISms != null) { success = iccISms.enableCellBroadcastRangeForSubscriber(getSubscriptionId(), startMessageId, endMessageId, ranType); } } catch (RemoteException ex) { // ignore it } return success; } /** * Disable reception of cell broadcast (SMS-CB) messages with the given * message identifier range and RAN type. The RAN type specify this message * ID range belong to 3GPP (GSM) or 3GPP2(CDMA). Note that if two different * clients enable the same message identifier, they must both disable it for * the device to stop receiving those messages. * Note: This call is blocking, callers may want to avoid calling it from * the main thread of an application. * * @param startMessageId first message identifier as specified in TS 23.041 (3GPP) * or C.R1001-G (3GPP2) * @param endMessageId last message identifier as specified in TS 23.041 (3GPP) * or C.R1001-G (3GPP2) * @param ranType as defined in class SmsManager, the value can be one of these: * android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_GSM * android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_CDMA * @return true if successful, false otherwise * * @see #enableCellBroadcastRange(int, int, int) * * @throws IllegalArgumentException if endMessageId < startMessageId * {@hide} */ public boolean disableCellBroadcastRange(int startMessageId, int endMessageId, int ranType) { boolean success = false; if (endMessageId < startMessageId) { throw new IllegalArgumentException("endMessageId < startMessageId"); } try { ISms iccISms = getISmsService(); if (iccISms != null) { success = iccISms.disableCellBroadcastRangeForSubscriber(getSubscriptionId(), startMessageId, endMessageId, ranType); } } catch (RemoteException ex) { // ignore it } return success; } /** * Create a list of SmsMessages from a list of RawSmsData * records returned by getAllMessagesFromIcc() * * @param records SMS EF records, returned by * getAllMessagesFromIcc * @return ArrayList of SmsMessage objects. */ private static ArrayList createMessageListFromRawRecords(List records) { ArrayList messages = new ArrayList(); if (records != null) { int count = records.size(); for (int i = 0; i < count; i++) { SmsRawData data = records.get(i); // List contains all records, including "free" records (null) if (data != null) { SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes()); if (sms != null) { messages.add(sms); } } } } return messages; } /** * SMS over IMS is supported if IMS is registered and SMS is supported * on IMS. * * @return true if SMS over IMS is supported, false otherwise * * @see #getImsSmsFormat() * * @hide */ public boolean isImsSmsSupported() { boolean boSupported = false; try { ISms iccISms = getISmsService(); if (iccISms != null) { boSupported = iccISms.isImsSmsSupportedForSubscriber(getSubscriptionId()); } } catch (RemoteException ex) { // ignore it } return boSupported; } /** * Gets SMS format supported on IMS. SMS over IMS format is * either 3GPP or 3GPP2. * * @return SmsMessage.FORMAT_3GPP, * SmsMessage.FORMAT_3GPP2 * or SmsMessage.FORMAT_UNKNOWN * * @see #isImsSmsSupported() * * @hide */ public String getImsSmsFormat() { String format = com.android.internal.telephony.SmsConstants.FORMAT_UNKNOWN; try { ISms iccISms = getISmsService(); if (iccISms != null) { format = iccISms.getImsSmsFormatForSubscriber(getSubscriptionId()); } } catch (RemoteException ex) { // ignore it } return format; } /** * Get default sms subscription id * * @return the default SMS subscription id */ public static int getDefaultSmsSubscriptionId() { ISms iccISms = null; try { iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); return iccISms.getPreferredSmsSubscription(); } catch (RemoteException ex) { return -1; } catch (NullPointerException ex) { return -1; } } /** * Get SMS prompt property, enabled or not * * @return true if enabled, false otherwise * @hide */ public boolean isSMSPromptEnabled() { ISms iccISms = null; try { iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); return iccISms.isSMSPromptEnabled(); } catch (RemoteException ex) { return false; } catch (NullPointerException ex) { return false; } } // see SmsMessage.getStatusOnIcc /** Free space (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */ static public final int STATUS_ON_ICC_FREE = 0; /** Received and read (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */ static public final int STATUS_ON_ICC_READ = 1; /** Received and unread (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */ static public final int STATUS_ON_ICC_UNREAD = 3; /** Stored and sent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */ static public final int STATUS_ON_ICC_SENT = 5; /** Stored and unsent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */ static public final int STATUS_ON_ICC_UNSENT = 7; // SMS send failure result codes /** Generic failure cause */ static public final int RESULT_ERROR_GENERIC_FAILURE = 1; /** Failed because radio was explicitly turned off */ static public final int RESULT_ERROR_RADIO_OFF = 2; /** Failed because no pdu provided */ static public final int RESULT_ERROR_NULL_PDU = 3; /** Failed because service is currently unavailable */ static public final int RESULT_ERROR_NO_SERVICE = 4; /** Failed because we reached the sending queue limit. {@hide} */ static public final int RESULT_ERROR_LIMIT_EXCEEDED = 5; /** Failed because FDN is enabled. {@hide} */ static public final int RESULT_ERROR_FDN_CHECK_FAILURE = 6; static private final String PHONE_PACKAGE_NAME = "com.android.phone"; /** * Send an MMS message * * @param context application context * @param contentUri the content Uri from which the message pdu will be read * @param locationUrl the optional location url where message should be sent to * @param configOverrides the carrier-specific messaging configuration values to override for * sending the message. * @param sentIntent if not NULL this PendingIntent is * broadcast when the message is successfully sent, or failed * @throws IllegalArgumentException if contentUri is empty */ public void sendMultimediaMessage(Context context, Uri contentUri, String locationUrl, Bundle configOverrides, PendingIntent sentIntent) { if (contentUri == null) { throw new IllegalArgumentException("Uri contentUri null"); } try { final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); if (iMms == null) { return; } iMms.sendMessage(getSubscriptionId(), ActivityThread.currentPackageName(), contentUri, locationUrl, configOverrides, sentIntent); } catch (RemoteException e) { // Ignore it } } /** * Download an MMS message from carrier by a given location URL * * @param context application context * @param locationUrl the location URL of the MMS message to be downloaded, usually obtained * from the MMS WAP push notification * @param contentUri the content uri to which the downloaded pdu will be written * @param configOverrides the carrier-specific messaging configuration values to override for * downloading the message. * @param downloadedIntent if not NULL this PendingIntent is * broadcast when the message is downloaded, or the download is failed * @throws IllegalArgumentException if locationUrl or contentUri is empty */ public void downloadMultimediaMessage(Context context, String locationUrl, Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent) { if (TextUtils.isEmpty(locationUrl)) { throw new IllegalArgumentException("Empty MMS location URL"); } if (contentUri == null) { throw new IllegalArgumentException("Uri contentUri null"); } try { final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); if (iMms == null) { return; } iMms.downloadMessage( getSubscriptionId(), ActivityThread.currentPackageName(), locationUrl, contentUri, configOverrides, downloadedIntent); } catch (RemoteException e) { // Ignore it } } // MMS send/download failure result codes public static final int MMS_ERROR_UNSPECIFIED = 1; public static final int MMS_ERROR_INVALID_APN = 2; public static final int MMS_ERROR_UNABLE_CONNECT_MMS = 3; public static final int MMS_ERROR_HTTP_FAILURE = 4; public static final int MMS_ERROR_IO_ERROR = 5; public static final int MMS_ERROR_RETRY = 6; public static final int MMS_ERROR_CONFIGURATION_ERROR = 7; public static final int MMS_ERROR_NO_DATA_NETWORK = 8; /** Intent extra name for MMS sending result data in byte array type */ public static final String EXTRA_MMS_DATA = "android.telephony.extra.MMS_DATA"; /** Intent extra name for HTTP status code for MMS HTTP failure in integer type */ public static final String EXTRA_MMS_HTTP_STATUS = "android.telephony.extra.MMS_HTTP_STATUS"; /** * Import a text message into system's SMS store * * Only default SMS apps can import SMS * * @param address the destination(source) address of the sent(received) message * @param type the type of the message * @param text the message text * @param timestampMillis the message timestamp in milliseconds * @param seen if the message is seen * @param read if the message is read * @return the message URI, null if failed * @hide */ public Uri importTextMessage(String address, int type, String text, long timestampMillis, boolean seen, boolean read) { try { IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); if (iMms != null) { return iMms.importTextMessage(ActivityThread.currentPackageName(), address, type, text, timestampMillis, seen, read); } } catch (RemoteException ex) { // ignore it } return null; } /** Represents the received SMS message for importing {@hide} */ public static final int SMS_TYPE_INCOMING = 0; /** Represents the sent SMS message for importing {@hide} */ public static final int SMS_TYPE_OUTGOING = 1; /** * Import a multimedia message into system's MMS store. Only the following PDU type is * supported: Retrieve.conf, Send.req, Notification.ind, Delivery.ind, Read-Orig.ind * * Only default SMS apps can import MMS * * @param contentUri the content uri from which to read the PDU of the message to import * @param messageId the optional message id. Use null if not specifying * @param timestampSecs the optional message timestamp. Use -1 if not specifying * @param seen if the message is seen * @param read if the message is read * @return the message URI, null if failed * @throws IllegalArgumentException if pdu is empty * {@hide} */ public Uri importMultimediaMessage(Uri contentUri, String messageId, long timestampSecs, boolean seen, boolean read) { if (contentUri == null) { throw new IllegalArgumentException("Uri contentUri null"); } try { IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); if (iMms != null) { return iMms.importMultimediaMessage(ActivityThread.currentPackageName(), contentUri, messageId, timestampSecs, seen, read); } } catch (RemoteException ex) { // ignore it } return null; } /** * Delete a system stored SMS or MMS message * * Only default SMS apps can delete system stored SMS and MMS messages * * @param messageUri the URI of the stored message * @return true if deletion is successful, false otherwise * @throws IllegalArgumentException if messageUri is empty * {@hide} */ public boolean deleteStoredMessage(Uri messageUri) { if (messageUri == null) { throw new IllegalArgumentException("Empty message URI"); } try { IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); if (iMms != null) { return iMms.deleteStoredMessage(ActivityThread.currentPackageName(), messageUri); } } catch (RemoteException ex) { // ignore it } return false; } /** * Delete a system stored SMS or MMS thread * * Only default SMS apps can delete system stored SMS and MMS conversations * * @param conversationId the ID of the message conversation * @return true if deletion is successful, false otherwise * {@hide} */ public boolean deleteStoredConversation(long conversationId) { try { IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); if (iMms != null) { return iMms.deleteStoredConversation( ActivityThread.currentPackageName(), conversationId); } } catch (RemoteException ex) { // ignore it } return false; } /** * Update the status properties of a system stored SMS or MMS message, e.g. * the read status of a message, etc. * * @param messageUri the URI of the stored message * @param statusValues a list of status properties in key-value pairs to update * @return true if update is successful, false otherwise * @throws IllegalArgumentException if messageUri is empty * {@hide} */ public boolean updateStoredMessageStatus(Uri messageUri, ContentValues statusValues) { if (messageUri == null) { throw new IllegalArgumentException("Empty message URI"); } try { IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); if (iMms != null) { return iMms.updateStoredMessageStatus(ActivityThread.currentPackageName(), messageUri, statusValues); } } catch (RemoteException ex) { // ignore it } return false; } /** Message status property: whether the message has been seen. 1 means seen, 0 not {@hide} */ public static final String MESSAGE_STATUS_SEEN = "seen"; /** Message status property: whether the message has been read. 1 means read, 0 not {@hide} */ public static final String MESSAGE_STATUS_READ = "read"; /** * Archive or unarchive a stored conversation * * @param conversationId the ID of the message conversation * @param archived true to archive the conversation, false to unarchive * @return true if update is successful, false otherwise * {@hide} */ public boolean archiveStoredConversation(long conversationId, boolean archived) { try { IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); if (iMms != null) { return iMms.archiveStoredConversation(ActivityThread.currentPackageName(), conversationId, archived); } } catch (RemoteException ex) { // ignore it } return false; } /** * Add a text message draft to system SMS store * * Only default SMS apps can add SMS draft * * @param address the destination address of message * @param text the body of the message to send * @return the URI of the stored draft message * {@hide} */ public Uri addTextMessageDraft(String address, String text) { try { IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); if (iMms != null) { return iMms.addTextMessageDraft(ActivityThread.currentPackageName(), address, text); } } catch (RemoteException ex) { // ignore it } return null; } /** * Add a multimedia message draft to system MMS store * * Only default SMS apps can add MMS draft * * @param contentUri the content uri from which to read the PDU data of the draft MMS * @return the URI of the stored draft message * @throws IllegalArgumentException if pdu is empty * {@hide} */ public Uri addMultimediaMessageDraft(Uri contentUri) { if (contentUri == null) { throw new IllegalArgumentException("Uri contentUri null"); } try { IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); if (iMms != null) { return iMms.addMultimediaMessageDraft(ActivityThread.currentPackageName(), contentUri); } } catch (RemoteException ex) { // ignore it } return null; } /** * Send a system stored text message. * * You can only send a failed text message or a draft text message. * * @param messageUri the URI of the stored message * @param scAddress is the service center address or null to use the current default SMSC * @param sentIntent if not NULL this PendingIntent is * broadcast when the message is successfully sent, or failed. * The result code will be Activity.RESULT_OK for success, * or one of these errors:
* RESULT_ERROR_GENERIC_FAILURE
* RESULT_ERROR_RADIO_OFF
* RESULT_ERROR_NULL_PDU
* For RESULT_ERROR_GENERIC_FAILURE the sentIntent may include * the extra "errorCode" containing a radio technology specific value, * generally only useful for troubleshooting.
* The per-application based SMS control checks sentIntent. If sentIntent * is NULL the caller will be checked against all unknown applications, * which cause smaller number of SMS to be sent in checking period. * @param deliveryIntent if not NULL this PendingIntent is * broadcast when the message is delivered to the recipient. The * raw pdu of the status report is in the extended data ("pdu"). * * @throws IllegalArgumentException if messageUri is empty * {@hide} */ public void sendStoredTextMessage(Uri messageUri, String scAddress, PendingIntent sentIntent, PendingIntent deliveryIntent) { if (messageUri == null) { throw new IllegalArgumentException("Empty message URI"); } try { ISms iccISms = getISmsServiceOrThrow(); iccISms.sendStoredText( getSubscriptionId(), ActivityThread.currentPackageName(), messageUri, scAddress, sentIntent, deliveryIntent); } catch (RemoteException ex) { // ignore it } } /** * Send a system stored multi-part text message. * * You can only send a failed text message or a draft text message. * The provided PendingIntent lists should match the part number of the * divided text of the stored message by using divideMessage * * @param messageUri the URI of the stored message * @param scAddress is the service center address or null to use * the current default SMSC * @param sentIntents if not null, an ArrayList of * PendingIntents (one for each message part) that is * broadcast when the corresponding message part has been sent. * The result code will be Activity.RESULT_OK for success, * or one of these errors:
* RESULT_ERROR_GENERIC_FAILURE
* RESULT_ERROR_RADIO_OFF
* RESULT_ERROR_NULL_PDU
* For RESULT_ERROR_GENERIC_FAILURE each sentIntent may include * the extra "errorCode" containing a radio technology specific value, * generally only useful for troubleshooting.
* The per-application based SMS control checks sentIntent. If sentIntent * is NULL the caller will be checked against all unknown applications, * which cause smaller number of SMS to be sent in checking period. * @param deliveryIntents if not null, an ArrayList of * PendingIntents (one for each message part) that is * broadcast when the corresponding message part has been delivered * to the recipient. The raw pdu of the status report is in the * extended data ("pdu"). * * @throws IllegalArgumentException if messageUri is empty * {@hide} */ public void sendStoredMultipartTextMessage(Uri messageUri, String scAddress, ArrayList sentIntents, ArrayList deliveryIntents) { if (messageUri == null) { throw new IllegalArgumentException("Empty message URI"); } try { ISms iccISms = getISmsServiceOrThrow(); iccISms.sendStoredMultipartText( getSubscriptionId(), ActivityThread.currentPackageName(), messageUri, scAddress, sentIntents, deliveryIntents); } catch (RemoteException ex) { // ignore it } } /** * Send a system stored MMS message * * This is used for sending a previously sent, but failed-to-send, message or * for sending a text message that has been stored as a draft. * * @param messageUri the URI of the stored message * @param configOverrides the carrier-specific messaging configuration values to override for * sending the message. * @param sentIntent if not NULL this PendingIntent is * broadcast when the message is successfully sent, or failed * @throws IllegalArgumentException if messageUri is empty * {@hide} */ public void sendStoredMultimediaMessage(Uri messageUri, Bundle configOverrides, PendingIntent sentIntent) { if (messageUri == null) { throw new IllegalArgumentException("Empty message URI"); } try { IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); if (iMms != null) { iMms.sendStoredMessage( getSubscriptionId(), ActivityThread.currentPackageName(), messageUri, configOverrides, sentIntent); } } catch (RemoteException ex) { // ignore it } } /** * Turns on/off the flag to automatically write sent/received SMS/MMS messages into system * * When this flag is on, all SMS/MMS sent/received are stored by system automatically * When this flag is off, only SMS/MMS sent by non-default SMS apps are stored by system * automatically * * This flag can only be changed by default SMS apps * * @param enabled Whether to enable message auto persisting * {@hide} */ public void setAutoPersisting(boolean enabled) { try { IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); if (iMms != null) { iMms.setAutoPersisting(ActivityThread.currentPackageName(), enabled); } } catch (RemoteException ex) { // ignore it } } /** * Get the value of the flag to automatically write sent/received SMS/MMS messages into system * * When this flag is on, all SMS/MMS sent/received are stored by system automatically * When this flag is off, only SMS/MMS sent by non-default SMS apps are stored by system * automatically * * @return the current value of the auto persist flag * {@hide} */ public boolean getAutoPersisting() { try { IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); if (iMms != null) { return iMms.getAutoPersisting(); } } catch (RemoteException ex) { // ignore it } return false; } /** * Get carrier-dependent configuration values. * * @return bundle key/values pairs of configuration values */ public Bundle getCarrierConfigValues() { try { IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); if (iMms != null) { return iMms.getCarrierConfigValues(getSubscriptionId()); } } catch (RemoteException ex) { // ignore it } return null; } /** * Filters a bundle to only contain MMS config variables. * * This is for use with bundles returned by {@link CarrierConfigManager} which contain MMS * config and unrelated config. It is assumed that all MMS_CONFIG_* keys are present in the * supplied bundle. * * @param config a Bundle that contains MMS config variables and possibly more. * @return a new Bundle that only contains the MMS_CONFIG_* keys defined above. * @hide */ public static Bundle getMmsConfig(BaseBundle config) { Bundle filtered = new Bundle(); filtered.putBoolean(MMS_CONFIG_APPEND_TRANSACTION_ID, config.getBoolean(MMS_CONFIG_APPEND_TRANSACTION_ID)); filtered.putBoolean(MMS_CONFIG_MMS_ENABLED, config.getBoolean(MMS_CONFIG_MMS_ENABLED)); filtered.putBoolean(MMS_CONFIG_GROUP_MMS_ENABLED, config.getBoolean(MMS_CONFIG_GROUP_MMS_ENABLED)); filtered.putBoolean(MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED, config.getBoolean(MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED)); filtered.putBoolean(MMS_CONFIG_ALIAS_ENABLED, config.getBoolean(MMS_CONFIG_ALIAS_ENABLED)); filtered.putBoolean(MMS_CONFIG_ALLOW_ATTACH_AUDIO, config.getBoolean(MMS_CONFIG_ALLOW_ATTACH_AUDIO)); filtered.putBoolean(MMS_CONFIG_MULTIPART_SMS_ENABLED, config.getBoolean(MMS_CONFIG_MULTIPART_SMS_ENABLED)); filtered.putBoolean(MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED, config.getBoolean(MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED)); filtered.putBoolean(MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION, config.getBoolean(MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION)); filtered.putBoolean(MMS_CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES, config.getBoolean(MMS_CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES)); filtered.putBoolean(MMS_CONFIG_MMS_READ_REPORT_ENABLED, config.getBoolean(MMS_CONFIG_MMS_READ_REPORT_ENABLED)); filtered.putBoolean(MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED, config.getBoolean(MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED)); filtered.putBoolean(MMS_CONFIG_CLOSE_CONNECTION, config.getBoolean(MMS_CONFIG_CLOSE_CONNECTION)); filtered.putInt(MMS_CONFIG_MAX_MESSAGE_SIZE, config.getInt(MMS_CONFIG_MAX_MESSAGE_SIZE)); filtered.putInt(MMS_CONFIG_MAX_IMAGE_WIDTH, config.getInt(MMS_CONFIG_MAX_IMAGE_WIDTH)); filtered.putInt(MMS_CONFIG_MAX_IMAGE_HEIGHT, config.getInt(MMS_CONFIG_MAX_IMAGE_HEIGHT)); filtered.putInt(MMS_CONFIG_RECIPIENT_LIMIT, config.getInt(MMS_CONFIG_RECIPIENT_LIMIT)); filtered.putInt(MMS_CONFIG_ALIAS_MIN_CHARS, config.getInt(MMS_CONFIG_ALIAS_MIN_CHARS)); filtered.putInt(MMS_CONFIG_ALIAS_MAX_CHARS, config.getInt(MMS_CONFIG_ALIAS_MAX_CHARS)); filtered.putInt(MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD, config.getInt(MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD)); filtered.putInt(MMS_CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD, config.getInt(MMS_CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD)); filtered.putInt(MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE, config.getInt(MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE)); filtered.putInt(MMS_CONFIG_SUBJECT_MAX_LENGTH, config.getInt(MMS_CONFIG_SUBJECT_MAX_LENGTH)); filtered.putInt(MMS_CONFIG_HTTP_SOCKET_TIMEOUT, config.getInt(MMS_CONFIG_HTTP_SOCKET_TIMEOUT)); filtered.putString(MMS_CONFIG_UA_PROF_TAG_NAME, config.getString(MMS_CONFIG_UA_PROF_TAG_NAME)); filtered.putString(MMS_CONFIG_USER_AGENT, config.getString(MMS_CONFIG_USER_AGENT)); filtered.putString(MMS_CONFIG_UA_PROF_URL, config.getString(MMS_CONFIG_UA_PROF_URL)); filtered.putString(MMS_CONFIG_HTTP_PARAMS, config.getString(MMS_CONFIG_HTTP_PARAMS)); filtered.putString(MMS_CONFIG_EMAIL_GATEWAY_NUMBER, config.getString(MMS_CONFIG_EMAIL_GATEWAY_NUMBER)); filtered.putString(MMS_CONFIG_NAI_SUFFIX, config.getString(MMS_CONFIG_NAI_SUFFIX)); filtered.putBoolean(MMS_CONFIG_SHOW_CELL_BROADCAST_APP_LINKS, config.getBoolean(MMS_CONFIG_SHOW_CELL_BROADCAST_APP_LINKS)); filtered.putBoolean(MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER, config.getBoolean(MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER)); return filtered; } }