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