Home | History | Annotate | Download | only in gsm
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.internal.telephony.gsm;
     18 
     19 import android.telephony.SmsCbCmasInfo;
     20 import android.telephony.SmsCbEtwsInfo;
     21 
     22 import java.util.Arrays;
     23 
     24 /**
     25  * Parses a 3GPP TS 23.041 cell broadcast message header. This class is public for use by
     26  * CellBroadcastReceiver test cases, but should not be used by applications.
     27  *
     28  * All relevant header information is now sent as a Parcelable
     29  * {@link android.telephony.SmsCbMessage} object in the "message" extra of the
     30  * {@link android.provider.Telephony.Sms.Intents#SMS_CB_RECEIVED_ACTION} or
     31  * {@link android.provider.Telephony.Sms.Intents#SMS_EMERGENCY_CB_RECEIVED_ACTION} intent.
     32  * The raw PDU is no longer sent to SMS CB applications.
     33  */
     34 class SmsCbHeader {
     35 
     36     /**
     37      * Length of SMS-CB header
     38      */
     39     static final int PDU_HEADER_LENGTH = 6;
     40 
     41     /**
     42      * GSM pdu format, as defined in 3gpp TS 23.041, section 9.4.1
     43      */
     44     static final int FORMAT_GSM = 1;
     45 
     46     /**
     47      * UMTS pdu format, as defined in 3gpp TS 23.041, section 9.4.2
     48      */
     49     static final int FORMAT_UMTS = 2;
     50 
     51     /**
     52      * GSM pdu format, as defined in 3gpp TS 23.041, section 9.4.1.3
     53      */
     54     static final int FORMAT_ETWS_PRIMARY = 3;
     55 
     56     /**
     57      * Message type value as defined in 3gpp TS 25.324, section 11.1.
     58      */
     59     private static final int MESSAGE_TYPE_CBS_MESSAGE = 1;
     60 
     61     /**
     62      * Length of GSM pdus
     63      */
     64     private static final int PDU_LENGTH_GSM = 88;
     65 
     66     /**
     67      * Maximum length of ETWS primary message GSM pdus
     68      */
     69     private static final int PDU_LENGTH_ETWS = 56;
     70 
     71     private final int mGeographicalScope;
     72 
     73     /** The serial number combines geographical scope, message code, and update number. */
     74     private final int mSerialNumber;
     75 
     76     /** The Message Identifier in 3GPP is the same as the Service Category in CDMA. */
     77     private final int mMessageIdentifier;
     78 
     79     private final int mDataCodingScheme;
     80 
     81     private final int mPageIndex;
     82 
     83     private final int mNrOfPages;
     84 
     85     private final int mFormat;
     86 
     87     /** ETWS warning notification info. */
     88     private final SmsCbEtwsInfo mEtwsInfo;
     89 
     90     /** CMAS warning notification info. */
     91     private final SmsCbCmasInfo mCmasInfo;
     92 
     93     public SmsCbHeader(byte[] pdu) throws IllegalArgumentException {
     94         if (pdu == null || pdu.length < PDU_HEADER_LENGTH) {
     95             throw new IllegalArgumentException("Illegal PDU");
     96         }
     97 
     98         if (pdu.length <= PDU_LENGTH_GSM) {
     99             // can be ETWS or GSM format.
    100             // Per TS23.041 9.4.1.2 and 9.4.1.3.2, GSM and ETWS format both
    101             // contain serial number which contains GS, Message Code, and Update Number
    102             // per 9.4.1.2.1, and message identifier in same octets
    103             mGeographicalScope = (pdu[0] & 0xc0) >>> 6;
    104             mSerialNumber = ((pdu[0] & 0xff) << 8) | (pdu[1] & 0xff);
    105             mMessageIdentifier = ((pdu[2] & 0xff) << 8) | (pdu[3] & 0xff);
    106             if (isEtwsMessage() && pdu.length <= PDU_LENGTH_ETWS) {
    107                 mFormat = FORMAT_ETWS_PRIMARY;
    108                 mDataCodingScheme = -1;
    109                 mPageIndex = -1;
    110                 mNrOfPages = -1;
    111                 boolean emergencyUserAlert = (pdu[4] & 0x1) != 0;
    112                 boolean activatePopup = (pdu[5] & 0x80) != 0;
    113                 int warningType = (pdu[4] & 0xfe) >>> 1;
    114                 byte[] warningSecurityInfo;
    115                 // copy the Warning-Security-Information, if present
    116                 if (pdu.length > PDU_HEADER_LENGTH) {
    117                     warningSecurityInfo = Arrays.copyOfRange(pdu, 6, pdu.length);
    118                 } else {
    119                     warningSecurityInfo = null;
    120                 }
    121                 mEtwsInfo = new SmsCbEtwsInfo(warningType, emergencyUserAlert, activatePopup,
    122                         warningSecurityInfo);
    123                 mCmasInfo = null;
    124                 return;     // skip the ETWS/CMAS initialization code for regular notifications
    125             } else {
    126                 // GSM pdus are no more than 88 bytes
    127                 mFormat = FORMAT_GSM;
    128                 mDataCodingScheme = pdu[4] & 0xff;
    129 
    130                 // Check for invalid page parameter
    131                 int pageIndex = (pdu[5] & 0xf0) >>> 4;
    132                 int nrOfPages = pdu[5] & 0x0f;
    133 
    134                 if (pageIndex == 0 || nrOfPages == 0 || pageIndex > nrOfPages) {
    135                     pageIndex = 1;
    136                     nrOfPages = 1;
    137                 }
    138 
    139                 mPageIndex = pageIndex;
    140                 mNrOfPages = nrOfPages;
    141             }
    142         } else {
    143             // UMTS pdus are always at least 90 bytes since the payload includes
    144             // a number-of-pages octet and also one length octet per page
    145             mFormat = FORMAT_UMTS;
    146 
    147             int messageType = pdu[0];
    148 
    149             if (messageType != MESSAGE_TYPE_CBS_MESSAGE) {
    150                 throw new IllegalArgumentException("Unsupported message type " + messageType);
    151             }
    152 
    153             mMessageIdentifier = ((pdu[1] & 0xff) << 8) | pdu[2] & 0xff;
    154             mGeographicalScope = (pdu[3] & 0xc0) >>> 6;
    155             mSerialNumber = ((pdu[3] & 0xff) << 8) | (pdu[4] & 0xff);
    156             mDataCodingScheme = pdu[5] & 0xff;
    157 
    158             // We will always consider a UMTS message as having one single page
    159             // since there's only one instance of the header, even though the
    160             // actual payload may contain several pages.
    161             mPageIndex = 1;
    162             mNrOfPages = 1;
    163         }
    164 
    165         if (isEtwsMessage()) {
    166             boolean emergencyUserAlert = isEtwsEmergencyUserAlert();
    167             boolean activatePopup = isEtwsPopupAlert();
    168             int warningType = getEtwsWarningType();
    169             mEtwsInfo = new SmsCbEtwsInfo(warningType, emergencyUserAlert, activatePopup, null);
    170             mCmasInfo = null;
    171         } else if (isCmasMessage()) {
    172             int messageClass = getCmasMessageClass();
    173             int severity = getCmasSeverity();
    174             int urgency = getCmasUrgency();
    175             int certainty = getCmasCertainty();
    176             mEtwsInfo = null;
    177             mCmasInfo = new SmsCbCmasInfo(messageClass, SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN,
    178                     SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN, severity, urgency, certainty);
    179         } else {
    180             mEtwsInfo = null;
    181             mCmasInfo = null;
    182         }
    183     }
    184 
    185     int getGeographicalScope() {
    186         return mGeographicalScope;
    187     }
    188 
    189     int getSerialNumber() {
    190         return mSerialNumber;
    191     }
    192 
    193     int getServiceCategory() {
    194         return mMessageIdentifier;
    195     }
    196 
    197     int getDataCodingScheme() {
    198         return mDataCodingScheme;
    199     }
    200 
    201     int getPageIndex() {
    202         return mPageIndex;
    203     }
    204 
    205     int getNumberOfPages() {
    206         return mNrOfPages;
    207     }
    208 
    209     SmsCbEtwsInfo getEtwsInfo() {
    210         return mEtwsInfo;
    211     }
    212 
    213     SmsCbCmasInfo getCmasInfo() {
    214         return mCmasInfo;
    215     }
    216 
    217     /**
    218      * Return whether this broadcast is an emergency (PWS) message type.
    219      * @return true if this message is emergency type; false otherwise
    220      */
    221     boolean isEmergencyMessage() {
    222         return mMessageIdentifier >= SmsCbConstants.MESSAGE_ID_PWS_FIRST_IDENTIFIER
    223                 && mMessageIdentifier <= SmsCbConstants.MESSAGE_ID_PWS_LAST_IDENTIFIER;
    224     }
    225 
    226     /**
    227      * Return whether this broadcast is an ETWS emergency message type.
    228      * @return true if this message is ETWS emergency type; false otherwise
    229      */
    230     private boolean isEtwsMessage() {
    231         return (mMessageIdentifier & SmsCbConstants.MESSAGE_ID_ETWS_TYPE_MASK)
    232                 == SmsCbConstants.MESSAGE_ID_ETWS_TYPE;
    233     }
    234 
    235     /**
    236      * Return whether this broadcast is an ETWS primary notification.
    237      * @return true if this message is an ETWS primary notification; false otherwise
    238      */
    239     boolean isEtwsPrimaryNotification() {
    240         return mFormat == FORMAT_ETWS_PRIMARY;
    241     }
    242 
    243     /**
    244      * Return whether this broadcast is in UMTS format.
    245      * @return true if this message is in UMTS format; false otherwise
    246      */
    247     boolean isUmtsFormat() {
    248         return mFormat == FORMAT_UMTS;
    249     }
    250 
    251     /**
    252      * Return whether this message is a CMAS emergency message type.
    253      * @return true if this message is CMAS emergency type; false otherwise
    254      */
    255     private boolean isCmasMessage() {
    256         return mMessageIdentifier >= SmsCbConstants.MESSAGE_ID_CMAS_FIRST_IDENTIFIER
    257                 && mMessageIdentifier <= SmsCbConstants.MESSAGE_ID_CMAS_LAST_IDENTIFIER;
    258     }
    259 
    260     /**
    261      * Return whether the popup alert flag is set for an ETWS warning notification.
    262      * This method assumes that the message ID has already been checked for ETWS type.
    263      *
    264      * @return true if the message code indicates a popup alert should be displayed
    265      */
    266     private boolean isEtwsPopupAlert() {
    267         return (mSerialNumber & SmsCbConstants.SERIAL_NUMBER_ETWS_ACTIVATE_POPUP) != 0;
    268     }
    269 
    270     /**
    271      * Return whether the emergency user alert flag is set for an ETWS warning notification.
    272      * This method assumes that the message ID has already been checked for ETWS type.
    273      *
    274      * @return true if the message code indicates an emergency user alert
    275      */
    276     private boolean isEtwsEmergencyUserAlert() {
    277         return (mSerialNumber & SmsCbConstants.SERIAL_NUMBER_ETWS_EMERGENCY_USER_ALERT) != 0;
    278     }
    279 
    280     /**
    281      * Returns the warning type for an ETWS warning notification.
    282      * This method assumes that the message ID has already been checked for ETWS type.
    283      *
    284      * @return the ETWS warning type defined in 3GPP TS 23.041 section 9.3.24
    285      */
    286     private int getEtwsWarningType() {
    287         return mMessageIdentifier - SmsCbConstants.MESSAGE_ID_ETWS_EARTHQUAKE_WARNING;
    288     }
    289 
    290     /**
    291      * Returns the message class for a CMAS warning notification.
    292      * This method assumes that the message ID has already been checked for CMAS type.
    293      * @return the CMAS message class as defined in {@link SmsCbCmasInfo}
    294      */
    295     private int getCmasMessageClass() {
    296         switch (mMessageIdentifier) {
    297             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL:
    298                 return SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT;
    299 
    300             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED:
    301             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY:
    302             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED:
    303             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY:
    304                 return SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT;
    305 
    306             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED:
    307             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY:
    308             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED:
    309             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY:
    310                 return SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT;
    311 
    312             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY:
    313                 return SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY;
    314 
    315             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST:
    316                 return SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST;
    317 
    318             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXERCISE:
    319                 return SmsCbCmasInfo.CMAS_CLASS_CMAS_EXERCISE;
    320 
    321             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE:
    322                 return SmsCbCmasInfo.CMAS_CLASS_OPERATOR_DEFINED_USE;
    323 
    324             default:
    325                 return SmsCbCmasInfo.CMAS_CLASS_UNKNOWN;
    326         }
    327     }
    328 
    329     /**
    330      * Returns the severity for a CMAS warning notification. This is only available for extreme
    331      * and severe alerts, not for other types such as Presidential Level and AMBER alerts.
    332      * This method assumes that the message ID has already been checked for CMAS type.
    333      * @return the CMAS severity as defined in {@link SmsCbCmasInfo}
    334      */
    335     private int getCmasSeverity() {
    336         switch (mMessageIdentifier) {
    337             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED:
    338             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY:
    339             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED:
    340             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY:
    341                 return SmsCbCmasInfo.CMAS_SEVERITY_EXTREME;
    342 
    343             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED:
    344             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY:
    345             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED:
    346             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY:
    347                 return SmsCbCmasInfo.CMAS_SEVERITY_SEVERE;
    348 
    349             default:
    350                 return SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN;
    351         }
    352     }
    353 
    354     /**
    355      * Returns the urgency for a CMAS warning notification. This is only available for extreme
    356      * and severe alerts, not for other types such as Presidential Level and AMBER alerts.
    357      * This method assumes that the message ID has already been checked for CMAS type.
    358      * @return the CMAS urgency as defined in {@link SmsCbCmasInfo}
    359      */
    360     private int getCmasUrgency() {
    361         switch (mMessageIdentifier) {
    362             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED:
    363             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY:
    364             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED:
    365             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY:
    366                 return SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE;
    367 
    368             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED:
    369             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY:
    370             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED:
    371             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY:
    372                 return SmsCbCmasInfo.CMAS_URGENCY_EXPECTED;
    373 
    374             default:
    375                 return SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN;
    376         }
    377     }
    378 
    379     /**
    380      * Returns the certainty for a CMAS warning notification. This is only available for extreme
    381      * and severe alerts, not for other types such as Presidential Level and AMBER alerts.
    382      * This method assumes that the message ID has already been checked for CMAS type.
    383      * @return the CMAS certainty as defined in {@link SmsCbCmasInfo}
    384      */
    385     private int getCmasCertainty() {
    386         switch (mMessageIdentifier) {
    387             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED:
    388             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED:
    389             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED:
    390             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED:
    391                 return SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED;
    392 
    393             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY:
    394             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY:
    395             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY:
    396             case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY:
    397                 return SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY;
    398 
    399             default:
    400                 return SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN;
    401         }
    402     }
    403 
    404     @Override
    405     public String toString() {
    406         return "SmsCbHeader{GS=" + mGeographicalScope + ", serialNumber=0x" +
    407                 Integer.toHexString(mSerialNumber) +
    408                 ", messageIdentifier=0x" + Integer.toHexString(mMessageIdentifier) +
    409                 ", DCS=0x" + Integer.toHexString(mDataCodingScheme) +
    410                 ", page " + mPageIndex + " of " + mNrOfPages + '}';
    411     }
    412 }
    413