| /* |
| * Copyright (C) 2010 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.cellbroadcastservice; |
| |
| import android.telephony.SmsCbCmasInfo; |
| import android.telephony.SmsCbEtwsInfo; |
| import android.telephony.SmsMessage; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| |
| import java.util.Arrays; |
| import java.util.Locale; |
| |
| /** |
| * 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#ACTION_SMS_EMERGENCY_CB_RECEIVED} intent. |
| * The raw PDU is no longer sent to SMS CB applications. |
| */ |
| public class SmsCbHeader { |
| /** |
| * Languages in the 0000xxxx DCS group as defined in 3GPP TS 23.038, section 5. |
| */ |
| private static final String[] LANGUAGE_CODES_GROUP_0 = { |
| Locale.GERMAN.getLanguage(), // German |
| Locale.ENGLISH.getLanguage(), // English |
| Locale.ITALIAN.getLanguage(), // Italian |
| Locale.FRENCH.getLanguage(), // French |
| new Locale("es").getLanguage(), // Spanish |
| new Locale("nl").getLanguage(), // Dutch |
| new Locale("sv").getLanguage(), // Swedish |
| new Locale("da").getLanguage(), // Danish |
| new Locale("pt").getLanguage(), // Portuguese |
| new Locale("fi").getLanguage(), // Finnish |
| new Locale("nb").getLanguage(), // Norwegian |
| new Locale("el").getLanguage(), // Greek |
| new Locale("tr").getLanguage(), // Turkish |
| new Locale("hu").getLanguage(), // Hungarian |
| new Locale("pl").getLanguage(), // Polish |
| null |
| }; |
| |
| /** |
| * Languages in the 0010xxxx DCS group as defined in 3GPP TS 23.038, section 5. |
| */ |
| private static final String[] LANGUAGE_CODES_GROUP_2 = { |
| new Locale("cs").getLanguage(), // Czech |
| new Locale("he").getLanguage(), // Hebrew |
| new Locale("ar").getLanguage(), // Arabic |
| new Locale("ru").getLanguage(), // Russian |
| new Locale("is").getLanguage(), // Icelandic |
| null, null, null, null, null, null, null, null, null, null, null |
| }; |
| |
| /** |
| * Length of SMS-CB header |
| */ |
| 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; |
| |
| /** |
| * UMTS pdu format, as defined in 3gpp TS 23.041, section 9.4.2 |
| */ |
| static final int FORMAT_UMTS = 2; |
| |
| /** |
| * ETWS pdu format, as defined in 3gpp TS 23.041, section 9.4.1.3 |
| */ |
| static final int FORMAT_ETWS_PRIMARY = 3; |
| |
| /** |
| * Message type value as defined in 3gpp TS 25.324, section 11.1. |
| */ |
| private static final int MESSAGE_TYPE_CBS_MESSAGE = 1; |
| |
| /** |
| * Length of GSM pdus |
| */ |
| private static final int PDU_LENGTH_GSM = 88; |
| |
| /** |
| * Maximum length of ETWS primary message GSM pdus |
| */ |
| private static final int PDU_LENGTH_ETWS = 56; |
| |
| private final int mGeographicalScope; |
| |
| /** The serial number combines geographical scope, message code, and update number. */ |
| private final int mSerialNumber; |
| |
| /** The Message Identifier in 3GPP is the same as the Service Category in CDMA. */ |
| private final int mMessageIdentifier; |
| |
| private final int mDataCodingScheme; |
| |
| private final int mPageIndex; |
| |
| private final int mNrOfPages; |
| |
| private final int mFormat; |
| |
| private DataCodingScheme mDataCodingSchemeStructedData; |
| |
| /** ETWS warning notification info. */ |
| private final SmsCbEtwsInfo mEtwsInfo; |
| |
| /** CMAS warning notification info. */ |
| private final SmsCbCmasInfo mCmasInfo; |
| |
| public SmsCbHeader(byte[] pdu) throws IllegalArgumentException { |
| if (pdu == null || pdu.length < PDU_HEADER_LENGTH) { |
| final String errorMessage = "Illegal PDU"; |
| CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_ERROR, |
| CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_ERROR__TYPE__GSM_INVALID_HEADER_LENGTH, |
| errorMessage); |
| throw new IllegalArgumentException(errorMessage); |
| } |
| |
| if (pdu.length <= PDU_LENGTH_GSM) { |
| // can be ETWS or GSM format. |
| // Per TS23.041 9.4.1.2 and 9.4.1.3.2, GSM and ETWS format both |
| // contain serial number which contains GS, Message Code, and Update Number |
| // per 9.4.1.2.1, and message identifier in same octets |
| mGeographicalScope = (pdu[0] & 0xc0) >>> 6; |
| mSerialNumber = ((pdu[0] & 0xff) << 8) | (pdu[1] & 0xff); |
| mMessageIdentifier = ((pdu[2] & 0xff) << 8) | (pdu[3] & 0xff); |
| if (isEtwsMessage() && pdu.length <= PDU_LENGTH_ETWS) { |
| mFormat = FORMAT_ETWS_PRIMARY; |
| mDataCodingScheme = -1; |
| mPageIndex = -1; |
| mNrOfPages = -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, |
| true, warningSecurityInfo); |
| mCmasInfo = null; |
| return; // skip the ETWS/CMAS initialization code for regular notifications |
| } else { |
| // GSM pdus are no more than 88 bytes |
| mFormat = FORMAT_GSM; |
| mDataCodingScheme = pdu[4] & 0xff; |
| |
| // Check for invalid page parameter |
| int pageIndex = (pdu[5] & 0xf0) >>> 4; |
| int nrOfPages = pdu[5] & 0x0f; |
| |
| if (pageIndex == 0 || nrOfPages == 0 || pageIndex > nrOfPages) { |
| pageIndex = 1; |
| nrOfPages = 1; |
| } |
| |
| mPageIndex = pageIndex; |
| mNrOfPages = nrOfPages; |
| } |
| } 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 |
| mFormat = FORMAT_UMTS; |
| |
| int messageType = pdu[0]; |
| |
| if (messageType != MESSAGE_TYPE_CBS_MESSAGE) { |
| IllegalArgumentException ex = new IllegalArgumentException( |
| "Unsupported message type " + messageType); |
| CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_ERROR, |
| CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_ERROR__TYPE__GSM_UNSUPPORTED_HEADER_MESSAGE_TYPE, |
| ex.toString()); |
| throw ex; |
| } |
| |
| mMessageIdentifier = ((pdu[1] & 0xff) << 8) | pdu[2] & 0xff; |
| mGeographicalScope = (pdu[3] & 0xc0) >>> 6; |
| mSerialNumber = ((pdu[3] & 0xff) << 8) | (pdu[4] & 0xff); |
| mDataCodingScheme = pdu[5] & 0xff; |
| |
| // We will always consider a UMTS message as having one single page |
| // since there's only one instance of the header, even though the |
| // actual payload may contain several pages. |
| mPageIndex = 1; |
| mNrOfPages = 1; |
| } |
| |
| if (mDataCodingScheme != -1) { |
| mDataCodingSchemeStructedData = new DataCodingScheme(mDataCodingScheme); |
| } |
| |
| if (isEtwsMessage()) { |
| boolean emergencyUserAlert = isEtwsEmergencyUserAlert(); |
| boolean activatePopup = isEtwsPopupAlert(); |
| int warningType = getEtwsWarningType(); |
| mEtwsInfo = new SmsCbEtwsInfo(warningType, emergencyUserAlert, activatePopup, |
| false, 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; |
| } |
| } |
| |
| public int getGeographicalScope() { |
| return mGeographicalScope; |
| } |
| |
| public int getSerialNumber() { |
| return mSerialNumber; |
| } |
| |
| public int getServiceCategory() { |
| return mMessageIdentifier; |
| } |
| |
| public int getDataCodingScheme() { |
| return mDataCodingScheme; |
| } |
| |
| public DataCodingScheme getDataCodingSchemeStructedData() { |
| return mDataCodingSchemeStructedData; |
| } |
| |
| public int getPageIndex() { |
| return mPageIndex; |
| } |
| |
| public int getNumberOfPages() { |
| return mNrOfPages; |
| } |
| |
| public SmsCbEtwsInfo getEtwsInfo() { |
| return mEtwsInfo; |
| } |
| |
| public SmsCbCmasInfo getCmasInfo() { |
| return mCmasInfo; |
| } |
| |
| /** |
| * Return whether this broadcast is an emergency (PWS) message type. |
| * @return true if this message is emergency type; false otherwise |
| */ |
| public boolean isEmergencyMessage() { |
| return mMessageIdentifier >= SmsCbConstants.MESSAGE_ID_PWS_FIRST_IDENTIFIER |
| && mMessageIdentifier <= SmsCbConstants.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 |
| */ |
| @VisibleForTesting |
| public boolean isEtwsMessage() { |
| return (mMessageIdentifier & SmsCbConstants.MESSAGE_ID_ETWS_TYPE_MASK) |
| == SmsCbConstants.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 |
| */ |
| public boolean isEtwsPrimaryNotification() { |
| return mFormat == FORMAT_ETWS_PRIMARY; |
| } |
| |
| /** |
| * Return whether this broadcast is in UMTS format. |
| * @return true if this message is in UMTS format; false otherwise |
| */ |
| public boolean isUmtsFormat() { |
| return mFormat == 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 mMessageIdentifier >= SmsCbConstants.MESSAGE_ID_CMAS_FIRST_IDENTIFIER |
| && mMessageIdentifier <= SmsCbConstants.MESSAGE_ID_CMAS_LAST_IDENTIFIER; |
| } |
| |
| /** |
| * Return whether the popup alert flag is set for an ETWS warning notification. |
| * This method assumes that the message ID has already been checked for ETWS type. |
| * |
| * @return true if the message code indicates a popup alert should be displayed |
| */ |
| private boolean isEtwsPopupAlert() { |
| return (mSerialNumber & SmsCbConstants.SERIAL_NUMBER_ETWS_ACTIVATE_POPUP) != 0; |
| } |
| |
| /** |
| * Return whether the emergency user alert flag is set for an ETWS warning notification. |
| * This method assumes that the message ID has already been checked for ETWS type. |
| * |
| * @return true if the message code indicates an emergency user alert |
| */ |
| private boolean isEtwsEmergencyUserAlert() { |
| return (mSerialNumber & 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 mMessageIdentifier - 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 (mMessageIdentifier) { |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL_LANGUAGE: |
| return SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT; |
| |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED_LANGUAGE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY_LANGUAGE: |
| return SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT; |
| |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED_LANGUAGE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY_LANGUAGE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED_LANGUAGE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY_LANGUAGE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED_LANGUAGE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY_LANGUAGE: |
| return SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT; |
| |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY_LANGUAGE: |
| return SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY; |
| |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST_LANGUAGE: |
| return SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST; |
| |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXERCISE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXERCISE_LANGUAGE: |
| return SmsCbCmasInfo.CMAS_CLASS_CMAS_EXERCISE; |
| |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE_LANGUAGE: |
| 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 (mMessageIdentifier) { |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED_LANGUAGE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY_LANGUAGE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED_LANGUAGE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY_LANGUAGE: |
| return SmsCbCmasInfo.CMAS_SEVERITY_EXTREME; |
| |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED_LANGUAGE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY_LANGUAGE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED_LANGUAGE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY_LANGUAGE: |
| 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 (mMessageIdentifier) { |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED_LANGUAGE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY_LANGUAGE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED_LANGUAGE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY_LANGUAGE: |
| return SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE; |
| |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED_LANGUAGE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY_LANGUAGE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED_LANGUAGE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY_LANGUAGE: |
| 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 (mMessageIdentifier) { |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED_LANGUAGE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED_LANGUAGE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED_LANGUAGE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED_LANGUAGE: |
| return SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED; |
| |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY_LANGUAGE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY_LANGUAGE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY_LANGUAGE: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY: |
| case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY_LANGUAGE: |
| return SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY; |
| |
| default: |
| return SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN; |
| } |
| } |
| |
| @Override |
| public String toString() { |
| return "SmsCbHeader{GS=" + mGeographicalScope + ", serialNumber=0x" |
| + Integer.toHexString(mSerialNumber) |
| + ", messageIdentifier=0x" + Integer.toHexString(mMessageIdentifier) |
| + ", format=" + mFormat |
| + ", DCS=0x" + Integer.toHexString(mDataCodingScheme) |
| + ", page " + mPageIndex + " of " + mNrOfPages + '}'; |
| } |
| |
| /** |
| * CBS Data Coding Scheme. |
| * Reference: 3GPP TS 23.038 version 15.0.0 section #5, CBS Data Coding Scheme |
| */ |
| public static final class DataCodingScheme { |
| public final int encoding; |
| public final String language; |
| public final boolean hasLanguageIndicator; |
| |
| public DataCodingScheme(int dataCodingScheme) { |
| int encoding = 0; |
| String language = null; |
| boolean hasLanguageIndicator = false; |
| |
| // Extract encoding and language from DCS, as defined in 3gpp TS 23.038, |
| // section 5. |
| switch ((dataCodingScheme & 0xf0) >> 4) { |
| case 0x00: |
| encoding = SmsMessage.ENCODING_7BIT; |
| language = LANGUAGE_CODES_GROUP_0[dataCodingScheme & 0x0f]; |
| break; |
| |
| case 0x01: |
| hasLanguageIndicator = true; |
| if ((dataCodingScheme & 0x0f) == 0x01) { |
| encoding = SmsMessage.ENCODING_16BIT; |
| } else { |
| encoding = SmsMessage.ENCODING_7BIT; |
| } |
| break; |
| |
| case 0x02: |
| encoding = SmsMessage.ENCODING_7BIT; |
| language = LANGUAGE_CODES_GROUP_2[dataCodingScheme & 0x0f]; |
| break; |
| |
| case 0x03: |
| encoding = SmsMessage.ENCODING_7BIT; |
| break; |
| |
| case 0x04: |
| case 0x05: |
| switch ((dataCodingScheme & 0x0c) >> 2) { |
| case 0x01: |
| encoding = SmsMessage.ENCODING_8BIT; |
| break; |
| |
| case 0x02: |
| encoding = SmsMessage.ENCODING_16BIT; |
| break; |
| |
| case 0x00: |
| default: |
| encoding = 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 |
| final String errorMessage = |
| "Unsupported GSM dataCodingScheme " + dataCodingScheme; |
| CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_ERROR, |
| CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_ERROR__TYPE__GSM_UNSUPPORTED_HEADER_DATA_CODING_SCHEME, |
| errorMessage); |
| throw new IllegalArgumentException(errorMessage); |
| |
| case 0x0f: |
| if (((dataCodingScheme & 0x04) >> 2) == 0x01) { |
| encoding = SmsMessage.ENCODING_8BIT; |
| } else { |
| encoding = SmsMessage.ENCODING_7BIT; |
| } |
| break; |
| |
| default: |
| // Reserved values are to be treated as 7-bit |
| encoding = SmsMessage.ENCODING_7BIT; |
| break; |
| } |
| |
| |
| this.encoding = encoding; |
| this.language = language; |
| this.hasLanguageIndicator = hasLanguageIndicator; |
| } |
| } |
| } |