Revert "Add support for CMAS warning notifications over CDMA." I'll submit again when the app change is ready.
This reverts commit 0c49f03a04
This commit is contained in:
committed by
Android (Google) Code Review
parent
0c49f03a04
commit
efba344b5a
@ -39,7 +39,6 @@ import android.os.SystemProperties;
|
||||
import android.provider.Telephony;
|
||||
import android.provider.Telephony.Sms.Intents;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.SmsCbMessage;
|
||||
import android.telephony.SmsMessage;
|
||||
import android.telephony.ServiceState;
|
||||
import android.util.Log;
|
||||
@ -1079,16 +1078,16 @@ public abstract class SMSDispatcher extends Handler {
|
||||
}
|
||||
};
|
||||
|
||||
protected void dispatchBroadcastMessage(SmsCbMessage message) {
|
||||
if (message.isEmergencyMessage()) {
|
||||
protected void dispatchBroadcastPdus(byte[][] pdus, boolean isEmergencyMessage) {
|
||||
if (isEmergencyMessage) {
|
||||
Intent intent = new Intent(Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION);
|
||||
intent.putExtra("message", message);
|
||||
Log.d(TAG, "Dispatching emergency SMS CB");
|
||||
intent.putExtra("pdus", pdus);
|
||||
Log.d(TAG, "Dispatching " + pdus.length + " emergency SMS CB pdus");
|
||||
dispatch(intent, RECEIVE_EMERGENCY_BROADCAST_PERMISSION);
|
||||
} else {
|
||||
Intent intent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
|
||||
intent.putExtra("message", message);
|
||||
Log.d(TAG, "Dispatching SMS CB");
|
||||
intent.putExtra("pdus", pdus);
|
||||
Log.d(TAG, "Dispatching " + pdus.length + " SMS CB pdus");
|
||||
dispatch(intent, RECEIVE_SMS_PERMISSION);
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ import android.os.SystemProperties;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.provider.Telephony;
|
||||
import android.provider.Telephony.Sms.Intents;
|
||||
import android.telephony.SmsCbMessage;
|
||||
import android.telephony.SmsManager;
|
||||
import android.telephony.SmsMessage.MessageClass;
|
||||
import android.util.Log;
|
||||
@ -98,10 +97,6 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleServiceCategoryProgramData(SmsMessage sms) {
|
||||
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int dispatchMessage(SmsMessageBase smsb) {
|
||||
@ -124,19 +119,8 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
|
||||
return Intents.RESULT_SMS_HANDLED;
|
||||
}
|
||||
|
||||
SmsMessage sms = (SmsMessage) smsb;
|
||||
|
||||
// Handle CMAS emergency broadcast messages.
|
||||
if (SmsEnvelope.MESSAGE_TYPE_BROADCAST == sms.getMessageType()) {
|
||||
Log.d(TAG, "Broadcast type message");
|
||||
SmsCbMessage message = sms.parseBroadcastSms();
|
||||
if (message != null) {
|
||||
dispatchBroadcastMessage(message);
|
||||
}
|
||||
return Intents.RESULT_SMS_HANDLED;
|
||||
}
|
||||
|
||||
// See if we have a network duplicate SMS.
|
||||
SmsMessage sms = (SmsMessage) smsb;
|
||||
mLastDispatchedSmsFingerprint = sms.getIncomingSmsFingerprint();
|
||||
if (mLastAcknowledgedSmsFingerprint != null &&
|
||||
Arrays.equals(mLastDispatchedSmsFingerprint, mLastAcknowledgedSmsFingerprint)) {
|
||||
@ -165,9 +149,6 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
|
||||
sms.isStatusReportMessage()) {
|
||||
handleCdmaStatusReport(sms);
|
||||
handled = true;
|
||||
} else if (SmsEnvelope.TELESERVICE_SCPT == teleService) {
|
||||
handleServiceCategoryProgramData(sms);
|
||||
handled = true;
|
||||
} else if ((sms.getUserData() == null)) {
|
||||
if (false) {
|
||||
Log.d(TAG, "Received SMS without user data");
|
||||
|
@ -19,10 +19,7 @@ package com.android.internal.telephony.cdma;
|
||||
import android.os.Parcel;
|
||||
import android.os.SystemProperties;
|
||||
import android.telephony.PhoneNumberUtils;
|
||||
import android.telephony.SmsCbLocation;
|
||||
import android.telephony.SmsCbMessage;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.telephony.IccUtils;
|
||||
import com.android.internal.telephony.SmsHeader;
|
||||
import com.android.internal.telephony.SmsMessageBase;
|
||||
@ -42,8 +39,6 @@ import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import static android.telephony.SmsMessage.MessageClass;
|
||||
|
||||
/**
|
||||
* TODO(cleanup): these constants are disturbing... are they not just
|
||||
* different interpretations on one number? And if we did not have
|
||||
@ -52,6 +47,12 @@ import static android.telephony.SmsMessage.MessageClass;
|
||||
* named CdmaSmsMessage, could it not?
|
||||
*/
|
||||
|
||||
import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES;
|
||||
import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER;
|
||||
import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS;
|
||||
import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS_WITH_HEADER;
|
||||
import static android.telephony.SmsMessage.MessageClass;
|
||||
|
||||
/**
|
||||
* TODO(cleanup): internally returning null in many places makes
|
||||
* debugging very hard (among many other reasons) and should be made
|
||||
@ -191,17 +192,16 @@ public class SmsMessage extends SmsMessageBase {
|
||||
|
||||
// bearer data
|
||||
countInt = p.readInt(); //p_cur->uBearerDataLen
|
||||
if (countInt < 0) {
|
||||
countInt = 0;
|
||||
if (countInt >0) {
|
||||
data = new byte[countInt];
|
||||
//p_cur->aBearerData[digitCount] :
|
||||
for (int index=0; index < countInt; index++) {
|
||||
data[index] = p.readByte();
|
||||
}
|
||||
env.bearerData = data;
|
||||
// BD gets further decoded when accessed in SMSDispatcher
|
||||
}
|
||||
|
||||
data = new byte[countInt];
|
||||
for (int index=0; index < countInt; index++) {
|
||||
data[index] = p.readByte();
|
||||
}
|
||||
// BD gets further decoded when accessed in SMSDispatcher
|
||||
env.bearerData = data;
|
||||
|
||||
// link the the filled objects to the SMS
|
||||
env.origAddress = addr;
|
||||
env.origSubaddress = subaddr;
|
||||
@ -731,29 +731,6 @@ public class SmsMessage extends SmsMessageBase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a broadcast SMS, possibly containing a CMAS alert.
|
||||
*/
|
||||
SmsCbMessage parseBroadcastSms() {
|
||||
BearerData bData = BearerData.decode(mEnvelope.bearerData, mEnvelope.serviceCategory);
|
||||
if (bData == null) {
|
||||
Log.w(LOG_TAG, "BearerData.decode() returned null");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Log.isLoggable(LOGGABLE_TAG, Log.VERBOSE)) {
|
||||
Log.d(LOG_TAG, "MT raw BearerData = " + HexDump.toHexString(mEnvelope.bearerData));
|
||||
}
|
||||
|
||||
String plmn = SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC);
|
||||
SmsCbLocation location = new SmsCbLocation(plmn);
|
||||
|
||||
return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP2,
|
||||
SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE, bData.messageId, location,
|
||||
mEnvelope.serviceCategory, bData.getLanguage(), bData.userData.payloadStr,
|
||||
bData.priority, null, bData.cmasWarningInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
@ -16,30 +16,29 @@
|
||||
|
||||
package com.android.internal.telephony.cdma.sms;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.telephony.SmsCbCmasInfo;
|
||||
import android.telephony.SmsCbMessage;
|
||||
import android.telephony.SmsMessage;
|
||||
import android.telephony.cdma.CdmaSmsCbProgramData;
|
||||
import android.text.format.Time;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.telephony.GsmAlphabet;
|
||||
import com.android.internal.telephony.IccUtils;
|
||||
import com.android.internal.telephony.SmsHeader;
|
||||
import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
|
||||
import com.android.internal.util.BitwiseInputStream;
|
||||
import com.android.internal.util.BitwiseOutputStream;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import static android.telephony.SmsMessage.ENCODING_16BIT;
|
||||
import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES;
|
||||
import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import android.telephony.SmsMessage;
|
||||
|
||||
import android.text.format.Time;
|
||||
|
||||
import com.android.internal.telephony.IccUtils;
|
||||
import com.android.internal.telephony.GsmAlphabet;
|
||||
import com.android.internal.telephony.SmsHeader;
|
||||
import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
|
||||
|
||||
import com.android.internal.util.BitwiseInputStream;
|
||||
import com.android.internal.util.BitwiseOutputStream;
|
||||
|
||||
import android.content.res.Resources;
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
|
||||
/**
|
||||
* An object to encode and decode CDMA SMS bearer data.
|
||||
*/
|
||||
@ -69,8 +68,8 @@ public final class BearerData {
|
||||
private final static byte SUBPARAM_MESSAGE_DISPLAY_MODE = 0x0F;
|
||||
//private final static byte SUBPARAM_MULTIPLE_ENCODING_USER_DATA = 0x10;
|
||||
private final static byte SUBPARAM_MESSAGE_DEPOSIT_INDEX = 0x11;
|
||||
private final static byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA = 0x12;
|
||||
private final static byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_RESULTS = 0x13;
|
||||
//private final static byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA = 0x12;
|
||||
//private final static byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_RESULTS = 0x13;
|
||||
private final static byte SUBPARAM_MESSAGE_STATUS = 0x14;
|
||||
//private final static byte SUBPARAM_TP_FAILURE_CAUSE = 0x15;
|
||||
//private final static byte SUBPARAM_ENHANCED_VMN = 0x16;
|
||||
@ -340,68 +339,12 @@ public final class BearerData {
|
||||
*/
|
||||
public CdmaSmsAddress callbackNumber;
|
||||
|
||||
/**
|
||||
* CMAS warning notification information.
|
||||
* @see #decodeCmasUserData(BearerData, int)
|
||||
*/
|
||||
public SmsCbCmasInfo cmasWarningInfo;
|
||||
|
||||
/**
|
||||
* The Service Category Program Data subparameter is used to enable and disable
|
||||
* SMS broadcast service categories to display. If this subparameter is present,
|
||||
* this field will contain a list of one or more
|
||||
* {@link android.telephony.cdma.CdmaSmsCbProgramData} objects containing the
|
||||
* operation(s) to perform.
|
||||
*/
|
||||
public List<CdmaSmsCbProgramData> serviceCategoryProgramData;
|
||||
|
||||
private static class CodingException extends Exception {
|
||||
public CodingException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the language indicator as a two-character ISO 639 string.
|
||||
* @return a two character ISO 639 language code
|
||||
*/
|
||||
public String getLanguage() {
|
||||
return getLanguageCodeForValue(language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a CDMA language indicator value to an ISO 639 two character language code.
|
||||
* @param languageValue the CDMA language value to convert
|
||||
* @return the two character ISO 639 language code for the specified value, or null if unknown
|
||||
*/
|
||||
private static String getLanguageCodeForValue(int languageValue) {
|
||||
switch (languageValue) {
|
||||
case LANGUAGE_ENGLISH:
|
||||
return "en";
|
||||
|
||||
case LANGUAGE_FRENCH:
|
||||
return "fr";
|
||||
|
||||
case LANGUAGE_SPANISH:
|
||||
return "es";
|
||||
|
||||
case LANGUAGE_JAPANESE:
|
||||
return "ja";
|
||||
|
||||
case LANGUAGE_KOREAN:
|
||||
return "ko";
|
||||
|
||||
case LANGUAGE_CHINESE:
|
||||
return "zh";
|
||||
|
||||
case LANGUAGE_HEBREW:
|
||||
return "he";
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
@ -973,9 +916,6 @@ public final class BearerData {
|
||||
private static String decodeUtf8(byte[] data, int offset, int numFields)
|
||||
throws CodingException
|
||||
{
|
||||
if (numFields < 0 || (numFields + offset) > data.length) {
|
||||
throw new CodingException("UTF-8 decode failed: offset or length out of range");
|
||||
}
|
||||
try {
|
||||
return new String(data, offset, numFields, "UTF-8");
|
||||
} catch (java.io.UnsupportedEncodingException ex) {
|
||||
@ -986,12 +926,11 @@ public final class BearerData {
|
||||
private static String decodeUtf16(byte[] data, int offset, int numFields)
|
||||
throws CodingException
|
||||
{
|
||||
int byteCount = numFields * 2;
|
||||
if (byteCount < 0 || (byteCount + offset) > data.length) {
|
||||
throw new CodingException("UTF-16 decode failed: offset or length out of range");
|
||||
}
|
||||
// Start reading from the next 16-bit aligned boundary after offset.
|
||||
int padding = offset % 2;
|
||||
numFields -= (offset + padding) / 2;
|
||||
try {
|
||||
return new String(data, offset, byteCount, "utf-16be");
|
||||
return new String(data, offset, numFields * 2, "utf-16be");
|
||||
} catch (java.io.UnsupportedEncodingException ex) {
|
||||
throw new CodingException("UTF-16 decode failed: " + ex);
|
||||
}
|
||||
@ -1049,11 +988,8 @@ public final class BearerData {
|
||||
private static String decodeLatin(byte[] data, int offset, int numFields)
|
||||
throws CodingException
|
||||
{
|
||||
if (numFields < 0 || (numFields + offset) > data.length) {
|
||||
throw new CodingException("ISO-8859-1 decode failed: offset or length out of range");
|
||||
}
|
||||
try {
|
||||
return new String(data, offset, numFields, "ISO-8859-1");
|
||||
return new String(data, offset, numFields - offset, "ISO-8859-1");
|
||||
} catch (java.io.UnsupportedEncodingException ex) {
|
||||
throw new CodingException("ISO-8859-1 decode failed: " + ex);
|
||||
}
|
||||
@ -1096,7 +1032,6 @@ public final class BearerData {
|
||||
userData.payloadStr = decodeUtf8(userData.payload, offset, userData.numFields);
|
||||
}
|
||||
break;
|
||||
|
||||
case UserData.ENCODING_IA5:
|
||||
case UserData.ENCODING_7BIT_ASCII:
|
||||
userData.payloadStr = decode7bitAscii(userData.payload, offset, userData.numFields);
|
||||
@ -1179,9 +1114,8 @@ public final class BearerData {
|
||||
BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
|
||||
int dataLen = inStream.available() / 6; // 6-bit packed character encoding.
|
||||
int numFields = bData.userData.numFields;
|
||||
// dataLen may be > 14 characters due to octet padding
|
||||
if ((numFields > 14) || (dataLen < numFields)) {
|
||||
throw new CodingException("IS-91 short message decoding failed");
|
||||
if ((dataLen > 14) || (dataLen < numFields)) {
|
||||
throw new CodingException("IS-91 voicemail status decoding failed");
|
||||
}
|
||||
StringBuffer strbuf = new StringBuffer(dataLen);
|
||||
for (int i = 0; i < numFields; i++) {
|
||||
@ -1605,7 +1539,7 @@ public final class BearerData {
|
||||
bData.userResponseCode = inStream.read(8);
|
||||
}
|
||||
if ((! decodeSuccess) || (paramBits > 0)) {
|
||||
Log.d(LOG_TAG, "USER_RESPONSE_CODE decode " +
|
||||
Log.d(LOG_TAG, "USER_REPONSE_CODE decode " +
|
||||
(decodeSuccess ? "succeeded" : "failed") +
|
||||
" (extra bits = " + paramBits + ")");
|
||||
}
|
||||
@ -1614,240 +1548,27 @@ public final class BearerData {
|
||||
return decodeSuccess;
|
||||
}
|
||||
|
||||
private static boolean decodeServiceCategoryProgramData(BearerData bData,
|
||||
BitwiseInputStream inStream) throws BitwiseInputStream.AccessException, CodingException
|
||||
{
|
||||
if (inStream.available() < 13) {
|
||||
throw new CodingException("SERVICE_CATEGORY_PROGRAM_DATA decode failed: only "
|
||||
+ inStream.available() + " bits available");
|
||||
}
|
||||
|
||||
int paramBits = inStream.read(8) * 8;
|
||||
int msgEncoding = inStream.read(5);
|
||||
paramBits -= 5;
|
||||
|
||||
if (inStream.available() < paramBits) {
|
||||
throw new CodingException("SERVICE_CATEGORY_PROGRAM_DATA decode failed: only "
|
||||
+ inStream.available() + " bits available (" + paramBits + " bits expected)");
|
||||
}
|
||||
|
||||
ArrayList<CdmaSmsCbProgramData> programDataList = new ArrayList<CdmaSmsCbProgramData>();
|
||||
|
||||
final int CATEGORY_FIELD_MIN_SIZE = 6 * 8;
|
||||
boolean decodeSuccess = false;
|
||||
while (paramBits >= CATEGORY_FIELD_MIN_SIZE) {
|
||||
int operation = inStream.read(4);
|
||||
int category = (inStream.read(8) << 8) | inStream.read(8);
|
||||
String language = getLanguageCodeForValue(inStream.read(8));
|
||||
int maxMessages = inStream.read(8);
|
||||
int alertOption = inStream.read(4);
|
||||
int numFields = inStream.read(8);
|
||||
paramBits -= CATEGORY_FIELD_MIN_SIZE;
|
||||
|
||||
int textBits = getBitsForNumFields(msgEncoding, numFields);
|
||||
if (paramBits < textBits) {
|
||||
throw new CodingException("category name is " + textBits + " bits in length,"
|
||||
+ " but there are only " + paramBits + " bits available");
|
||||
}
|
||||
|
||||
UserData userData = new UserData();
|
||||
userData.msgEncoding = msgEncoding;
|
||||
userData.msgEncodingSet = true;
|
||||
userData.numFields = numFields;
|
||||
userData.payload = inStream.readByteArray(textBits);
|
||||
paramBits -= textBits;
|
||||
|
||||
decodeUserDataPayload(userData, false);
|
||||
String categoryName = userData.payloadStr;
|
||||
CdmaSmsCbProgramData programData = new CdmaSmsCbProgramData(operation, category,
|
||||
language, maxMessages, alertOption, categoryName);
|
||||
programDataList.add(programData);
|
||||
|
||||
decodeSuccess = true;
|
||||
}
|
||||
|
||||
if ((! decodeSuccess) || (paramBits > 0)) {
|
||||
Log.d(LOG_TAG, "SERVICE_CATEGORY_PROGRAM_DATA decode " +
|
||||
(decodeSuccess ? "succeeded" : "failed") +
|
||||
" (extra bits = " + paramBits + ')');
|
||||
}
|
||||
|
||||
inStream.skip(paramBits);
|
||||
bData.serviceCategoryProgramData = programDataList;
|
||||
return decodeSuccess;
|
||||
}
|
||||
|
||||
private static int serviceCategoryToCmasMessageClass(int serviceCategory) {
|
||||
switch (serviceCategory) {
|
||||
case SmsEnvelope.SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT:
|
||||
return SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT;
|
||||
|
||||
case SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT:
|
||||
return SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT;
|
||||
|
||||
case SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT:
|
||||
return SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT;
|
||||
|
||||
case SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY:
|
||||
return SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY;
|
||||
|
||||
case SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE:
|
||||
return SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST;
|
||||
|
||||
default:
|
||||
return SmsCbCmasInfo.CMAS_CLASS_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the number of bits to read for the specified number of encoded characters.
|
||||
* @param msgEncoding the message encoding to use
|
||||
* @param numFields the number of characters to read. For Shift-JIS and Korean encodings,
|
||||
* this is the number of bytes to read.
|
||||
* @return the number of bits to read from the stream
|
||||
* @throws CodingException if the specified encoding is not supported
|
||||
*/
|
||||
private static int getBitsForNumFields(int msgEncoding, int numFields)
|
||||
throws CodingException {
|
||||
switch (msgEncoding) {
|
||||
case UserData.ENCODING_OCTET:
|
||||
case UserData.ENCODING_SHIFT_JIS:
|
||||
case UserData.ENCODING_KOREAN:
|
||||
case UserData.ENCODING_LATIN:
|
||||
case UserData.ENCODING_LATIN_HEBREW:
|
||||
return numFields * 8;
|
||||
|
||||
case UserData.ENCODING_IA5:
|
||||
case UserData.ENCODING_7BIT_ASCII:
|
||||
case UserData.ENCODING_GSM_7BIT_ALPHABET:
|
||||
return numFields * 7;
|
||||
|
||||
case UserData.ENCODING_UNICODE_16:
|
||||
return numFields * 16;
|
||||
|
||||
default:
|
||||
throw new CodingException("unsupported message encoding (" + msgEncoding + ')');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CMAS message decoding.
|
||||
* (See TIA-1149-0-1, CMAS over CDMA)
|
||||
*
|
||||
* @param serviceCategory is the service category from the SMS envelope
|
||||
*/
|
||||
private static void decodeCmasUserData(BearerData bData, int serviceCategory)
|
||||
throws BitwiseInputStream.AccessException, CodingException {
|
||||
BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
|
||||
if (inStream.available() < 8) {
|
||||
throw new CodingException("emergency CB with no CMAE_protocol_version");
|
||||
}
|
||||
int protocolVersion = inStream.read(8);
|
||||
if (protocolVersion != 0) {
|
||||
throw new CodingException("unsupported CMAE_protocol_version " + protocolVersion);
|
||||
}
|
||||
|
||||
int messageClass = serviceCategoryToCmasMessageClass(serviceCategory);
|
||||
int category = SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN;
|
||||
int responseType = SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN;
|
||||
int severity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN;
|
||||
int urgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN;
|
||||
int certainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN;
|
||||
|
||||
while (inStream.available() >= 16) {
|
||||
int recordType = inStream.read(8);
|
||||
int recordLen = inStream.read(8);
|
||||
switch (recordType) {
|
||||
case 0: // Type 0 elements (Alert text)
|
||||
UserData alertUserData = new UserData();
|
||||
alertUserData.msgEncoding = inStream.read(5);
|
||||
alertUserData.msgEncodingSet = true;
|
||||
alertUserData.msgType = 0;
|
||||
|
||||
int numFields; // number of chars to decode
|
||||
switch (alertUserData.msgEncoding) {
|
||||
case UserData.ENCODING_OCTET:
|
||||
case UserData.ENCODING_LATIN:
|
||||
numFields = recordLen - 1; // subtract 1 byte for encoding
|
||||
break;
|
||||
|
||||
case UserData.ENCODING_IA5:
|
||||
case UserData.ENCODING_7BIT_ASCII:
|
||||
case UserData.ENCODING_GSM_7BIT_ALPHABET:
|
||||
numFields = ((recordLen * 8) - 5) / 7; // subtract 5 bits for encoding
|
||||
break;
|
||||
|
||||
case UserData.ENCODING_UNICODE_16:
|
||||
numFields = (recordLen - 1) / 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
numFields = 0; // unsupported encoding
|
||||
}
|
||||
|
||||
alertUserData.numFields = numFields;
|
||||
alertUserData.payload = inStream.readByteArray(recordLen * 8 - 5);
|
||||
decodeUserDataPayload(alertUserData, false);
|
||||
bData.userData = alertUserData;
|
||||
break;
|
||||
|
||||
case 1: // Type 1 elements
|
||||
category = inStream.read(8);
|
||||
responseType = inStream.read(8);
|
||||
severity = inStream.read(4);
|
||||
urgency = inStream.read(4);
|
||||
certainty = inStream.read(4);
|
||||
inStream.skip(recordLen * 8 - 28);
|
||||
break;
|
||||
|
||||
default:
|
||||
Log.w(LOG_TAG, "skipping unsupported CMAS record type " + recordType);
|
||||
inStream.skip(recordLen * 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bData.cmasWarningInfo = new SmsCbCmasInfo(messageClass, category, responseType, severity,
|
||||
urgency, certainty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create BearerData object from serialized representation.
|
||||
* (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
|
||||
*
|
||||
* @param smsData byte array of raw encoded SMS bearer data.
|
||||
*
|
||||
* @return an instance of BearerData.
|
||||
*/
|
||||
public static BearerData decode(byte[] smsData) {
|
||||
return decode(smsData, 0);
|
||||
}
|
||||
|
||||
private static boolean isCmasAlertCategory(int category) {
|
||||
return category >= SmsEnvelope.SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT
|
||||
&& category <= SmsEnvelope.SERVICE_CATEGORY_CMAS_LAST_RESERVED_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create BearerData object from serialized representation.
|
||||
* (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
|
||||
*
|
||||
* @param smsData byte array of raw encoded SMS bearer data.
|
||||
* @param serviceCategory the envelope service category (for CMAS alert handling)
|
||||
* @return an instance of BearerData.
|
||||
*/
|
||||
public static BearerData decode(byte[] smsData, int serviceCategory) {
|
||||
try {
|
||||
BitwiseInputStream inStream = new BitwiseInputStream(smsData);
|
||||
BearerData bData = new BearerData();
|
||||
int foundSubparamMask = 0;
|
||||
while (inStream.available() > 0) {
|
||||
boolean decodeSuccess = false;
|
||||
int subparamId = inStream.read(8);
|
||||
int subparamIdBit = 1 << subparamId;
|
||||
if ((foundSubparamMask & subparamIdBit) != 0) {
|
||||
throw new CodingException("illegal duplicate subparameter (" +
|
||||
subparamId + ")");
|
||||
}
|
||||
boolean decodeSuccess;
|
||||
switch (subparamId) {
|
||||
case SUBPARAM_MESSAGE_IDENTIFIER:
|
||||
decodeSuccess = decodeMessageId(bData, inStream);
|
||||
@ -1903,9 +1624,6 @@ public final class BearerData {
|
||||
case SUBPARAM_MESSAGE_DEPOSIT_INDEX:
|
||||
decodeSuccess = decodeDepositIndex(bData, inStream);
|
||||
break;
|
||||
case SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA:
|
||||
decodeSuccess = decodeServiceCategoryProgramData(bData, inStream);
|
||||
break;
|
||||
default:
|
||||
throw new CodingException("unsupported bearer data subparameter ("
|
||||
+ subparamId + ")");
|
||||
@ -1916,10 +1634,7 @@ public final class BearerData {
|
||||
throw new CodingException("missing MESSAGE_IDENTIFIER subparam");
|
||||
}
|
||||
if (bData.userData != null) {
|
||||
if (isCmasAlertCategory(serviceCategory) && bData.priorityIndicatorSet
|
||||
&& bData.priority == SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY) {
|
||||
decodeCmasUserData(bData, serviceCategory);
|
||||
} else if (bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) {
|
||||
if (bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) {
|
||||
if ((foundSubparamMask ^
|
||||
(1 << SUBPARAM_MESSAGE_IDENTIFIER) ^
|
||||
(1 << SUBPARAM_USER_DATA))
|
||||
|
@ -37,7 +37,6 @@ public final class SmsEnvelope {
|
||||
static public final int TELESERVICE_VMN = 0x1003;
|
||||
static public final int TELESERVICE_WAP = 0x1004;
|
||||
static public final int TELESERVICE_WEMT = 0x1005;
|
||||
static public final int TELESERVICE_SCPT = 0x1006;
|
||||
|
||||
/**
|
||||
* The following are defined as extensions to the standard teleservices
|
||||
@ -47,17 +46,14 @@ public final class SmsEnvelope {
|
||||
// number of messages waiting, it's used by some CDMA carriers for a voice mail count.
|
||||
static public final int TELESERVICE_MWI = 0x40000;
|
||||
|
||||
// Service Categories for Cell Broadcast, see 3GPP2 C.R1001 table 9.3.1-1
|
||||
// static final int SERVICE_CATEGORY_EMERGENCY = 0x0001;
|
||||
// ServiceCategories for Cell Broadcast, see 3GPP2 C.R1001 table 9.3.1-1
|
||||
//static public final int SERVICECATEGORY_EMERGENCY = 0x0010;
|
||||
//...
|
||||
|
||||
// CMAS alert service category assignments, see 3GPP2 C.R1001 table 9.3.3-1
|
||||
public static final int SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT = 0x1000;
|
||||
public static final int SERVICE_CATEGORY_CMAS_EXTREME_THREAT = 0x1001;
|
||||
public static final int SERVICE_CATEGORY_CMAS_SEVERE_THREAT = 0x1002;
|
||||
public static final int SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY = 0x1003;
|
||||
public static final int SERVICE_CATEGORY_CMAS_TEST_MESSAGE = 0x1004;
|
||||
public static final int SERVICE_CATEGORY_CMAS_LAST_RESERVED_VALUE = 0x10ff;
|
||||
/**
|
||||
* maximum lengths for fields as defined in ril_cdma_sms.h
|
||||
*/
|
||||
static public final int SMS_BEARER_DATA_MAX = 255;
|
||||
|
||||
/**
|
||||
* Provides the type of a SMS message like point to point, broadcast or acknowledge
|
||||
|
@ -32,9 +32,9 @@ public class UserData {
|
||||
public static final int ENCODING_7BIT_ASCII = 0x02;
|
||||
public static final int ENCODING_IA5 = 0x03;
|
||||
public static final int ENCODING_UNICODE_16 = 0x04;
|
||||
public static final int ENCODING_SHIFT_JIS = 0x05;
|
||||
public static final int ENCODING_KOREAN = 0x06;
|
||||
public static final int ENCODING_LATIN_HEBREW = 0x07;
|
||||
//public static final int ENCODING_SHIFT_JIS = 0x05;
|
||||
//public static final int ENCODING_KOREAN = 0x06;
|
||||
//public static final int ENCODING_LATIN_HEBREW = 0x07;
|
||||
public static final int ENCODING_LATIN = 0x08;
|
||||
public static final int ENCODING_GSM_7BIT_ALPHABET = 0x09;
|
||||
public static final int ENCODING_GSM_DCS = 0x0A;
|
||||
|
@ -26,7 +26,6 @@ import android.os.SystemProperties;
|
||||
import android.provider.Telephony.Sms;
|
||||
import android.provider.Telephony.Sms.Intents;
|
||||
import android.telephony.PhoneNumberUtils;
|
||||
import android.telephony.SmsCbLocation;
|
||||
import android.telephony.SmsCbMessage;
|
||||
import android.telephony.SmsManager;
|
||||
import android.telephony.gsm.GsmCellLocation;
|
||||
@ -319,18 +318,24 @@ public final class GsmSMSDispatcher extends SMSDispatcher {
|
||||
* concatenated message
|
||||
*/
|
||||
private static final class SmsCbConcatInfo {
|
||||
|
||||
private final SmsCbHeader mHeader;
|
||||
private final SmsCbLocation mLocation;
|
||||
|
||||
public SmsCbConcatInfo(SmsCbHeader header, SmsCbLocation location) {
|
||||
private final String mPlmn;
|
||||
|
||||
private final int mLac;
|
||||
|
||||
private final int mCid;
|
||||
|
||||
public SmsCbConcatInfo(SmsCbHeader header, String plmn, int lac, int cid) {
|
||||
mHeader = header;
|
||||
mLocation = location;
|
||||
mPlmn = plmn;
|
||||
mLac = lac;
|
||||
mCid = cid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (mHeader.getSerialNumber() * 31) + mLocation.hashCode();
|
||||
return mHeader.messageIdentifier * 31 + mHeader.updateNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -338,28 +343,49 @@ public final class GsmSMSDispatcher extends SMSDispatcher {
|
||||
if (obj instanceof SmsCbConcatInfo) {
|
||||
SmsCbConcatInfo other = (SmsCbConcatInfo)obj;
|
||||
|
||||
// Two pages match if they have the same serial number (which includes the
|
||||
// geographical scope and update number), and both pages belong to the same
|
||||
// location (PLMN, plus LAC and CID if these are part of the geographical scope).
|
||||
return mHeader.getSerialNumber() == other.mHeader.getSerialNumber()
|
||||
&& mLocation.equals(other.mLocation);
|
||||
// Two pages match if all header attributes (except the page
|
||||
// index) are identical, and both pages belong to the same
|
||||
// location (which is also determined by the scope parameter)
|
||||
if (mHeader.geographicalScope == other.mHeader.geographicalScope
|
||||
&& mHeader.messageCode == other.mHeader.messageCode
|
||||
&& mHeader.updateNumber == other.mHeader.updateNumber
|
||||
&& mHeader.messageIdentifier == other.mHeader.messageIdentifier
|
||||
&& mHeader.dataCodingScheme == other.mHeader.dataCodingScheme
|
||||
&& mHeader.nrOfPages == other.mHeader.nrOfPages) {
|
||||
return matchesLocation(other.mPlmn, other.mLac, other.mCid);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the location code for this message to the current location code. The match is
|
||||
* relative to the geographical scope of the message, which determines whether the LAC
|
||||
* and Cell ID are saved in mLocation or set to -1 to match all values.
|
||||
* Checks if this concatenation info matches the given location. The
|
||||
* granularity of the match depends on the geographical scope.
|
||||
*
|
||||
* @param plmn the current PLMN
|
||||
* @param lac the current Location Area (GSM) or Service Area (UMTS)
|
||||
* @param cid the current Cell ID
|
||||
* @return true if this message is valid for the current location; false otherwise
|
||||
* @param plmn PLMN
|
||||
* @param lac Location area code
|
||||
* @param cid Cell ID
|
||||
* @return true if matching, false otherwise
|
||||
*/
|
||||
public boolean matchesLocation(String plmn, int lac, int cid) {
|
||||
return mLocation.isInLocationArea(plmn, lac, cid);
|
||||
switch (mHeader.geographicalScope) {
|
||||
case SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE:
|
||||
case SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE:
|
||||
if (mCid != cid) {
|
||||
return false;
|
||||
}
|
||||
// deliberate fall-through
|
||||
case SmsCbMessage.GEOGRAPHICAL_SCOPE_LA_WIDE:
|
||||
if (mLac != lac) {
|
||||
return false;
|
||||
}
|
||||
// deliberate fall-through
|
||||
case SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE:
|
||||
return mPlmn != null && mPlmn.equals(plmn);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -395,42 +421,24 @@ public final class GsmSMSDispatcher extends SMSDispatcher {
|
||||
int lac = cellLocation.getLac();
|
||||
int cid = cellLocation.getCid();
|
||||
|
||||
SmsCbLocation location;
|
||||
switch (header.getGeographicalScope()) {
|
||||
case SmsCbMessage.GEOGRAPHICAL_SCOPE_LA_WIDE:
|
||||
location = new SmsCbLocation(plmn, lac, -1);
|
||||
break;
|
||||
|
||||
case SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE:
|
||||
case SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE:
|
||||
location = new SmsCbLocation(plmn, lac, cid);
|
||||
break;
|
||||
|
||||
case SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE:
|
||||
default:
|
||||
location = new SmsCbLocation(plmn);
|
||||
break;
|
||||
}
|
||||
|
||||
byte[][] pdus;
|
||||
int pageCount = header.getNumberOfPages();
|
||||
if (pageCount > 1) {
|
||||
if (header.nrOfPages > 1) {
|
||||
// Multi-page message
|
||||
SmsCbConcatInfo concatInfo = new SmsCbConcatInfo(header, location);
|
||||
SmsCbConcatInfo concatInfo = new SmsCbConcatInfo(header, plmn, lac, cid);
|
||||
|
||||
// Try to find other pages of the same message
|
||||
pdus = mSmsCbPageMap.get(concatInfo);
|
||||
|
||||
if (pdus == null) {
|
||||
// This is the first page of this message, make room for all
|
||||
// This it the first page of this message, make room for all
|
||||
// pages and keep until complete
|
||||
pdus = new byte[pageCount][];
|
||||
pdus = new byte[header.nrOfPages][];
|
||||
|
||||
mSmsCbPageMap.put(concatInfo, pdus);
|
||||
}
|
||||
|
||||
// Page parameter is one-based
|
||||
pdus[header.getPageIndex() - 1] = receivedPdu;
|
||||
pdus[header.pageIndex - 1] = receivedPdu;
|
||||
|
||||
for (int i = 0; i < pdus.length; i++) {
|
||||
if (pdus[i] == null) {
|
||||
@ -447,8 +455,8 @@ public final class GsmSMSDispatcher extends SMSDispatcher {
|
||||
pdus[0] = receivedPdu;
|
||||
}
|
||||
|
||||
SmsCbMessage message = GsmSmsCbMessage.createSmsCbMessage(header, location, pdus);
|
||||
dispatchBroadcastMessage(message);
|
||||
boolean isEmergencyMessage = SmsCbHeader.isEmergencyMessage(header.messageIdentifier);
|
||||
dispatchBroadcastPdus(pdus, isEmergencyMessage);
|
||||
|
||||
// Remove messages that are out of scope to prevent the map from
|
||||
// growing indefinitely, containing incomplete messages that were
|
||||
|
@ -1,286 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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 com.android.internal.telephony.gsm;
|
||||
|
||||
import android.telephony.SmsCbLocation;
|
||||
import android.telephony.SmsCbMessage;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.android.internal.telephony.GsmAlphabet;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* Parses a GSM or UMTS format SMS-CB message into an {@link SmsCbMessage} object. The class is
|
||||
* public because {@link #createSmsCbMessage(SmsCbLocation, byte[][])} is used by some test cases.
|
||||
*/
|
||||
public class GsmSmsCbMessage {
|
||||
|
||||
/**
|
||||
* Languages in the 0000xxxx DCS group as defined in 3GPP TS 23.038, section 5.
|
||||
*/
|
||||
private static final String[] LANGUAGE_CODES_GROUP_0 = {
|
||||
"de", "en", "it", "fr", "es", "nl", "sv", "da", "pt", "fi", "no", "el", "tr", "hu",
|
||||
"pl", null
|
||||
};
|
||||
|
||||
/**
|
||||
* Languages in the 0010xxxx DCS group as defined in 3GPP TS 23.038, section 5.
|
||||
*/
|
||||
private static final String[] LANGUAGE_CODES_GROUP_2 = {
|
||||
"cs", "he", "ar", "ru", "is", null, null, null, null, null, null, null, null, null,
|
||||
null, null
|
||||
};
|
||||
|
||||
private static final char CARRIAGE_RETURN = 0x0d;
|
||||
|
||||
private static final int PDU_BODY_PAGE_LENGTH = 82;
|
||||
|
||||
/** Utility class with only static methods. */
|
||||
private GsmSmsCbMessage() { }
|
||||
|
||||
/**
|
||||
* Create a new SmsCbMessage object from a header object plus one or more received PDUs.
|
||||
*
|
||||
* @param pdus PDU bytes
|
||||
*/
|
||||
static SmsCbMessage createSmsCbMessage(SmsCbHeader header, SmsCbLocation location,
|
||||
byte[][] pdus) throws IllegalArgumentException {
|
||||
if (header.isEtwsPrimaryNotification()) {
|
||||
return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP,
|
||||
header.getGeographicalScope(), header.getSerialNumber(),
|
||||
location, header.getServiceCategory(),
|
||||
null, "ETWS", SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY,
|
||||
header.getEtwsInfo(), header.getCmasInfo());
|
||||
} else {
|
||||
String language = null;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte[] pdu : pdus) {
|
||||
Pair<String, String> p = parseBody(header, pdu);
|
||||
language = p.first;
|
||||
sb.append(p.second);
|
||||
}
|
||||
int priority = header.isEmergencyMessage() ? SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY
|
||||
: SmsCbMessage.MESSAGE_PRIORITY_NORMAL;
|
||||
|
||||
return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP,
|
||||
header.getGeographicalScope(), header.getSerialNumber(), location,
|
||||
header.getServiceCategory(), language, sb.toString(), priority,
|
||||
header.getEtwsInfo(), header.getCmasInfo());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new SmsCbMessage object from one or more received PDUs. This is used by some
|
||||
* CellBroadcastReceiver test cases, because SmsCbHeader is now package local.
|
||||
*
|
||||
* @param location the location (geographical scope) for the message
|
||||
* @param pdus PDU bytes
|
||||
*/
|
||||
public static SmsCbMessage createSmsCbMessage(SmsCbLocation location, byte[][] pdus)
|
||||
throws IllegalArgumentException {
|
||||
SmsCbHeader header = new SmsCbHeader(pdus[0]);
|
||||
return createSmsCbMessage(header, location, pdus);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and unpack the body text according to the encoding in the DCS.
|
||||
* After completing successfully this method will have assigned the body
|
||||
* text into mBody, and optionally the language code into mLanguage
|
||||
*
|
||||
* @param header the message header to use
|
||||
* @param pdu the PDU to decode
|
||||
* @return a Pair of Strings containing the language and body of the message
|
||||
*/
|
||||
private static Pair<String, String> parseBody(SmsCbHeader header, byte[] pdu) {
|
||||
int encoding;
|
||||
String language = null;
|
||||
boolean hasLanguageIndicator = false;
|
||||
int dataCodingScheme = header.getDataCodingScheme();
|
||||
|
||||
// Extract encoding and language from DCS, as defined in 3gpp TS 23.038,
|
||||
// section 5.
|
||||
switch ((dataCodingScheme & 0xf0) >> 4) {
|
||||
case 0x00:
|
||||
encoding = android.telephony.SmsMessage.ENCODING_7BIT;
|
||||
language = LANGUAGE_CODES_GROUP_0[dataCodingScheme & 0x0f];
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
hasLanguageIndicator = true;
|
||||
if ((dataCodingScheme & 0x0f) == 0x01) {
|
||||
encoding = android.telephony.SmsMessage.ENCODING_16BIT;
|
||||
} else {
|
||||
encoding = android.telephony.SmsMessage.ENCODING_7BIT;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
encoding = android.telephony.SmsMessage.ENCODING_7BIT;
|
||||
language = LANGUAGE_CODES_GROUP_2[dataCodingScheme & 0x0f];
|
||||
break;
|
||||
|
||||
case 0x03:
|
||||
encoding = android.telephony.SmsMessage.ENCODING_7BIT;
|
||||
break;
|
||||
|
||||
case 0x04:
|
||||
case 0x05:
|
||||
switch ((dataCodingScheme & 0x0c) >> 2) {
|
||||
case 0x01:
|
||||
encoding = android.telephony.SmsMessage.ENCODING_8BIT;
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
encoding = android.telephony.SmsMessage.ENCODING_16BIT;
|
||||
break;
|
||||
|
||||
case 0x00:
|
||||
default:
|
||||
encoding = android.telephony.SmsMessage.ENCODING_7BIT;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x06:
|
||||
case 0x07:
|
||||
// Compression not supported
|
||||
case 0x09:
|
||||
// UDH structure not supported
|
||||
case 0x0e:
|
||||
// Defined by the WAP forum not supported
|
||||
throw new IllegalArgumentException("Unsupported GSM dataCodingScheme "
|
||||
+ dataCodingScheme);
|
||||
|
||||
case 0x0f:
|
||||
if (((dataCodingScheme & 0x04) >> 2) == 0x01) {
|
||||
encoding = android.telephony.SmsMessage.ENCODING_8BIT;
|
||||
} else {
|
||||
encoding = android.telephony.SmsMessage.ENCODING_7BIT;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Reserved values are to be treated as 7-bit
|
||||
encoding = android.telephony.SmsMessage.ENCODING_7BIT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (header.isUmtsFormat()) {
|
||||
// Payload may contain multiple pages
|
||||
int nrPages = pdu[SmsCbHeader.PDU_HEADER_LENGTH];
|
||||
|
||||
if (pdu.length < SmsCbHeader.PDU_HEADER_LENGTH + 1 + (PDU_BODY_PAGE_LENGTH + 1)
|
||||
* nrPages) {
|
||||
throw new IllegalArgumentException("Pdu length " + pdu.length + " does not match "
|
||||
+ nrPages + " pages");
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < nrPages; i++) {
|
||||
// Each page is 82 bytes followed by a length octet indicating
|
||||
// the number of useful octets within those 82
|
||||
int offset = SmsCbHeader.PDU_HEADER_LENGTH + 1 + (PDU_BODY_PAGE_LENGTH + 1) * i;
|
||||
int length = pdu[offset + PDU_BODY_PAGE_LENGTH];
|
||||
|
||||
if (length > PDU_BODY_PAGE_LENGTH) {
|
||||
throw new IllegalArgumentException("Page length " + length
|
||||
+ " exceeds maximum value " + PDU_BODY_PAGE_LENGTH);
|
||||
}
|
||||
|
||||
Pair<String, String> p = unpackBody(pdu, encoding, offset, length,
|
||||
hasLanguageIndicator, language);
|
||||
language = p.first;
|
||||
sb.append(p.second);
|
||||
}
|
||||
return new Pair<String, String>(language, sb.toString());
|
||||
} else {
|
||||
// Payload is one single page
|
||||
int offset = SmsCbHeader.PDU_HEADER_LENGTH;
|
||||
int length = pdu.length - offset;
|
||||
|
||||
return unpackBody(pdu, encoding, offset, length, hasLanguageIndicator, language);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack body text from the pdu using the given encoding, position and
|
||||
* length within the pdu
|
||||
*
|
||||
* @param pdu The pdu
|
||||
* @param encoding The encoding, as derived from the DCS
|
||||
* @param offset Position of the first byte to unpack
|
||||
* @param length Number of bytes to unpack
|
||||
* @param hasLanguageIndicator true if the body text is preceded by a
|
||||
* language indicator. If so, this method will as a side-effect
|
||||
* assign the extracted language code into mLanguage
|
||||
* @param language the language to return if hasLanguageIndicator is false
|
||||
* @return a Pair of Strings containing the language and body of the message
|
||||
*/
|
||||
private static Pair<String, String> unpackBody(byte[] pdu, int encoding, int offset, int length,
|
||||
boolean hasLanguageIndicator, String language) {
|
||||
String body = null;
|
||||
|
||||
switch (encoding) {
|
||||
case android.telephony.SmsMessage.ENCODING_7BIT:
|
||||
body = GsmAlphabet.gsm7BitPackedToString(pdu, offset, length * 8 / 7);
|
||||
|
||||
if (hasLanguageIndicator && body != null && body.length() > 2) {
|
||||
// Language is two GSM characters followed by a CR.
|
||||
// The actual body text is offset by 3 characters.
|
||||
language = body.substring(0, 2);
|
||||
body = body.substring(3);
|
||||
}
|
||||
break;
|
||||
|
||||
case android.telephony.SmsMessage.ENCODING_16BIT:
|
||||
if (hasLanguageIndicator && pdu.length >= offset + 2) {
|
||||
// Language is two GSM characters.
|
||||
// The actual body text is offset by 2 bytes.
|
||||
language = GsmAlphabet.gsm7BitPackedToString(pdu, offset, 2);
|
||||
offset += 2;
|
||||
length -= 2;
|
||||
}
|
||||
|
||||
try {
|
||||
body = new String(pdu, offset, (length & 0xfffe), "utf-16");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// Apparently it wasn't valid UTF-16.
|
||||
throw new IllegalArgumentException("Error decoding UTF-16 message", e);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (body != null) {
|
||||
// Remove trailing carriage return
|
||||
for (int i = body.length() - 1; i >= 0; i--) {
|
||||
if (body.charAt(i) != CARRIAGE_RETURN) {
|
||||
body = body.substring(0, i + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
body = "";
|
||||
}
|
||||
|
||||
return new Pair<String, String>(language, body);
|
||||
}
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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 com.android.internal.telephony.gsm;
|
||||
|
||||
/**
|
||||
* Constants used in SMS Cell Broadcast messages (see 3GPP TS 23.041). This class is used by the
|
||||
* boot-time broadcast channel enable and database upgrade code in CellBroadcastReceiver, so it
|
||||
* is public, but should be avoided in favor of the radio technology independent constants in
|
||||
* {@link android.telephony.SmsCbMessage}, {@link android.telephony.SmsCbEtwsInfo}, and
|
||||
* {@link android.telephony.SmsCbCmasInfo} classes.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public class SmsCbConstants {
|
||||
|
||||
/** Private constructor for utility class. */
|
||||
private SmsCbConstants() { }
|
||||
|
||||
/** Start of PWS Message Identifier range (includes ETWS and CMAS). */
|
||||
public static final int MESSAGE_ID_PWS_FIRST_IDENTIFIER = 0x1100;
|
||||
|
||||
/** Bitmask for messages of ETWS type (including future extensions). */
|
||||
public static final int MESSAGE_ID_ETWS_TYPE_MASK = 0xFFF8;
|
||||
|
||||
/** Value for messages of ETWS type after applying {@link #MESSAGE_ID_ETWS_TYPE_MASK}. */
|
||||
public static final int MESSAGE_ID_ETWS_TYPE = 0x1100;
|
||||
|
||||
/** ETWS Message Identifier for earthquake warning message. */
|
||||
public static final int MESSAGE_ID_ETWS_EARTHQUAKE_WARNING = 0x1100;
|
||||
|
||||
/** ETWS Message Identifier for tsunami warning message. */
|
||||
public static final int MESSAGE_ID_ETWS_TSUNAMI_WARNING = 0x1101;
|
||||
|
||||
/** ETWS Message Identifier for earthquake and tsunami combined warning message. */
|
||||
public static final int MESSAGE_ID_ETWS_EARTHQUAKE_AND_TSUNAMI_WARNING = 0x1102;
|
||||
|
||||
/** ETWS Message Identifier for test message. */
|
||||
public static final int MESSAGE_ID_ETWS_TEST_MESSAGE = 0x1103;
|
||||
|
||||
/** ETWS Message Identifier for messages related to other emergency types. */
|
||||
public static final int MESSAGE_ID_ETWS_OTHER_EMERGENCY_TYPE = 0x1104;
|
||||
|
||||
/** Start of CMAS Message Identifier range. */
|
||||
public static final int MESSAGE_ID_CMAS_FIRST_IDENTIFIER = 0x1112;
|
||||
|
||||
/** CMAS Message Identifier for Presidential Level alerts. */
|
||||
public static final int MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL = 0x1112;
|
||||
|
||||
/** CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Observed. */
|
||||
public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED = 0x1113;
|
||||
|
||||
/** CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Likely. */
|
||||
public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY = 0x1114;
|
||||
|
||||
/** CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Observed. */
|
||||
public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED = 0x1115;
|
||||
|
||||
/** CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Likely. */
|
||||
public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY = 0x1116;
|
||||
|
||||
/** CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Observed. */
|
||||
public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED = 0x1117;
|
||||
|
||||
/** CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Likely. */
|
||||
public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY = 0x1118;
|
||||
|
||||
/** CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Observed. */
|
||||
public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED = 0x1119;
|
||||
|
||||
/** CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Likely. */
|
||||
public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY = 0x111A;
|
||||
|
||||
/** CMAS Message Identifier for Child Abduction Emergency (Amber Alert). */
|
||||
public static final int MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY = 0x111B;
|
||||
|
||||
/** CMAS Message Identifier for the Required Monthly Test. */
|
||||
public static final int MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST = 0x111C;
|
||||
|
||||
/** CMAS Message Identifier for CMAS Exercise. */
|
||||
public static final int MESSAGE_ID_CMAS_ALERT_EXERCISE = 0x111D;
|
||||
|
||||
/** CMAS Message Identifier for operator defined use. */
|
||||
public static final int MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE = 0x111E;
|
||||
|
||||
/** End of CMAS Message Identifier range (including future extensions). */
|
||||
public static final int MESSAGE_ID_CMAS_LAST_IDENTIFIER = 0x112F;
|
||||
|
||||
/** End of PWS Message Identifier range (includes ETWS, CMAS, and future extensions). */
|
||||
public static final int MESSAGE_ID_PWS_LAST_IDENTIFIER = 0x18FF;
|
||||
|
||||
/** ETWS serial number flag to activate the popup display. */
|
||||
public static final int SERIAL_NUMBER_ETWS_ACTIVATE_POPUP = 0x1000;
|
||||
|
||||
/** ETWS serial number flag to activate the emergency user alert. */
|
||||
public static final int SERIAL_NUMBER_ETWS_EMERGENCY_USER_ALERT = 0x2000;
|
||||
|
||||
/** ETWS warning type value for earthquake. */
|
||||
public static final int ETWS_WARNING_TYPE_EARTHQUAKE = 0x00;
|
||||
|
||||
/** ETWS warning type value for tsunami. */
|
||||
public static final int ETWS_WARNING_TYPE_TSUNAMI = 0x01;
|
||||
|
||||
/** ETWS warning type value for earthquake and tsunami. */
|
||||
public static final int ETWS_WARNING_TYPE_EARTHQUAKE_AND_TSUNAMI = 0x02;
|
||||
|
||||
/** ETWS warning type value for test broadcast. */
|
||||
public static final int ETWS_WARNING_TYPE_TEST = 0x03;
|
||||
|
||||
/** ETWS warning type value for other notifications. */
|
||||
public static final int ETWS_WARNING_TYPE_OTHER = 0x04;
|
||||
}
|
@ -16,42 +16,28 @@
|
||||
|
||||
package com.android.internal.telephony.gsm;
|
||||
|
||||
import android.telephony.SmsCbCmasInfo;
|
||||
import android.telephony.SmsCbEtwsInfo;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Parses a 3GPP TS 23.041 cell broadcast message header. This class is public for use by
|
||||
* CellBroadcastReceiver test cases, but should not be used by applications.
|
||||
*
|
||||
* All relevant header information is now sent as a Parcelable
|
||||
* {@link android.telephony.SmsCbMessage} object in the "message" extra of the
|
||||
* {@link android.provider.Telephony.Sms.Intents#SMS_CB_RECEIVED_ACTION} or
|
||||
* {@link android.provider.Telephony.Sms.Intents#SMS_EMERGENCY_CB_RECEIVED_ACTION} intent.
|
||||
* The raw PDU is no longer sent to SMS CB applications.
|
||||
*/
|
||||
class SmsCbHeader {
|
||||
import android.telephony.SmsCbConstants;
|
||||
|
||||
public class SmsCbHeader implements SmsCbConstants {
|
||||
/**
|
||||
* Length of SMS-CB header
|
||||
*/
|
||||
static final int PDU_HEADER_LENGTH = 6;
|
||||
public static final int PDU_HEADER_LENGTH = 6;
|
||||
|
||||
/**
|
||||
* GSM pdu format, as defined in 3gpp TS 23.041, section 9.4.1
|
||||
*/
|
||||
static final int FORMAT_GSM = 1;
|
||||
public static final int FORMAT_GSM = 1;
|
||||
|
||||
/**
|
||||
* UMTS pdu format, as defined in 3gpp TS 23.041, section 9.4.2
|
||||
*/
|
||||
static final int FORMAT_UMTS = 2;
|
||||
public static final int FORMAT_UMTS = 2;
|
||||
|
||||
/**
|
||||
* GSM pdu format, as defined in 3gpp TS 23.041, section 9.4.1.3
|
||||
*/
|
||||
static final int FORMAT_ETWS_PRIMARY = 3;
|
||||
public static final int FORMAT_ETWS_PRIMARY = 3;
|
||||
|
||||
/**
|
||||
* Message type value as defined in 3gpp TS 25.324, section 11.1.
|
||||
@ -61,34 +47,34 @@ class SmsCbHeader {
|
||||
/**
|
||||
* Length of GSM pdus
|
||||
*/
|
||||
private static final int PDU_LENGTH_GSM = 88;
|
||||
public static final int PDU_LENGTH_GSM = 88;
|
||||
|
||||
/**
|
||||
* Maximum length of ETWS primary message GSM pdus
|
||||
*/
|
||||
private static final int PDU_LENGTH_ETWS = 56;
|
||||
public static final int PDU_LENGTH_ETWS = 56;
|
||||
|
||||
private final int geographicalScope;
|
||||
public final int geographicalScope;
|
||||
|
||||
/** The serial number combines geographical scope, message code, and update number. */
|
||||
private final int serialNumber;
|
||||
public final int messageCode;
|
||||
|
||||
/** The Message Identifier in 3GPP is the same as the Service Category in CDMA. */
|
||||
private final int messageIdentifier;
|
||||
public final int updateNumber;
|
||||
|
||||
private final int dataCodingScheme;
|
||||
public final int messageIdentifier;
|
||||
|
||||
private final int pageIndex;
|
||||
public final int dataCodingScheme;
|
||||
|
||||
private final int nrOfPages;
|
||||
public final int pageIndex;
|
||||
|
||||
private final int format;
|
||||
public final int nrOfPages;
|
||||
|
||||
/** ETWS warning notification info. */
|
||||
private final SmsCbEtwsInfo mEtwsInfo;
|
||||
public final int format;
|
||||
|
||||
/** CMAS warning notification info. */
|
||||
private final SmsCbCmasInfo mCmasInfo;
|
||||
public final boolean etwsEmergencyUserAlert;
|
||||
|
||||
public final boolean etwsPopup;
|
||||
|
||||
public final int etwsWarningType;
|
||||
|
||||
public SmsCbHeader(byte[] pdu) throws IllegalArgumentException {
|
||||
if (pdu == null || pdu.length < PDU_HEADER_LENGTH) {
|
||||
@ -97,31 +83,22 @@ class SmsCbHeader {
|
||||
|
||||
if (pdu.length <= PDU_LENGTH_ETWS) {
|
||||
format = FORMAT_ETWS_PRIMARY;
|
||||
geographicalScope = (pdu[0] & 0xc0) >> 6;
|
||||
serialNumber = ((pdu[0] & 0xff) << 8) | (pdu[1] & 0xff);
|
||||
geographicalScope = -1; //not applicable
|
||||
messageCode = -1;
|
||||
updateNumber = -1;
|
||||
messageIdentifier = ((pdu[2] & 0xff) << 8) | (pdu[3] & 0xff);
|
||||
dataCodingScheme = -1;
|
||||
pageIndex = -1;
|
||||
nrOfPages = -1;
|
||||
boolean emergencyUserAlert = (pdu[4] & 0x1) != 0;
|
||||
boolean activatePopup = (pdu[5] & 0x80) != 0;
|
||||
int warningType = (pdu[4] & 0xfe) >> 1;
|
||||
byte[] warningSecurityInfo;
|
||||
// copy the Warning-Security-Information, if present
|
||||
if (pdu.length > PDU_HEADER_LENGTH) {
|
||||
warningSecurityInfo = Arrays.copyOfRange(pdu, 6, pdu.length);
|
||||
} else {
|
||||
warningSecurityInfo = null;
|
||||
}
|
||||
mEtwsInfo = new SmsCbEtwsInfo(warningType, emergencyUserAlert, activatePopup,
|
||||
warningSecurityInfo);
|
||||
mCmasInfo = null;
|
||||
return; // skip the ETWS/CMAS initialization code for regular notifications
|
||||
etwsEmergencyUserAlert = (pdu[4] & 0x1) != 0;
|
||||
etwsPopup = (pdu[5] & 0x80) != 0;
|
||||
etwsWarningType = (pdu[4] & 0xfe) >> 1;
|
||||
} else if (pdu.length <= PDU_LENGTH_GSM) {
|
||||
// GSM pdus are no more than 88 bytes
|
||||
format = FORMAT_GSM;
|
||||
geographicalScope = (pdu[0] & 0xc0) >> 6;
|
||||
serialNumber = ((pdu[0] & 0xff) << 8) | (pdu[1] & 0xff);
|
||||
messageCode = ((pdu[0] & 0x3f) << 4) | ((pdu[1] & 0xf0) >> 4);
|
||||
updateNumber = pdu[1] & 0x0f;
|
||||
messageIdentifier = ((pdu[2] & 0xff) << 8) | (pdu[3] & 0xff);
|
||||
dataCodingScheme = pdu[4] & 0xff;
|
||||
|
||||
@ -136,6 +113,9 @@ class SmsCbHeader {
|
||||
|
||||
this.pageIndex = pageIndex;
|
||||
this.nrOfPages = nrOfPages;
|
||||
etwsEmergencyUserAlert = false;
|
||||
etwsPopup = false;
|
||||
etwsWarningType = -1;
|
||||
} else {
|
||||
// UMTS pdus are always at least 90 bytes since the payload includes
|
||||
// a number-of-pages octet and also one length octet per page
|
||||
@ -149,7 +129,8 @@ class SmsCbHeader {
|
||||
|
||||
messageIdentifier = ((pdu[1] & 0xff) << 8) | pdu[2] & 0xff;
|
||||
geographicalScope = (pdu[3] & 0xc0) >> 6;
|
||||
serialNumber = ((pdu[3] & 0xff) << 8) | (pdu[4] & 0xff);
|
||||
messageCode = ((pdu[3] & 0x3f) << 4) | ((pdu[4] & 0xf0) >> 4);
|
||||
updateNumber = pdu[4] & 0x0f;
|
||||
dataCodingScheme = pdu[5] & 0xff;
|
||||
|
||||
// We will always consider a UMTS message as having one single page
|
||||
@ -157,251 +138,75 @@ class SmsCbHeader {
|
||||
// actual payload may contain several pages.
|
||||
pageIndex = 1;
|
||||
nrOfPages = 1;
|
||||
}
|
||||
|
||||
if (isEtwsMessage()) {
|
||||
boolean emergencyUserAlert = isEtwsEmergencyUserAlert();
|
||||
boolean activatePopup = isEtwsPopupAlert();
|
||||
int warningType = getEtwsWarningType();
|
||||
mEtwsInfo = new SmsCbEtwsInfo(warningType, emergencyUserAlert, activatePopup, null);
|
||||
mCmasInfo = null;
|
||||
} else if (isCmasMessage()) {
|
||||
int messageClass = getCmasMessageClass();
|
||||
int severity = getCmasSeverity();
|
||||
int urgency = getCmasUrgency();
|
||||
int certainty = getCmasCertainty();
|
||||
mEtwsInfo = null;
|
||||
mCmasInfo = new SmsCbCmasInfo(messageClass, SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN,
|
||||
SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN, severity, urgency, certainty);
|
||||
} else {
|
||||
mEtwsInfo = null;
|
||||
mCmasInfo = null;
|
||||
etwsEmergencyUserAlert = false;
|
||||
etwsPopup = false;
|
||||
etwsWarningType = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int getGeographicalScope() {
|
||||
return geographicalScope;
|
||||
}
|
||||
|
||||
int getSerialNumber() {
|
||||
return serialNumber;
|
||||
}
|
||||
|
||||
int getServiceCategory() {
|
||||
return messageIdentifier;
|
||||
}
|
||||
|
||||
int getDataCodingScheme() {
|
||||
return dataCodingScheme;
|
||||
}
|
||||
|
||||
int getPageIndex() {
|
||||
return pageIndex;
|
||||
}
|
||||
|
||||
int getNumberOfPages() {
|
||||
return nrOfPages;
|
||||
}
|
||||
|
||||
SmsCbEtwsInfo getEtwsInfo() {
|
||||
return mEtwsInfo;
|
||||
}
|
||||
|
||||
SmsCbCmasInfo getCmasInfo() {
|
||||
return mCmasInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this broadcast is an emergency (PWS) message type.
|
||||
* @return true if this message is emergency type; false otherwise
|
||||
* Return whether the specified message ID is an emergency (PWS) message type.
|
||||
* This method is static and takes an argument so that it can be used by
|
||||
* CellBroadcastReceiver, which stores message ID's in SQLite rather than PDU.
|
||||
* @param id the message identifier to check
|
||||
* @return true if the message is emergency type; false otherwise
|
||||
*/
|
||||
boolean isEmergencyMessage() {
|
||||
return messageIdentifier >= SmsCbConstants.MESSAGE_ID_PWS_FIRST_IDENTIFIER
|
||||
&& messageIdentifier <= SmsCbConstants.MESSAGE_ID_PWS_LAST_IDENTIFIER;
|
||||
public static boolean isEmergencyMessage(int id) {
|
||||
return id >= MESSAGE_ID_PWS_FIRST_IDENTIFIER && id <= MESSAGE_ID_PWS_LAST_IDENTIFIER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this broadcast is an ETWS emergency message type.
|
||||
* @return true if this message is ETWS emergency type; false otherwise
|
||||
* Return whether the specified message ID is an ETWS emergency message type.
|
||||
* This method is static and takes an argument so that it can be used by
|
||||
* CellBroadcastReceiver, which stores message ID's in SQLite rather than PDU.
|
||||
* @param id the message identifier to check
|
||||
* @return true if the message is ETWS emergency type; false otherwise
|
||||
*/
|
||||
private boolean isEtwsMessage() {
|
||||
return (messageIdentifier & SmsCbConstants.MESSAGE_ID_ETWS_TYPE_MASK)
|
||||
== SmsCbConstants.MESSAGE_ID_ETWS_TYPE;
|
||||
public static boolean isEtwsMessage(int id) {
|
||||
return (id & MESSAGE_ID_ETWS_TYPE_MASK) == MESSAGE_ID_ETWS_TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this broadcast is an ETWS primary notification.
|
||||
* @return true if this message is an ETWS primary notification; false otherwise
|
||||
* Return whether the specified message ID is a CMAS emergency message type.
|
||||
* This method is static and takes an argument so that it can be used by
|
||||
* CellBroadcastReceiver, which stores message ID's in SQLite rather than PDU.
|
||||
* @param id the message identifier to check
|
||||
* @return true if the message is CMAS emergency type; false otherwise
|
||||
*/
|
||||
boolean isEtwsPrimaryNotification() {
|
||||
return format == FORMAT_ETWS_PRIMARY;
|
||||
public static boolean isCmasMessage(int id) {
|
||||
return id >= MESSAGE_ID_CMAS_FIRST_IDENTIFIER && id <= MESSAGE_ID_CMAS_LAST_IDENTIFIER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this broadcast is in UMTS format.
|
||||
* @return true if this message is in UMTS format; false otherwise
|
||||
*/
|
||||
boolean isUmtsFormat() {
|
||||
return format == FORMAT_UMTS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this message is a CMAS emergency message type.
|
||||
* @return true if this message is CMAS emergency type; false otherwise
|
||||
*/
|
||||
private boolean isCmasMessage() {
|
||||
return messageIdentifier >= SmsCbConstants.MESSAGE_ID_CMAS_FIRST_IDENTIFIER
|
||||
&& messageIdentifier <= SmsCbConstants.MESSAGE_ID_CMAS_LAST_IDENTIFIER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the popup alert flag is set for an ETWS warning notification.
|
||||
* Return whether the specified message code indicates an ETWS popup alert.
|
||||
* This method is static and takes an argument so that it can be used by
|
||||
* CellBroadcastReceiver, which stores message codes in SQLite rather than PDU.
|
||||
* This method assumes that the message ID has already been checked for ETWS type.
|
||||
*
|
||||
* @param messageCode the message code to check
|
||||
* @return true if the message code indicates a popup alert should be displayed
|
||||
*/
|
||||
private boolean isEtwsPopupAlert() {
|
||||
return (serialNumber & SmsCbConstants.SERIAL_NUMBER_ETWS_ACTIVATE_POPUP) != 0;
|
||||
public static boolean isEtwsPopupAlert(int messageCode) {
|
||||
return (messageCode & MESSAGE_CODE_ETWS_ACTIVATE_POPUP) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the emergency user alert flag is set for an ETWS warning notification.
|
||||
* Return whether the specified message code indicates an ETWS emergency user alert.
|
||||
* This method is static and takes an argument so that it can be used by
|
||||
* CellBroadcastReceiver, which stores message codes in SQLite rather than PDU.
|
||||
* This method assumes that the message ID has already been checked for ETWS type.
|
||||
*
|
||||
* @param messageCode the message code to check
|
||||
* @return true if the message code indicates an emergency user alert
|
||||
*/
|
||||
private boolean isEtwsEmergencyUserAlert() {
|
||||
return (serialNumber & SmsCbConstants.SERIAL_NUMBER_ETWS_EMERGENCY_USER_ALERT) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the warning type for an ETWS warning notification.
|
||||
* This method assumes that the message ID has already been checked for ETWS type.
|
||||
*
|
||||
* @return the ETWS warning type defined in 3GPP TS 23.041 section 9.3.24
|
||||
*/
|
||||
private int getEtwsWarningType() {
|
||||
return messageIdentifier - SmsCbConstants.MESSAGE_ID_ETWS_EARTHQUAKE_WARNING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message class for a CMAS warning notification.
|
||||
* This method assumes that the message ID has already been checked for CMAS type.
|
||||
* @return the CMAS message class as defined in {@link SmsCbCmasInfo}
|
||||
*/
|
||||
private int getCmasMessageClass() {
|
||||
switch (messageIdentifier) {
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL:
|
||||
return SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT;
|
||||
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED:
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY:
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED:
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY:
|
||||
return SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT;
|
||||
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED:
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY:
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED:
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY:
|
||||
return SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT;
|
||||
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY:
|
||||
return SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY;
|
||||
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST:
|
||||
return SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST;
|
||||
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXERCISE:
|
||||
return SmsCbCmasInfo.CMAS_CLASS_CMAS_EXERCISE;
|
||||
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE:
|
||||
return SmsCbCmasInfo.CMAS_CLASS_OPERATOR_DEFINED_USE;
|
||||
|
||||
default:
|
||||
return SmsCbCmasInfo.CMAS_CLASS_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the severity for a CMAS warning notification. This is only available for extreme
|
||||
* and severe alerts, not for other types such as Presidential Level and AMBER alerts.
|
||||
* This method assumes that the message ID has already been checked for CMAS type.
|
||||
* @return the CMAS severity as defined in {@link SmsCbCmasInfo}
|
||||
*/
|
||||
private int getCmasSeverity() {
|
||||
switch (messageIdentifier) {
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED:
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY:
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED:
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY:
|
||||
return SmsCbCmasInfo.CMAS_SEVERITY_EXTREME;
|
||||
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED:
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY:
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED:
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY:
|
||||
return SmsCbCmasInfo.CMAS_SEVERITY_SEVERE;
|
||||
|
||||
default:
|
||||
return SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the urgency for a CMAS warning notification. This is only available for extreme
|
||||
* and severe alerts, not for other types such as Presidential Level and AMBER alerts.
|
||||
* This method assumes that the message ID has already been checked for CMAS type.
|
||||
* @return the CMAS urgency as defined in {@link SmsCbCmasInfo}
|
||||
*/
|
||||
private int getCmasUrgency() {
|
||||
switch (messageIdentifier) {
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED:
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY:
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED:
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY:
|
||||
return SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE;
|
||||
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED:
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY:
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED:
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY:
|
||||
return SmsCbCmasInfo.CMAS_URGENCY_EXPECTED;
|
||||
|
||||
default:
|
||||
return SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the certainty for a CMAS warning notification. This is only available for extreme
|
||||
* and severe alerts, not for other types such as Presidential Level and AMBER alerts.
|
||||
* This method assumes that the message ID has already been checked for CMAS type.
|
||||
* @return the CMAS certainty as defined in {@link SmsCbCmasInfo}
|
||||
*/
|
||||
private int getCmasCertainty() {
|
||||
switch (messageIdentifier) {
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED:
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED:
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED:
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED:
|
||||
return SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED;
|
||||
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY:
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY:
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY:
|
||||
case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY:
|
||||
return SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY;
|
||||
|
||||
default:
|
||||
return SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN;
|
||||
}
|
||||
public static boolean isEtwsEmergencyUserAlert(int messageCode) {
|
||||
return (messageCode & MESSAGE_CODE_ETWS_EMERGENCY_USER_ALERT) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SmsCbHeader{GS=" + geographicalScope + ", serialNumber=0x" +
|
||||
Integer.toHexString(serialNumber) +
|
||||
return "SmsCbHeader{GS=" + geographicalScope + ", messageCode=0x" +
|
||||
Integer.toHexString(messageCode) + ", updateNumber=" + updateNumber +
|
||||
", messageIdentifier=0x" + Integer.toHexString(messageIdentifier) +
|
||||
", DCS=0x" + Integer.toHexString(dataCodingScheme) +
|
||||
", page " + pageIndex + " of " + nrOfPages + '}';
|
||||
|
Reference in New Issue
Block a user