461 lines
18 KiB
Java
461 lines
18 KiB
Java
/*
|
|
* 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.PendingIntent;
|
|
import android.os.RemoteException;
|
|
import android.os.ServiceManager;
|
|
import android.text.TextUtils;
|
|
|
|
import com.android.internal.telephony.ISms;
|
|
import com.android.internal.telephony.IccConstants;
|
|
import com.android.internal.telephony.SmsRawData;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
|
|
/*
|
|
* 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 SmsManager.getDefault().
|
|
*/
|
|
public final class SmsManager {
|
|
/** Singleton object constructed during class initialization. */
|
|
private static final SmsManager sInstance = new SmsManager();
|
|
|
|
/**
|
|
* Send a text based SMS.
|
|
*
|
|
* @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 <code>PendingIntent</code> is
|
|
* broadcast when the message is successfully sent, or failed.
|
|
* The result code will be <code>Activity.RESULT_OK</code> for success,
|
|
* or one of these errors:<br>
|
|
* <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
|
|
* <code>RESULT_ERROR_RADIO_OFF</code><br>
|
|
* <code>RESULT_ERROR_NULL_PDU</code><br>
|
|
* For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
|
|
* the extra "errorCode" containing a radio technology specific value,
|
|
* generally only useful for troubleshooting.<br>
|
|
* 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 <code>PendingIntent</code> 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) {
|
|
if (TextUtils.isEmpty(destinationAddress)) {
|
|
throw new IllegalArgumentException("Invalid destinationAddress");
|
|
}
|
|
|
|
if (TextUtils.isEmpty(text)) {
|
|
throw new IllegalArgumentException("Invalid message body");
|
|
}
|
|
|
|
try {
|
|
ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
|
|
if (iccISms != null) {
|
|
iccISms.sendText(destinationAddress, scAddress, text, sentIntent, deliveryIntent);
|
|
}
|
|
} 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 <code>ArrayList</code> of strings that, in order,
|
|
* comprise the original message
|
|
*/
|
|
public ArrayList<String> divideMessage(String text) {
|
|
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
|
|
* <code>divideMessage</code>.
|
|
*
|
|
* @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 <code>ArrayList</code> of strings that, in order,
|
|
* comprise the original message
|
|
* @param sentIntents if not null, an <code>ArrayList</code> of
|
|
* <code>PendingIntent</code>s (one for each message part) that is
|
|
* broadcast when the corresponding message part has been sent.
|
|
* The result code will be <code>Activity.RESULT_OK</code> for success,
|
|
* or one of these errors:<br>
|
|
* <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
|
|
* <code>RESULT_ERROR_RADIO_OFF</code><br>
|
|
* <code>RESULT_ERROR_NULL_PDU</code><br>
|
|
* For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include
|
|
* the extra "errorCode" containing a radio technology specific value,
|
|
* generally only useful for troubleshooting.<br>
|
|
* 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 <code>ArrayList</code> of
|
|
* <code>PendingIntent</code>s (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<String> parts,
|
|
ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
|
|
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 = ISms.Stub.asInterface(ServiceManager.getService("isms"));
|
|
if (iccISms != null) {
|
|
iccISms.sendMultipartText(destinationAddress, scAddress, parts,
|
|
sentIntents, deliveryIntents);
|
|
}
|
|
} 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 data based SMS to a specific application port.
|
|
*
|
|
* @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 <code>PendingIntent</code> is
|
|
* broadcast when the message is successfully sent, or failed.
|
|
* The result code will be <code>Activity.RESULT_OK</code> for success,
|
|
* or one of these errors:<br>
|
|
* <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
|
|
* <code>RESULT_ERROR_RADIO_OFF</code><br>
|
|
* <code>RESULT_ERROR_NULL_PDU</code><br>
|
|
* For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
|
|
* the extra "errorCode" containing a radio technology specific value,
|
|
* generally only useful for troubleshooting.<br>
|
|
* 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 <code>PendingIntent</code> 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 = ISms.Stub.asInterface(ServiceManager.getService("isms"));
|
|
if (iccISms != null) {
|
|
iccISms.sendData(destinationAddress, scAddress, destinationPort & 0xFFFF,
|
|
data, sentIntent, deliveryIntent);
|
|
}
|
|
} catch (RemoteException ex) {
|
|
// ignore it
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the default instance of the SmsManager
|
|
*
|
|
* @return the default instance of the SmsManager
|
|
*/
|
|
public static SmsManager getDefault() {
|
|
return sInstance;
|
|
}
|
|
|
|
private SmsManager() {
|
|
//nothing
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*
|
|
* {@hide}
|
|
*/
|
|
public boolean copyMessageToIcc(byte[] smsc, byte[] pdu, int status) {
|
|
boolean success = false;
|
|
|
|
try {
|
|
ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
|
|
if (iccISms != null) {
|
|
success = iccISms.copyMessageToIccEf(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 = ISms.Stub.asInterface(ServiceManager.getService("isms"));
|
|
if (iccISms != null) {
|
|
success = iccISms.updateMessageOnIccEf(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 = ISms.Stub.asInterface(ServiceManager.getService("isms"));
|
|
if (iccISms != null) {
|
|
success = iccISms.updateMessageOnIccEf(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 <code>ArrayList</code> of <code>SmsMessage</code> objects
|
|
*
|
|
* {@hide}
|
|
*/
|
|
public ArrayList<SmsMessage> getAllMessagesFromIcc() {
|
|
List<SmsRawData> records = null;
|
|
|
|
try {
|
|
ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
|
|
if (iccISms != null) {
|
|
records = iccISms.getAllMessagesFromIccEf();
|
|
}
|
|
} catch (RemoteException ex) {
|
|
// ignore it
|
|
}
|
|
|
|
return createMessageListFromRawRecords(records);
|
|
}
|
|
|
|
/**
|
|
* Enable reception of cell broadcast (SMS-CB) messages with the given
|
|
* message identifier. 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
|
|
* @return true if successful, false otherwise
|
|
* @see #disableCellBroadcast(int)
|
|
*
|
|
* {@hide}
|
|
*/
|
|
public boolean enableCellBroadcast(int messageIdentifier) {
|
|
boolean success = false;
|
|
|
|
try {
|
|
ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
|
|
if (iccISms != null) {
|
|
success = iccISms.enableCellBroadcast(messageIdentifier);
|
|
}
|
|
} catch (RemoteException ex) {
|
|
// ignore it
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
/**
|
|
* Disable reception of cell broadcast (SMS-CB) messages with the given
|
|
* message identifier. 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
|
|
* @return true if successful, false otherwise
|
|
*
|
|
* @see #enableCellBroadcast(int)
|
|
*
|
|
* {@hide}
|
|
*/
|
|
public boolean disableCellBroadcast(int messageIdentifier) {
|
|
boolean success = false;
|
|
|
|
try {
|
|
ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
|
|
if (iccISms != null) {
|
|
success = iccISms.disableCellBroadcast(messageIdentifier);
|
|
}
|
|
} catch (RemoteException ex) {
|
|
// ignore it
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
/**
|
|
* Create a list of <code>SmsMessage</code>s from a list of RawSmsData
|
|
* records returned by <code>getAllMessagesFromIcc()</code>
|
|
*
|
|
* @param records SMS EF records, returned by
|
|
* <code>getAllMessagesFromIcc</code>
|
|
* @return <code>ArrayList</code> of <code>SmsMessage</code> objects.
|
|
*/
|
|
private ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
|
|
ArrayList<SmsMessage> messages = new ArrayList<SmsMessage>();
|
|
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;
|
|
}
|
|
|
|
// 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;
|
|
}
|