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