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.cellbroadcastservice; 18 19 import android.telephony.SmsCbCmasInfo; 20 import android.telephony.SmsCbEtwsInfo; 21 import android.telephony.SmsMessage; 22 23 import com.android.internal.annotations.VisibleForTesting; 24 25 import java.util.Arrays; 26 import java.util.Locale; 27 28 /** 29 * Parses a 3GPP TS 23.041 cell broadcast message header. This class is public for use by 30 * CellBroadcastReceiver test cases, but should not be used by applications. 31 * 32 * All relevant header information is now sent as a Parcelable 33 * {@link android.telephony.SmsCbMessage} object in the "message" extra of the 34 * {@link android.provider.Telephony.Sms.Intents#SMS_CB_RECEIVED_ACTION} or 35 * {@link android.provider.Telephony.Sms.Intents#ACTION_SMS_EMERGENCY_CB_RECEIVED} intent. 36 * The raw PDU is no longer sent to SMS CB applications. 37 */ 38 public class SmsCbHeader { 39 /** 40 * Languages in the 0000xxxx DCS group as defined in 3GPP TS 23.038, section 5. 41 */ 42 private static final String[] LANGUAGE_CODES_GROUP_0 = { 43 Locale.GERMAN.getLanguage(), // German 44 Locale.ENGLISH.getLanguage(), // English 45 Locale.ITALIAN.getLanguage(), // Italian 46 Locale.FRENCH.getLanguage(), // French 47 new Locale("es").getLanguage(), // Spanish 48 new Locale("nl").getLanguage(), // Dutch 49 new Locale("sv").getLanguage(), // Swedish 50 new Locale("da").getLanguage(), // Danish 51 new Locale("pt").getLanguage(), // Portuguese 52 new Locale("fi").getLanguage(), // Finnish 53 new Locale("nb").getLanguage(), // Norwegian 54 new Locale("el").getLanguage(), // Greek 55 new Locale("tr").getLanguage(), // Turkish 56 new Locale("hu").getLanguage(), // Hungarian 57 new Locale("pl").getLanguage(), // Polish 58 null 59 }; 60 61 /** 62 * Languages in the 0010xxxx DCS group as defined in 3GPP TS 23.038, section 5. 63 */ 64 private static final String[] LANGUAGE_CODES_GROUP_2 = { 65 new Locale("cs").getLanguage(), // Czech 66 new Locale("he").getLanguage(), // Hebrew 67 new Locale("ar").getLanguage(), // Arabic 68 new Locale("ru").getLanguage(), // Russian 69 new Locale("is").getLanguage(), // Icelandic 70 null, null, null, null, null, null, null, null, null, null, null 71 }; 72 73 /** 74 * Length of SMS-CB header 75 */ 76 public static final int PDU_HEADER_LENGTH = 6; 77 78 /** 79 * GSM pdu format, as defined in 3gpp TS 23.041, section 9.4.1 80 */ 81 static final int FORMAT_GSM = 1; 82 83 /** 84 * UMTS pdu format, as defined in 3gpp TS 23.041, section 9.4.2 85 */ 86 static final int FORMAT_UMTS = 2; 87 88 /** 89 * ETWS pdu format, as defined in 3gpp TS 23.041, section 9.4.1.3 90 */ 91 static final int FORMAT_ETWS_PRIMARY = 3; 92 93 /** 94 * Message type value as defined in 3gpp TS 25.324, section 11.1. 95 */ 96 private static final int MESSAGE_TYPE_CBS_MESSAGE = 1; 97 98 /** 99 * Length of GSM pdus 100 */ 101 private static final int PDU_LENGTH_GSM = 88; 102 103 /** 104 * Maximum length of ETWS primary message GSM pdus 105 */ 106 private static final int PDU_LENGTH_ETWS = 56; 107 108 private final int mGeographicalScope; 109 110 /** The serial number combines geographical scope, message code, and update number. */ 111 private final int mSerialNumber; 112 113 /** The Message Identifier in 3GPP is the same as the Service Category in CDMA. */ 114 private final int mMessageIdentifier; 115 116 private final int mDataCodingScheme; 117 118 private final int mPageIndex; 119 120 private final int mNrOfPages; 121 122 private final int mFormat; 123 124 private DataCodingScheme mDataCodingSchemeStructedData; 125 126 /** ETWS warning notification info. */ 127 private final SmsCbEtwsInfo mEtwsInfo; 128 129 /** CMAS warning notification info. */ 130 private final SmsCbCmasInfo mCmasInfo; 131 SmsCbHeader(byte[] pdu)132 public SmsCbHeader(byte[] pdu) throws IllegalArgumentException { 133 if (pdu == null || pdu.length < PDU_HEADER_LENGTH) { 134 final String errorMessage = "Illegal PDU"; 135 CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_ERROR, 136 CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_ERROR__TYPE__GSM_INVALID_HEADER_LENGTH, 137 errorMessage); 138 throw new IllegalArgumentException(errorMessage); 139 } 140 141 if (pdu.length <= PDU_LENGTH_GSM) { 142 // can be ETWS or GSM format. 143 // Per TS23.041 9.4.1.2 and 9.4.1.3.2, GSM and ETWS format both 144 // contain serial number which contains GS, Message Code, and Update Number 145 // per 9.4.1.2.1, and message identifier in same octets 146 mGeographicalScope = (pdu[0] & 0xc0) >>> 6; 147 mSerialNumber = ((pdu[0] & 0xff) << 8) | (pdu[1] & 0xff); 148 mMessageIdentifier = ((pdu[2] & 0xff) << 8) | (pdu[3] & 0xff); 149 if (isEtwsMessage() && pdu.length <= PDU_LENGTH_ETWS) { 150 mFormat = FORMAT_ETWS_PRIMARY; 151 mDataCodingScheme = -1; 152 mPageIndex = -1; 153 mNrOfPages = -1; 154 boolean emergencyUserAlert = (pdu[4] & 0x1) != 0; 155 boolean activatePopup = (pdu[5] & 0x80) != 0; 156 int warningType = (pdu[4] & 0xfe) >>> 1; 157 byte[] warningSecurityInfo; 158 // copy the Warning-Security-Information, if present 159 if (pdu.length > PDU_HEADER_LENGTH) { 160 warningSecurityInfo = Arrays.copyOfRange(pdu, 6, pdu.length); 161 } else { 162 warningSecurityInfo = null; 163 } 164 mEtwsInfo = new SmsCbEtwsInfo(warningType, emergencyUserAlert, activatePopup, 165 true, warningSecurityInfo); 166 mCmasInfo = null; 167 return; // skip the ETWS/CMAS initialization code for regular notifications 168 } else { 169 // GSM pdus are no more than 88 bytes 170 mFormat = FORMAT_GSM; 171 mDataCodingScheme = pdu[4] & 0xff; 172 173 // Check for invalid page parameter 174 int pageIndex = (pdu[5] & 0xf0) >>> 4; 175 int nrOfPages = pdu[5] & 0x0f; 176 177 if (pageIndex == 0 || nrOfPages == 0 || pageIndex > nrOfPages) { 178 pageIndex = 1; 179 nrOfPages = 1; 180 } 181 182 mPageIndex = pageIndex; 183 mNrOfPages = nrOfPages; 184 } 185 } else { 186 // UMTS pdus are always at least 90 bytes since the payload includes 187 // a number-of-pages octet and also one length octet per page 188 mFormat = FORMAT_UMTS; 189 190 int messageType = pdu[0]; 191 192 if (messageType != MESSAGE_TYPE_CBS_MESSAGE) { 193 IllegalArgumentException ex = new IllegalArgumentException( 194 "Unsupported message type " + messageType); 195 CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_ERROR, 196 CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_ERROR__TYPE__GSM_UNSUPPORTED_HEADER_MESSAGE_TYPE, 197 ex.toString()); 198 throw ex; 199 } 200 201 mMessageIdentifier = ((pdu[1] & 0xff) << 8) | pdu[2] & 0xff; 202 mGeographicalScope = (pdu[3] & 0xc0) >>> 6; 203 mSerialNumber = ((pdu[3] & 0xff) << 8) | (pdu[4] & 0xff); 204 mDataCodingScheme = pdu[5] & 0xff; 205 206 // We will always consider a UMTS message as having one single page 207 // since there's only one instance of the header, even though the 208 // actual payload may contain several pages. 209 mPageIndex = 1; 210 mNrOfPages = 1; 211 } 212 213 if (mDataCodingScheme != -1) { 214 mDataCodingSchemeStructedData = new DataCodingScheme(mDataCodingScheme); 215 } 216 217 if (isEtwsMessage()) { 218 boolean emergencyUserAlert = isEtwsEmergencyUserAlert(); 219 boolean activatePopup = isEtwsPopupAlert(); 220 int warningType = getEtwsWarningType(); 221 mEtwsInfo = new SmsCbEtwsInfo(warningType, emergencyUserAlert, activatePopup, 222 false, null); 223 mCmasInfo = null; 224 } else if (isCmasMessage()) { 225 int messageClass = getCmasMessageClass(); 226 int severity = getCmasSeverity(); 227 int urgency = getCmasUrgency(); 228 int certainty = getCmasCertainty(); 229 mEtwsInfo = null; 230 mCmasInfo = new SmsCbCmasInfo(messageClass, SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN, 231 SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN, severity, urgency, certainty); 232 } else { 233 mEtwsInfo = null; 234 mCmasInfo = null; 235 } 236 } 237 getGeographicalScope()238 public int getGeographicalScope() { 239 return mGeographicalScope; 240 } 241 getSerialNumber()242 public int getSerialNumber() { 243 return mSerialNumber; 244 } 245 getServiceCategory()246 public int getServiceCategory() { 247 return mMessageIdentifier; 248 } 249 getDataCodingScheme()250 public int getDataCodingScheme() { 251 return mDataCodingScheme; 252 } 253 getDataCodingSchemeStructedData()254 public DataCodingScheme getDataCodingSchemeStructedData() { 255 return mDataCodingSchemeStructedData; 256 } 257 getPageIndex()258 public int getPageIndex() { 259 return mPageIndex; 260 } 261 getNumberOfPages()262 public int getNumberOfPages() { 263 return mNrOfPages; 264 } 265 getEtwsInfo()266 public SmsCbEtwsInfo getEtwsInfo() { 267 return mEtwsInfo; 268 } 269 getCmasInfo()270 public SmsCbCmasInfo getCmasInfo() { 271 return mCmasInfo; 272 } 273 274 /** 275 * Return whether this broadcast is an emergency (PWS) message type. 276 * @return true if this message is emergency type; false otherwise 277 */ isEmergencyMessage()278 public boolean isEmergencyMessage() { 279 return mMessageIdentifier >= SmsCbConstants.MESSAGE_ID_PWS_FIRST_IDENTIFIER 280 && mMessageIdentifier <= SmsCbConstants.MESSAGE_ID_PWS_LAST_IDENTIFIER; 281 } 282 283 /** 284 * Return whether this broadcast is an ETWS emergency message type. 285 * @return true if this message is ETWS emergency type; false otherwise 286 */ 287 @VisibleForTesting isEtwsMessage()288 public boolean isEtwsMessage() { 289 return (mMessageIdentifier & SmsCbConstants.MESSAGE_ID_ETWS_TYPE_MASK) 290 == SmsCbConstants.MESSAGE_ID_ETWS_TYPE; 291 } 292 293 /** 294 * Return whether this broadcast is an ETWS primary notification. 295 * @return true if this message is an ETWS primary notification; false otherwise 296 */ isEtwsPrimaryNotification()297 public boolean isEtwsPrimaryNotification() { 298 return mFormat == FORMAT_ETWS_PRIMARY; 299 } 300 301 /** 302 * Return whether this broadcast is in UMTS format. 303 * @return true if this message is in UMTS format; false otherwise 304 */ isUmtsFormat()305 public boolean isUmtsFormat() { 306 return mFormat == FORMAT_UMTS; 307 } 308 309 /** 310 * Return whether this message is a CMAS emergency message type. 311 * @return true if this message is CMAS emergency type; false otherwise 312 */ isCmasMessage()313 private boolean isCmasMessage() { 314 return mMessageIdentifier >= SmsCbConstants.MESSAGE_ID_CMAS_FIRST_IDENTIFIER 315 && mMessageIdentifier <= SmsCbConstants.MESSAGE_ID_CMAS_LAST_IDENTIFIER; 316 } 317 318 /** 319 * Return whether the popup alert flag is set for an ETWS warning notification. 320 * This method assumes that the message ID has already been checked for ETWS type. 321 * 322 * @return true if the message code indicates a popup alert should be displayed 323 */ isEtwsPopupAlert()324 private boolean isEtwsPopupAlert() { 325 return (mSerialNumber & SmsCbConstants.SERIAL_NUMBER_ETWS_ACTIVATE_POPUP) != 0; 326 } 327 328 /** 329 * Return whether the emergency user alert flag is set for an ETWS warning notification. 330 * This method assumes that the message ID has already been checked for ETWS type. 331 * 332 * @return true if the message code indicates an emergency user alert 333 */ isEtwsEmergencyUserAlert()334 private boolean isEtwsEmergencyUserAlert() { 335 return (mSerialNumber & SmsCbConstants.SERIAL_NUMBER_ETWS_EMERGENCY_USER_ALERT) != 0; 336 } 337 338 /** 339 * Returns the warning type for an ETWS warning notification. 340 * This method assumes that the message ID has already been checked for ETWS type. 341 * 342 * @return the ETWS warning type defined in 3GPP TS 23.041 section 9.3.24 343 */ getEtwsWarningType()344 private int getEtwsWarningType() { 345 return mMessageIdentifier - SmsCbConstants.MESSAGE_ID_ETWS_EARTHQUAKE_WARNING; 346 } 347 348 /** 349 * Returns the message class for a CMAS warning notification. 350 * This method assumes that the message ID has already been checked for CMAS type. 351 * @return the CMAS message class as defined in {@link SmsCbCmasInfo} 352 */ getCmasMessageClass()353 private int getCmasMessageClass() { 354 switch (mMessageIdentifier) { 355 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL: 356 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL_LANGUAGE: 357 return SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT; 358 359 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED: 360 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED_LANGUAGE: 361 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY: 362 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY_LANGUAGE: 363 return SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT; 364 365 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED: 366 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED_LANGUAGE: 367 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY: 368 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY_LANGUAGE: 369 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED: 370 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED_LANGUAGE: 371 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY: 372 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY_LANGUAGE: 373 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED: 374 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED_LANGUAGE: 375 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY: 376 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY_LANGUAGE: 377 return SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT; 378 379 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY: 380 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY_LANGUAGE: 381 return SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY; 382 383 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST: 384 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST_LANGUAGE: 385 return SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST; 386 387 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXERCISE: 388 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXERCISE_LANGUAGE: 389 return SmsCbCmasInfo.CMAS_CLASS_CMAS_EXERCISE; 390 391 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE: 392 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE_LANGUAGE: 393 return SmsCbCmasInfo.CMAS_CLASS_OPERATOR_DEFINED_USE; 394 395 default: 396 return SmsCbCmasInfo.CMAS_CLASS_UNKNOWN; 397 } 398 } 399 400 /** 401 * Returns the severity for a CMAS warning notification. This is only available for extreme 402 * and severe alerts, not for other types such as Presidential Level and AMBER alerts. 403 * This method assumes that the message ID has already been checked for CMAS type. 404 * @return the CMAS severity as defined in {@link SmsCbCmasInfo} 405 */ getCmasSeverity()406 private int getCmasSeverity() { 407 switch (mMessageIdentifier) { 408 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED: 409 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED_LANGUAGE: 410 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY: 411 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY_LANGUAGE: 412 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED: 413 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED_LANGUAGE: 414 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY: 415 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY_LANGUAGE: 416 return SmsCbCmasInfo.CMAS_SEVERITY_EXTREME; 417 418 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED: 419 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED_LANGUAGE: 420 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY: 421 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY_LANGUAGE: 422 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED: 423 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED_LANGUAGE: 424 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY: 425 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY_LANGUAGE: 426 return SmsCbCmasInfo.CMAS_SEVERITY_SEVERE; 427 428 default: 429 return SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN; 430 } 431 } 432 433 /** 434 * Returns the urgency for a CMAS warning notification. This is only available for extreme 435 * and severe alerts, not for other types such as Presidential Level and AMBER alerts. 436 * This method assumes that the message ID has already been checked for CMAS type. 437 * @return the CMAS urgency as defined in {@link SmsCbCmasInfo} 438 */ getCmasUrgency()439 private int getCmasUrgency() { 440 switch (mMessageIdentifier) { 441 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED: 442 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED_LANGUAGE: 443 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY: 444 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY_LANGUAGE: 445 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED: 446 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED_LANGUAGE: 447 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY: 448 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY_LANGUAGE: 449 return SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE; 450 451 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED: 452 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED_LANGUAGE: 453 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY: 454 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY_LANGUAGE: 455 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED: 456 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED_LANGUAGE: 457 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY: 458 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY_LANGUAGE: 459 return SmsCbCmasInfo.CMAS_URGENCY_EXPECTED; 460 461 default: 462 return SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN; 463 } 464 } 465 466 /** 467 * Returns the certainty for a CMAS warning notification. This is only available for extreme 468 * and severe alerts, not for other types such as Presidential Level and AMBER alerts. 469 * This method assumes that the message ID has already been checked for CMAS type. 470 * @return the CMAS certainty as defined in {@link SmsCbCmasInfo} 471 */ getCmasCertainty()472 private int getCmasCertainty() { 473 switch (mMessageIdentifier) { 474 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED: 475 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED_LANGUAGE: 476 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED: 477 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED_LANGUAGE: 478 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED: 479 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED_LANGUAGE: 480 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED: 481 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED_LANGUAGE: 482 return SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED; 483 484 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY: 485 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY_LANGUAGE: 486 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY: 487 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY_LANGUAGE: 488 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY: 489 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY_LANGUAGE: 490 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY: 491 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY_LANGUAGE: 492 return SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY; 493 494 default: 495 return SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN; 496 } 497 } 498 499 @Override toString()500 public String toString() { 501 return "SmsCbHeader{GS=" + mGeographicalScope + ", serialNumber=0x" 502 + Integer.toHexString(mSerialNumber) 503 + ", messageIdentifier=0x" + Integer.toHexString(mMessageIdentifier) 504 + ", format=" + mFormat 505 + ", DCS=0x" + Integer.toHexString(mDataCodingScheme) 506 + ", page " + mPageIndex + " of " + mNrOfPages + '}'; 507 } 508 509 /** 510 * CBS Data Coding Scheme. 511 * Reference: 3GPP TS 23.038 version 15.0.0 section #5, CBS Data Coding Scheme 512 */ 513 public static final class DataCodingScheme { 514 public final int encoding; 515 public final String language; 516 public final boolean hasLanguageIndicator; 517 DataCodingScheme(int dataCodingScheme)518 public DataCodingScheme(int dataCodingScheme) { 519 int encoding = 0; 520 String language = null; 521 boolean hasLanguageIndicator = false; 522 523 // Extract encoding and language from DCS, as defined in 3gpp TS 23.038, 524 // section 5. 525 switch ((dataCodingScheme & 0xf0) >> 4) { 526 case 0x00: 527 encoding = SmsMessage.ENCODING_7BIT; 528 language = LANGUAGE_CODES_GROUP_0[dataCodingScheme & 0x0f]; 529 break; 530 531 case 0x01: 532 hasLanguageIndicator = true; 533 if ((dataCodingScheme & 0x0f) == 0x01) { 534 encoding = SmsMessage.ENCODING_16BIT; 535 } else { 536 encoding = SmsMessage.ENCODING_7BIT; 537 } 538 break; 539 540 case 0x02: 541 encoding = SmsMessage.ENCODING_7BIT; 542 language = LANGUAGE_CODES_GROUP_2[dataCodingScheme & 0x0f]; 543 break; 544 545 case 0x03: 546 encoding = SmsMessage.ENCODING_7BIT; 547 break; 548 549 case 0x04: 550 case 0x05: 551 switch ((dataCodingScheme & 0x0c) >> 2) { 552 case 0x01: 553 encoding = SmsMessage.ENCODING_8BIT; 554 break; 555 556 case 0x02: 557 encoding = SmsMessage.ENCODING_16BIT; 558 break; 559 560 case 0x00: 561 default: 562 encoding = SmsMessage.ENCODING_7BIT; 563 break; 564 } 565 break; 566 567 case 0x06: 568 case 0x07: 569 // Compression not supported 570 case 0x09: 571 // UDH structure not supported 572 case 0x0e: 573 // Defined by the WAP forum not supported 574 final String errorMessage = 575 "Unsupported GSM dataCodingScheme " + dataCodingScheme; 576 CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_ERROR, 577 CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_ERROR__TYPE__GSM_UNSUPPORTED_HEADER_DATA_CODING_SCHEME, 578 errorMessage); 579 throw new IllegalArgumentException(errorMessage); 580 581 case 0x0f: 582 if (((dataCodingScheme & 0x04) >> 2) == 0x01) { 583 encoding = SmsMessage.ENCODING_8BIT; 584 } else { 585 encoding = SmsMessage.ENCODING_7BIT; 586 } 587 break; 588 589 default: 590 // Reserved values are to be treated as 7-bit 591 encoding = SmsMessage.ENCODING_7BIT; 592 break; 593 } 594 595 596 this.encoding = encoding; 597 this.language = language; 598 this.hasLanguageIndicator = hasLanguageIndicator; 599 } 600 } 601 } 602