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 17package com.android.internal.telephony.gsm; 18 19import android.telephony.SmsCbCmasInfo; 20import android.telephony.SmsCbEtwsInfo; 21 22import 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 */ 34class 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 geographicalScope; 72 73 /** The serial number combines geographical scope, message code, and update number. */ 74 private final int serialNumber; 75 76 /** The Message Identifier in 3GPP is the same as the Service Category in CDMA. */ 77 private final int messageIdentifier; 78 79 private final int dataCodingScheme; 80 81 private final int pageIndex; 82 83 private final int nrOfPages; 84 85 private final int format; 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_ETWS) { 99 format = FORMAT_ETWS_PRIMARY; 100 geographicalScope = (pdu[0] & 0xc0) >> 6; 101 serialNumber = ((pdu[0] & 0xff) << 8) | (pdu[1] & 0xff); 102 messageIdentifier = ((pdu[2] & 0xff) << 8) | (pdu[3] & 0xff); 103 dataCodingScheme = -1; 104 pageIndex = -1; 105 nrOfPages = -1; 106 boolean emergencyUserAlert = (pdu[4] & 0x1) != 0; 107 boolean activatePopup = (pdu[5] & 0x80) != 0; 108 int warningType = (pdu[4] & 0xfe) >> 1; 109 byte[] warningSecurityInfo; 110 // copy the Warning-Security-Information, if present 111 if (pdu.length > PDU_HEADER_LENGTH) { 112 warningSecurityInfo = Arrays.copyOfRange(pdu, 6, pdu.length); 113 } else { 114 warningSecurityInfo = null; 115 } 116 mEtwsInfo = new SmsCbEtwsInfo(warningType, emergencyUserAlert, activatePopup, 117 warningSecurityInfo); 118 mCmasInfo = null; 119 return; // skip the ETWS/CMAS initialization code for regular notifications 120 } else if (pdu.length <= PDU_LENGTH_GSM) { 121 // GSM pdus are no more than 88 bytes 122 format = FORMAT_GSM; 123 geographicalScope = (pdu[0] & 0xc0) >> 6; 124 serialNumber = ((pdu[0] & 0xff) << 8) | (pdu[1] & 0xff); 125 messageIdentifier = ((pdu[2] & 0xff) << 8) | (pdu[3] & 0xff); 126 dataCodingScheme = pdu[4] & 0xff; 127 128 // Check for invalid page parameter 129 int pageIndex = (pdu[5] & 0xf0) >> 4; 130 int nrOfPages = pdu[5] & 0x0f; 131 132 if (pageIndex == 0 || nrOfPages == 0 || pageIndex > nrOfPages) { 133 pageIndex = 1; 134 nrOfPages = 1; 135 } 136 137 this.pageIndex = pageIndex; 138 this.nrOfPages = nrOfPages; 139 } else { 140 // UMTS pdus are always at least 90 bytes since the payload includes 141 // a number-of-pages octet and also one length octet per page 142 format = FORMAT_UMTS; 143 144 int messageType = pdu[0]; 145 146 if (messageType != MESSAGE_TYPE_CBS_MESSAGE) { 147 throw new IllegalArgumentException("Unsupported message type " + messageType); 148 } 149 150 messageIdentifier = ((pdu[1] & 0xff) << 8) | pdu[2] & 0xff; 151 geographicalScope = (pdu[3] & 0xc0) >> 6; 152 serialNumber = ((pdu[3] & 0xff) << 8) | (pdu[4] & 0xff); 153 dataCodingScheme = pdu[5] & 0xff; 154 155 // We will always consider a UMTS message as having one single page 156 // since there's only one instance of the header, even though the 157 // actual payload may contain several pages. 158 pageIndex = 1; 159 nrOfPages = 1; 160 } 161 162 if (isEtwsMessage()) { 163 boolean emergencyUserAlert = isEtwsEmergencyUserAlert(); 164 boolean activatePopup = isEtwsPopupAlert(); 165 int warningType = getEtwsWarningType(); 166 mEtwsInfo = new SmsCbEtwsInfo(warningType, emergencyUserAlert, activatePopup, null); 167 mCmasInfo = null; 168 } else if (isCmasMessage()) { 169 int messageClass = getCmasMessageClass(); 170 int severity = getCmasSeverity(); 171 int urgency = getCmasUrgency(); 172 int certainty = getCmasCertainty(); 173 mEtwsInfo = null; 174 mCmasInfo = new SmsCbCmasInfo(messageClass, SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN, 175 SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN, severity, urgency, certainty); 176 } else { 177 mEtwsInfo = null; 178 mCmasInfo = null; 179 } 180 } 181 182 int getGeographicalScope() { 183 return geographicalScope; 184 } 185 186 int getSerialNumber() { 187 return serialNumber; 188 } 189 190 int getServiceCategory() { 191 return messageIdentifier; 192 } 193 194 int getDataCodingScheme() { 195 return dataCodingScheme; 196 } 197 198 int getPageIndex() { 199 return pageIndex; 200 } 201 202 int getNumberOfPages() { 203 return nrOfPages; 204 } 205 206 SmsCbEtwsInfo getEtwsInfo() { 207 return mEtwsInfo; 208 } 209 210 SmsCbCmasInfo getCmasInfo() { 211 return mCmasInfo; 212 } 213 214 /** 215 * Return whether this broadcast is an emergency (PWS) message type. 216 * @return true if this message is emergency type; false otherwise 217 */ 218 boolean isEmergencyMessage() { 219 return messageIdentifier >= SmsCbConstants.MESSAGE_ID_PWS_FIRST_IDENTIFIER 220 && messageIdentifier <= SmsCbConstants.MESSAGE_ID_PWS_LAST_IDENTIFIER; 221 } 222 223 /** 224 * Return whether this broadcast is an ETWS emergency message type. 225 * @return true if this message is ETWS emergency type; false otherwise 226 */ 227 private boolean isEtwsMessage() { 228 return (messageIdentifier & SmsCbConstants.MESSAGE_ID_ETWS_TYPE_MASK) 229 == SmsCbConstants.MESSAGE_ID_ETWS_TYPE; 230 } 231 232 /** 233 * Return whether this broadcast is an ETWS primary notification. 234 * @return true if this message is an ETWS primary notification; false otherwise 235 */ 236 boolean isEtwsPrimaryNotification() { 237 return format == FORMAT_ETWS_PRIMARY; 238 } 239 240 /** 241 * Return whether this broadcast is in UMTS format. 242 * @return true if this message is in UMTS format; false otherwise 243 */ 244 boolean isUmtsFormat() { 245 return format == FORMAT_UMTS; 246 } 247 248 /** 249 * Return whether this message is a CMAS emergency message type. 250 * @return true if this message is CMAS emergency type; false otherwise 251 */ 252 private boolean isCmasMessage() { 253 return messageIdentifier >= SmsCbConstants.MESSAGE_ID_CMAS_FIRST_IDENTIFIER 254 && messageIdentifier <= SmsCbConstants.MESSAGE_ID_CMAS_LAST_IDENTIFIER; 255 } 256 257 /** 258 * Return whether the popup alert flag is set for an ETWS warning notification. 259 * This method assumes that the message ID has already been checked for ETWS type. 260 * 261 * @return true if the message code indicates a popup alert should be displayed 262 */ 263 private boolean isEtwsPopupAlert() { 264 return (serialNumber & SmsCbConstants.SERIAL_NUMBER_ETWS_ACTIVATE_POPUP) != 0; 265 } 266 267 /** 268 * Return whether the emergency user alert flag is set for an ETWS warning notification. 269 * This method assumes that the message ID has already been checked for ETWS type. 270 * 271 * @return true if the message code indicates an emergency user alert 272 */ 273 private boolean isEtwsEmergencyUserAlert() { 274 return (serialNumber & SmsCbConstants.SERIAL_NUMBER_ETWS_EMERGENCY_USER_ALERT) != 0; 275 } 276 277 /** 278 * Returns the warning type for an ETWS warning notification. 279 * This method assumes that the message ID has already been checked for ETWS type. 280 * 281 * @return the ETWS warning type defined in 3GPP TS 23.041 section 9.3.24 282 */ 283 private int getEtwsWarningType() { 284 return messageIdentifier - SmsCbConstants.MESSAGE_ID_ETWS_EARTHQUAKE_WARNING; 285 } 286 287 /** 288 * Returns the message class for a CMAS warning notification. 289 * This method assumes that the message ID has already been checked for CMAS type. 290 * @return the CMAS message class as defined in {@link SmsCbCmasInfo} 291 */ 292 private int getCmasMessageClass() { 293 switch (messageIdentifier) { 294 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL: 295 return SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT; 296 297 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED: 298 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY: 299 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED: 300 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY: 301 return SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT; 302 303 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED: 304 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY: 305 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED: 306 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY: 307 return SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT; 308 309 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY: 310 return SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY; 311 312 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST: 313 return SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST; 314 315 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXERCISE: 316 return SmsCbCmasInfo.CMAS_CLASS_CMAS_EXERCISE; 317 318 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE: 319 return SmsCbCmasInfo.CMAS_CLASS_OPERATOR_DEFINED_USE; 320 321 default: 322 return SmsCbCmasInfo.CMAS_CLASS_UNKNOWN; 323 } 324 } 325 326 /** 327 * Returns the severity for a CMAS warning notification. This is only available for extreme 328 * and severe alerts, not for other types such as Presidential Level and AMBER alerts. 329 * This method assumes that the message ID has already been checked for CMAS type. 330 * @return the CMAS severity as defined in {@link SmsCbCmasInfo} 331 */ 332 private int getCmasSeverity() { 333 switch (messageIdentifier) { 334 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED: 335 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY: 336 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED: 337 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY: 338 return SmsCbCmasInfo.CMAS_SEVERITY_EXTREME; 339 340 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED: 341 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY: 342 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED: 343 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY: 344 return SmsCbCmasInfo.CMAS_SEVERITY_SEVERE; 345 346 default: 347 return SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN; 348 } 349 } 350 351 /** 352 * Returns the urgency for a CMAS warning notification. This is only available for extreme 353 * and severe alerts, not for other types such as Presidential Level and AMBER alerts. 354 * This method assumes that the message ID has already been checked for CMAS type. 355 * @return the CMAS urgency as defined in {@link SmsCbCmasInfo} 356 */ 357 private int getCmasUrgency() { 358 switch (messageIdentifier) { 359 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED: 360 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY: 361 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED: 362 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY: 363 return SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE; 364 365 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED: 366 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY: 367 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED: 368 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY: 369 return SmsCbCmasInfo.CMAS_URGENCY_EXPECTED; 370 371 default: 372 return SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN; 373 } 374 } 375 376 /** 377 * Returns the certainty for a CMAS warning notification. This is only available for extreme 378 * and severe alerts, not for other types such as Presidential Level and AMBER alerts. 379 * This method assumes that the message ID has already been checked for CMAS type. 380 * @return the CMAS certainty as defined in {@link SmsCbCmasInfo} 381 */ 382 private int getCmasCertainty() { 383 switch (messageIdentifier) { 384 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED: 385 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED: 386 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED: 387 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED: 388 return SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED; 389 390 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY: 391 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY: 392 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY: 393 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY: 394 return SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY; 395 396 default: 397 return SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN; 398 } 399 } 400 401 @Override 402 public String toString() { 403 return "SmsCbHeader{GS=" + geographicalScope + ", serialNumber=0x" + 404 Integer.toHexString(serialNumber) + 405 ", messageIdentifier=0x" + Integer.toHexString(messageIdentifier) + 406 ", DCS=0x" + Integer.toHexString(dataCodingScheme) + 407 ", page " + pageIndex + " of " + nrOfPages + '}'; 408 } 409} 410