ArrayList
of strings that, in order, comprise the original msg text.
* @hide
*/
@UnsupportedAppUsage
public static ArrayListArrayList
of strings that, in order, comprise the original msg text.
* @hide
*/
public static ArrayListSubmitPdu
containing the encoded SC address if applicable and the
* encoded message. Returns null on encode error.
*/
public static SubmitPdu getSubmitPdu(String scAddress,
String destinationAddress, String message, boolean statusReportRequested) {
return getSubmitPdu(
scAddress,
destinationAddress,
message,
statusReportRequested,
SmsManager.getDefaultSmsSubscriptionId());
}
/**
* Gets an SMS-SUBMIT PDU for a destination address and a message.
* This method will not attempt to use any GSM national language 7 bit encodings.
*
* @param scAddress Service Centre address. Null means use default.
* @param destinationAddress the address of the destination for the message.
* @param message string representation of the message payload.
* @param statusReportRequested indicates whether a report is requested for this message.
* @param subId subscription of the message.
* @return a SubmitPdu
containing the encoded SC address if applicable and the
* encoded message. Returns null on encode error.
* @hide
*/
public static SubmitPdu getSubmitPdu(String scAddress,
String destinationAddress, String message, boolean statusReportRequested, int subId) {
SubmitPduBase spb;
if (useCdmaFormatForMoSms(subId)) {
spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
destinationAddress, message, statusReportRequested, null);
} else {
spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
destinationAddress, message, statusReportRequested);
}
return spb != null ? new SubmitPdu(spb) : null;
}
/**
* Gets an SMS-SUBMIT PDU for a data message to a destination address & port.
* This method will not attempt to use any GSM national language 7 bit encodings.
*
* @param scAddress Service Centre address. Null means use default.
* @param destinationAddress the address of the destination for the message.
* @param destinationPort the port to deliver the message to at the destination.
* @param data the data for the message.
* @param statusReportRequested indicates whether a report is requested for this message.
* @return a SubmitPdu
containing the encoded SC address if applicable and the
* encoded message. Returns null on encode error.
*/
public static SubmitPdu getSubmitPdu(String scAddress,
String destinationAddress, short destinationPort, byte[] data,
boolean statusReportRequested) {
SubmitPduBase spb;
if (useCdmaFormatForMoSms()) {
spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
destinationAddress, destinationPort, data, statusReportRequested);
} else {
spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
destinationAddress, destinationPort, data, statusReportRequested);
}
return spb != null ? new SubmitPdu(spb) : null;
}
// TODO: SubmitPdu class is used for SMS-DELIVER also now. Refactor for SubmitPdu and new
// DeliverPdu accordingly.
/**
* Gets an SMS PDU to store in the ICC.
*
* @param subId subscription of the message.
* @param status message status. One of these status:
* SmsManager.STATUS_ON_ICC_READ
* SmsManager.STATUS_ON_ICC_UNREAD
* SmsManager.STATUS_ON_ICC_SENT
* SmsManager.STATUS_ON_ICC_UNSENT
* @param scAddress Service Centre address. Null means use default.
* @param address destination or originating address.
* @param message string representation of the message payload.
* @param date the time stamp the message was received.
* @return a SubmitPdu
containing the encoded SC address if applicable and the
* encoded message. Returns null on encode error.
* @hide
*/
@SystemApi
@Nullable
public static SubmitPdu getSmsPdu(int subId, @SmsManager.StatusOnIcc int status,
@Nullable String scAddress, @NonNull String address, @NonNull String message,
long date) {
SubmitPduBase spb;
if (isCdmaVoice(subId)) { // 3GPP2 format
if (status == SmsManager.STATUS_ON_ICC_READ
|| status == SmsManager.STATUS_ON_ICC_UNREAD) { // Deliver PDU
spb = com.android.internal.telephony.cdma.SmsMessage.getDeliverPdu(address,
message, date);
} else { // Submit PDU
spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
address, message, false /* statusReportRequested */, null /* smsHeader */);
}
} else { // 3GPP format
if (status == SmsManager.STATUS_ON_ICC_READ
|| status == SmsManager.STATUS_ON_ICC_UNREAD) { // Deliver PDU
spb = com.android.internal.telephony.gsm.SmsMessage.getDeliverPdu(scAddress,
address, message, date);
} else { // Submit PDU
spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
address, message, false /* statusReportRequested */, null /* header */);
}
}
return spb != null ? new SubmitPdu(spb) : null;
}
/**
* Get an SMS-SUBMIT PDU's encoded message.
* This is used by Bluetooth MAP profile to handle long non UTF-8 SMS messages.
*
* @param isTypeGsm true when message's type is GSM, false when type is CDMA
* @param destinationAddress the address of the destination for the message
* @param message message content
* @param encoding User data text encoding code unit size
* @param languageTable GSM national language table to use, specified by 3GPP
* 23.040 9.2.3.24.16
* @param languageShiftTable GSM national language shift table to use, specified by 3GPP
* 23.040 9.2.3.24.15
* @param refNumber reference number of concatenated SMS, specified by 3GPP 23.040 9.2.3.24.1
* @param seqNumber sequence number of concatenated SMS, specified by 3GPP 23.040 9.2.3.24.1
* @param msgCount count of messages of concatenated SMS, specified by 3GPP 23.040 9.2.3.24.2
* @return a byte[] containing the encoded message
*
* @hide
*/
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
@SystemApi
@NonNull
public static byte[] getSubmitPduEncodedMessage(boolean isTypeGsm,
@NonNull String destinationAddress,
@NonNull String message,
@EncodingSize int encoding,
@IntRange(from = 0) int languageTable,
@IntRange(from = 0) int languageShiftTable,
@IntRange(from = 0, to = 255) int refNumber,
@IntRange(from = 1, to = 255) int seqNumber,
@IntRange(from = 1, to = 255) int msgCount) {
byte[] data;
SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
concatRef.refNumber = refNumber;
concatRef.seqNumber = seqNumber; // 1-based sequence
concatRef.msgCount = msgCount;
// We currently set this to true since our messaging app will never
// send more than 255 parts (it converts the message to MMS well before that).
// However, we should support 3rd party messaging apps that might need 16-bit
// references
// Note: It's not sufficient to just flip this bit to true; it will have
// ripple effects (several calculations assume 8-bit ref).
concatRef.isEightBits = true;
SmsHeader smsHeader = new SmsHeader();
smsHeader.concatRef = concatRef;
/* Depending on the type, call either GSM or CDMA getSubmitPdu(). The encoding
* will be determined(again) by getSubmitPdu().
* All packets need to be encoded using the same encoding, as the bMessage
* only have one filed to describe the encoding for all messages in a concatenated
* SMS... */
if (encoding == ENCODING_7BIT) {
smsHeader.languageTable = languageTable;
smsHeader.languageShiftTable = languageShiftTable;
}
if (isTypeGsm) {
data = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(null,
destinationAddress, message, false,
SmsHeader.toByteArray(smsHeader), encoding, languageTable,
languageShiftTable).encodedMessage;
} else { // SMS_TYPE_CDMA
UserData uData = new UserData();
uData.payloadStr = message;
uData.userDataHeader = smsHeader;
if (encoding == ENCODING_7BIT) {
uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET;
} else { // assume UTF-16
uData.msgEncoding = UserData.ENCODING_UNICODE_16;
}
uData.msgEncodingSet = true;
data = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(
destinationAddress, uData, false).encodedMessage;
}
if (data == null) {
return new byte[0];
}
return data;
}
/**
* Returns the address of the SMS service center that relayed this message
* or null if there is none.
*/
public String getServiceCenterAddress() {
return mWrappedSmsMessage.getServiceCenterAddress();
}
/**
* Returns the originating address (sender) of this SMS message in String
* form or null if unavailable.
*
* If the address is a GSM-formatted address, it will be in a format specified by 3GPP
* 23.040 Sec 9.1.2.5. If it is a CDMA address, it will be a format specified by 3GPP2
* C.S005-D Table 2.7.1.3.2.4-2. The choice of format is carrier-specific, so callers of the
* should be careful to avoid assumptions about the returned content.
*
* @return a String representation of the address; null if unavailable.
*/
@Nullable
public String getOriginatingAddress() {
return mWrappedSmsMessage.getOriginatingAddress();
}
/**
* Returns the originating address, or email from address if this message
* was from an email gateway. Returns null if originating address
* unavailable.
*/
public String getDisplayOriginatingAddress() {
return mWrappedSmsMessage.getDisplayOriginatingAddress();
}
/**
* Returns the message body as a String, if it exists and is text based.
* @return message body if there is one, otherwise null
*/
public String getMessageBody() {
return mWrappedSmsMessage.getMessageBody();
}
/**
* Returns the class of this message.
*/
public MessageClass getMessageClass() {
switch(mWrappedSmsMessage.getMessageClass()) {
case CLASS_0: return MessageClass.CLASS_0;
case CLASS_1: return MessageClass.CLASS_1;
case CLASS_2: return MessageClass.CLASS_2;
case CLASS_3: return MessageClass.CLASS_3;
default: return MessageClass.UNKNOWN;
}
}
/**
* Returns the message body, or email message body if this message was from
* an email gateway. Returns null if message body unavailable.
*/
public String getDisplayMessageBody() {
return mWrappedSmsMessage.getDisplayMessageBody();
}
/**
* Unofficial convention of a subject line enclosed in parens empty string
* if not present
*/
public String getPseudoSubject() {
return mWrappedSmsMessage.getPseudoSubject();
}
/**
* Returns the service centre timestamp in currentTimeMillis() format
*/
public long getTimestampMillis() {
return mWrappedSmsMessage.getTimestampMillis();
}
/**
* Returns true if message is an email.
*
* @return true if this message came through an email gateway and email
* sender / subject / parsed body are available
*/
public boolean isEmail() {
return mWrappedSmsMessage.isEmail();
}
/**
* @return if isEmail() is true, body of the email sent through the gateway.
* null otherwise
*/
public String getEmailBody() {
return mWrappedSmsMessage.getEmailBody();
}
/**
* @return if isEmail() is true, email from address of email sent through
* the gateway. null otherwise
*/
public String getEmailFrom() {
return mWrappedSmsMessage.getEmailFrom();
}
/**
* Get protocol identifier.
*/
public int getProtocolIdentifier() {
return mWrappedSmsMessage.getProtocolIdentifier();
}
/**
* See TS 23.040 9.2.3.9 returns true if this is a "replace short message"
* SMS
*/
public boolean isReplace() {
return mWrappedSmsMessage.isReplace();
}
/**
* Returns true for CPHS MWI toggle message.
*
* @return true if this is a CPHS MWI toggle message See CPHS 4.2 section
* B.4.2
*/
public boolean isCphsMwiMessage() {
return mWrappedSmsMessage.isCphsMwiMessage();
}
/**
* returns true if this message is a CPHS voicemail / message waiting
* indicator (MWI) clear message
*/
public boolean isMWIClearMessage() {
return mWrappedSmsMessage.isMWIClearMessage();
}
/**
* returns true if this message is a CPHS voicemail / message waiting
* indicator (MWI) set message
*/
public boolean isMWISetMessage() {
return mWrappedSmsMessage.isMWISetMessage();
}
/**
* returns true if this message is a "Message Waiting Indication Group:
* Discard Message" notification and should not be stored.
*/
public boolean isMwiDontStore() {
return mWrappedSmsMessage.isMwiDontStore();
}
/**
* returns the user data section minus the user data header if one was
* present.
*/
public byte[] getUserData() {
return mWrappedSmsMessage.getUserData();
}
/**
* Returns the raw PDU for the message.
*
* @return the raw PDU for the message.
*/
public byte[] getPdu() {
return mWrappedSmsMessage.getPdu();
}
/**
* Returns the status of the message on the SIM (read, unread, sent, unsent).
*
* @return the status of the message on the SIM. These are:
* SmsManager.STATUS_ON_SIM_FREE
* SmsManager.STATUS_ON_SIM_READ
* SmsManager.STATUS_ON_SIM_UNREAD
* SmsManager.STATUS_ON_SIM_SEND
* SmsManager.STATUS_ON_SIM_UNSENT
* @deprecated Use getStatusOnIcc instead.
*/
@Deprecated public int getStatusOnSim() {
return mWrappedSmsMessage.getStatusOnIcc();
}
/**
* Returns the status of the message on the ICC (read, unread, sent, unsent).
*
* @return the status of the message on the ICC. These are:
* SmsManager.STATUS_ON_ICC_FREE
* SmsManager.STATUS_ON_ICC_READ
* SmsManager.STATUS_ON_ICC_UNREAD
* SmsManager.STATUS_ON_ICC_SEND
* SmsManager.STATUS_ON_ICC_UNSENT
*/
public int getStatusOnIcc() {
return mWrappedSmsMessage.getStatusOnIcc();
}
/**
* Returns the record index of the message on the SIM (1-based index).
* @return the record index of the message on the SIM, or -1 if this
* SmsMessage was not created from a SIM SMS EF record.
* @deprecated Use getIndexOnIcc instead.
*/
@Deprecated public int getIndexOnSim() {
return mWrappedSmsMessage.getIndexOnIcc();
}
/**
* Returns the record index of the message on the ICC (1-based index).
* @return the record index of the message on the ICC, or -1 if this
* SmsMessage was not created from a ICC SMS EF record.
*/
public int getIndexOnIcc() {
return mWrappedSmsMessage.getIndexOnIcc();
}
/**
* GSM: For an SMS-STATUS-REPORT message, this returns the status field from the status report.
* This field indicates the status of a previously submitted SMS, if requested.
* See TS 23.040, 9.2.3.15 TP-Status for a description of values.
* CDMA: For not interfering with status codes from GSM, the value is shifted to the bits 31-16.
* The value is composed of an error class (bits 25-24) and a status code (bits 23-16). Possible
* codes are described in C.S0015-B, v2.0, 4.5.21.
*
* @return 0 for GSM or 2 shifted left by 16 for CDMA indicates the previously sent message was
* received. See TS 23.040, 9.2.3.15 and C.S0015-B, v2.0, 4.5.21 for a description of
* other possible values.
*/
public int getStatus() {
return mWrappedSmsMessage.getStatus();
}
/**
* Return true iff the message is a SMS-STATUS-REPORT message.
*/
public boolean isStatusReportMessage() {
return mWrappedSmsMessage.isStatusReportMessage();
}
/**
* Returns true iff the TP-Reply-Path
bit is set in
* this message.
*/
public boolean isReplyPathPresent() {
return mWrappedSmsMessage.isReplyPathPresent();
}
/**
* Return the encoding type of a received SMS message, which is specified using ENCODING_*
* GSM: defined in android.telephony.SmsConstants
* CDMA: defined in android.telephony.cdma.UserData
*
* @hide
*/
public int getReceivedEncodingType() {
return mWrappedSmsMessage.getReceivedEncodingType();
}
/**
* Check if format of the message is 3GPP.
*
* @hide
*/
public boolean is3gpp() {
return (mWrappedSmsMessage instanceof com.android.internal.telephony.gsm.SmsMessage);
}
/**
* Determines whether or not to use CDMA format for MO SMS.
* If SMS over IMS is supported, then format is based on IMS SMS format,
* otherwise format is based on current phone type.
*
* @return true if Cdma format should be used for MO SMS, false otherwise.
*/
@UnsupportedAppUsage
private static boolean useCdmaFormatForMoSms() {
// IMS is registered with SMS support, check the SMS format supported
return useCdmaFormatForMoSms(SmsManager.getDefaultSmsSubscriptionId());
}
/**
* Determines whether or not to use CDMA format for MO SMS.
* If SMS over IMS is supported, then format is based on IMS SMS format,
* otherwise format is based on current phone type.
*
* @param subId Subscription for which phone type is returned.
*
* @return true if Cdma format should be used for MO SMS, false otherwise.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private static boolean useCdmaFormatForMoSms(int subId) {
SmsManager smsManager = SmsManager.getSmsManagerForSubscriptionId(subId);
if (!smsManager.isImsSmsSupported()) {
// use Voice technology to determine SMS format.
return isCdmaVoice(subId);
}
// IMS is registered with SMS support, check the SMS format supported
return (SmsConstants.FORMAT_3GPP2.equals(smsManager.getImsSmsFormat()));
}
/**
* Determines whether or not to current phone type is cdma.
*
* @return true if current phone type is cdma, false otherwise.
*/
private static boolean isCdmaVoice() {
return isCdmaVoice(SmsManager.getDefaultSmsSubscriptionId());
}
/**
* Determines whether or not to current phone type is cdma
*
* @return true if current phone type is cdma, false otherwise.
*/
private static boolean isCdmaVoice(int subId) {
int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(subId);
return (PHONE_TYPE_CDMA == activePhone);
}
/**
* Decide if the carrier supports long SMS.
* {@hide}
*/
public static boolean hasEmsSupport() {
if (!isNoEmsSupportConfigListExisted()) {
return true;
}
String simOperator;
String gid;
final long identity = Binder.clearCallingIdentity();
try {
simOperator = TelephonyManager.getDefault().getSimOperatorNumeric();
gid = TelephonyManager.getDefault().getGroupIdLevel1();
} finally {
Binder.restoreCallingIdentity(identity);
}
if (!TextUtils.isEmpty(simOperator)) {
for (NoEmsSupportConfig currentConfig : mNoEmsSupportConfigList) {
if (currentConfig == null) {
Rlog.w("SmsMessage", "hasEmsSupport currentConfig is null");
continue;
}
if (simOperator.startsWith(currentConfig.mOperatorNumber) &&
(TextUtils.isEmpty(currentConfig.mGid1) ||
(!TextUtils.isEmpty(currentConfig.mGid1) &&
currentConfig.mGid1.equalsIgnoreCase(gid)))) {
return false;
}
}
}
return true;
}
/**
* Check where to add " x/y" in each SMS segment, begin or end.
* {@hide}
*/
public static boolean shouldAppendPageNumberAsPrefix() {
if (!isNoEmsSupportConfigListExisted()) {
return false;
}
String simOperator;
String gid;
final long identity = Binder.clearCallingIdentity();
try {
simOperator = TelephonyManager.getDefault().getSimOperatorNumeric();
gid = TelephonyManager.getDefault().getGroupIdLevel1();
} finally {
Binder.restoreCallingIdentity(identity);
}
for (NoEmsSupportConfig currentConfig : mNoEmsSupportConfigList) {
if (simOperator.startsWith(currentConfig.mOperatorNumber) &&
(TextUtils.isEmpty(currentConfig.mGid1) ||
(!TextUtils.isEmpty(currentConfig.mGid1)
&& currentConfig.mGid1.equalsIgnoreCase(gid)))) {
return currentConfig.mIsPrefix;
}
}
return false;
}
private static class NoEmsSupportConfig {
String mOperatorNumber;
String mGid1;
boolean mIsPrefix;
public NoEmsSupportConfig(String[] config) {
mOperatorNumber = config[0];
mIsPrefix = "prefix".equals(config[1]);
mGid1 = config.length > 2 ? config[2] : null;
}
@Override
public String toString() {
return "NoEmsSupportConfig { mOperatorNumber = " + mOperatorNumber
+ ", mIsPrefix = " + mIsPrefix + ", mGid1 = " + mGid1 + " }";
}
}
private static NoEmsSupportConfig[] mNoEmsSupportConfigList = null;
private static boolean mIsNoEmsSupportConfigListLoaded = false;
private static boolean isNoEmsSupportConfigListExisted() {
synchronized (SmsMessage.class) {
if (!mIsNoEmsSupportConfigListLoaded) {
Resources r = Resources.getSystem();
if (r != null) {
String[] listArray = r.getStringArray(
com.android.internal.R.array.no_ems_support_sim_operators);
if ((listArray != null) && (listArray.length > 0)) {
mNoEmsSupportConfigList = new NoEmsSupportConfig[listArray.length];
for (int i = 0; i < listArray.length; i++) {
mNoEmsSupportConfigList[i] = new NoEmsSupportConfig(
listArray[i].split(";"));
}
}
mIsNoEmsSupportConfigListLoaded = true;
}
}
}
if (mNoEmsSupportConfigList != null && mNoEmsSupportConfigList.length != 0) {
return true;
}
return false;
}
/**
* Returns the recipient address(receiver) of this SMS message in String form or null if
* unavailable.
* {@hide}
*/
@Nullable
public String getRecipientAddress() {
return mWrappedSmsMessage.getRecipientAddress();
}
}