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 throw new IllegalArgumentException("Illegal PDU"); 135 } 136 137 if (pdu.length <= PDU_LENGTH_GSM) { 138 // can be ETWS or GSM format. 139 // Per TS23.041 9.4.1.2 and 9.4.1.3.2, GSM and ETWS format both 140 // contain serial number which contains GS, Message Code, and Update Number 141 // per 9.4.1.2.1, and message identifier in same octets 142 mGeographicalScope = (pdu[0] & 0xc0) >>> 6; 143 mSerialNumber = ((pdu[0] & 0xff) << 8) | (pdu[1] & 0xff); 144 mMessageIdentifier = ((pdu[2] & 0xff) << 8) | (pdu[3] & 0xff); 145 if (isEtwsMessage() && pdu.length <= PDU_LENGTH_ETWS) { 146 mFormat = FORMAT_ETWS_PRIMARY; 147 mDataCodingScheme = -1; 148 mPageIndex = -1; 149 mNrOfPages = -1; 150 boolean emergencyUserAlert = (pdu[4] & 0x1) != 0; 151 boolean activatePopup = (pdu[5] & 0x80) != 0; 152 int warningType = (pdu[4] & 0xfe) >>> 1; 153 byte[] warningSecurityInfo; 154 // copy the Warning-Security-Information, if present 155 if (pdu.length > PDU_HEADER_LENGTH) { 156 warningSecurityInfo = Arrays.copyOfRange(pdu, 6, pdu.length); 157 } else { 158 warningSecurityInfo = null; 159 } 160 mEtwsInfo = new SmsCbEtwsInfo(warningType, emergencyUserAlert, activatePopup, 161 true, warningSecurityInfo); 162 mCmasInfo = null; 163 return; // skip the ETWS/CMAS initialization code for regular notifications 164 } else { 165 // GSM pdus are no more than 88 bytes 166 mFormat = FORMAT_GSM; 167 mDataCodingScheme = pdu[4] & 0xff; 168 169 // Check for invalid page parameter 170 int pageIndex = (pdu[5] & 0xf0) >>> 4; 171 int nrOfPages = pdu[5] & 0x0f; 172 173 if (pageIndex == 0 || nrOfPages == 0 || pageIndex > nrOfPages) { 174 pageIndex = 1; 175 nrOfPages = 1; 176 } 177 178 mPageIndex = pageIndex; 179 mNrOfPages = nrOfPages; 180 } 181 } else { 182 // UMTS pdus are always at least 90 bytes since the payload includes 183 // a number-of-pages octet and also one length octet per page 184 mFormat = FORMAT_UMTS; 185 186 int messageType = pdu[0]; 187 188 if (messageType != MESSAGE_TYPE_CBS_MESSAGE) { 189 throw new IllegalArgumentException("Unsupported message type " + messageType); 190 } 191 192 mMessageIdentifier = ((pdu[1] & 0xff) << 8) | pdu[2] & 0xff; 193 mGeographicalScope = (pdu[3] & 0xc0) >>> 6; 194 mSerialNumber = ((pdu[3] & 0xff) << 8) | (pdu[4] & 0xff); 195 mDataCodingScheme = pdu[5] & 0xff; 196 197 // We will always consider a UMTS message as having one single page 198 // since there's only one instance of the header, even though the 199 // actual payload may contain several pages. 200 mPageIndex = 1; 201 mNrOfPages = 1; 202 } 203 204 if (mDataCodingScheme != -1) { 205 mDataCodingSchemeStructedData = new DataCodingScheme(mDataCodingScheme); 206 } 207 208 if (isEtwsMessage()) { 209 boolean emergencyUserAlert = isEtwsEmergencyUserAlert(); 210 boolean activatePopup = isEtwsPopupAlert(); 211 int warningType = getEtwsWarningType(); 212 mEtwsInfo = new SmsCbEtwsInfo(warningType, emergencyUserAlert, activatePopup, 213 false, null); 214 mCmasInfo = null; 215 } else if (isCmasMessage()) { 216 int messageClass = getCmasMessageClass(); 217 int severity = getCmasSeverity(); 218 int urgency = getCmasUrgency(); 219 int certainty = getCmasCertainty(); 220 mEtwsInfo = null; 221 mCmasInfo = new SmsCbCmasInfo(messageClass, SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN, 222 SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN, severity, urgency, certainty); 223 } else { 224 mEtwsInfo = null; 225 mCmasInfo = null; 226 } 227 } 228 getGeographicalScope()229 public int getGeographicalScope() { 230 return mGeographicalScope; 231 } 232 getSerialNumber()233 public int getSerialNumber() { 234 return mSerialNumber; 235 } 236 getServiceCategory()237 public int getServiceCategory() { 238 return mMessageIdentifier; 239 } 240 getDataCodingScheme()241 public int getDataCodingScheme() { 242 return mDataCodingScheme; 243 } 244 getDataCodingSchemeStructedData()245 public DataCodingScheme getDataCodingSchemeStructedData() { 246 return mDataCodingSchemeStructedData; 247 } 248 getPageIndex()249 public int getPageIndex() { 250 return mPageIndex; 251 } 252 getNumberOfPages()253 public int getNumberOfPages() { 254 return mNrOfPages; 255 } 256 getEtwsInfo()257 public SmsCbEtwsInfo getEtwsInfo() { 258 return mEtwsInfo; 259 } 260 getCmasInfo()261 public SmsCbCmasInfo getCmasInfo() { 262 return mCmasInfo; 263 } 264 265 /** 266 * Return whether this broadcast is an emergency (PWS) message type. 267 * @return true if this message is emergency type; false otherwise 268 */ isEmergencyMessage()269 public boolean isEmergencyMessage() { 270 return mMessageIdentifier >= SmsCbConstants.MESSAGE_ID_PWS_FIRST_IDENTIFIER 271 && mMessageIdentifier <= SmsCbConstants.MESSAGE_ID_PWS_LAST_IDENTIFIER; 272 } 273 274 /** 275 * Return whether this broadcast is an ETWS emergency message type. 276 * @return true if this message is ETWS emergency type; false otherwise 277 */ 278 @VisibleForTesting isEtwsMessage()279 public boolean isEtwsMessage() { 280 return (mMessageIdentifier & SmsCbConstants.MESSAGE_ID_ETWS_TYPE_MASK) 281 == SmsCbConstants.MESSAGE_ID_ETWS_TYPE; 282 } 283 284 /** 285 * Return whether this broadcast is an ETWS primary notification. 286 * @return true if this message is an ETWS primary notification; false otherwise 287 */ isEtwsPrimaryNotification()288 public boolean isEtwsPrimaryNotification() { 289 return mFormat == FORMAT_ETWS_PRIMARY; 290 } 291 292 /** 293 * Return whether this broadcast is in UMTS format. 294 * @return true if this message is in UMTS format; false otherwise 295 */ isUmtsFormat()296 public boolean isUmtsFormat() { 297 return mFormat == FORMAT_UMTS; 298 } 299 300 /** 301 * Return whether this message is a CMAS emergency message type. 302 * @return true if this message is CMAS emergency type; false otherwise 303 */ isCmasMessage()304 private boolean isCmasMessage() { 305 return mMessageIdentifier >= SmsCbConstants.MESSAGE_ID_CMAS_FIRST_IDENTIFIER 306 && mMessageIdentifier <= SmsCbConstants.MESSAGE_ID_CMAS_LAST_IDENTIFIER; 307 } 308 309 /** 310 * Return whether the popup alert flag is set for an ETWS warning notification. 311 * This method assumes that the message ID has already been checked for ETWS type. 312 * 313 * @return true if the message code indicates a popup alert should be displayed 314 */ isEtwsPopupAlert()315 private boolean isEtwsPopupAlert() { 316 return (mSerialNumber & SmsCbConstants.SERIAL_NUMBER_ETWS_ACTIVATE_POPUP) != 0; 317 } 318 319 /** 320 * Return whether the emergency user alert flag is set for an ETWS warning notification. 321 * This method assumes that the message ID has already been checked for ETWS type. 322 * 323 * @return true if the message code indicates an emergency user alert 324 */ isEtwsEmergencyUserAlert()325 private boolean isEtwsEmergencyUserAlert() { 326 return (mSerialNumber & SmsCbConstants.SERIAL_NUMBER_ETWS_EMERGENCY_USER_ALERT) != 0; 327 } 328 329 /** 330 * Returns the warning type for an ETWS warning notification. 331 * This method assumes that the message ID has already been checked for ETWS type. 332 * 333 * @return the ETWS warning type defined in 3GPP TS 23.041 section 9.3.24 334 */ getEtwsWarningType()335 private int getEtwsWarningType() { 336 return mMessageIdentifier - SmsCbConstants.MESSAGE_ID_ETWS_EARTHQUAKE_WARNING; 337 } 338 339 /** 340 * Returns the message class for a CMAS warning notification. 341 * This method assumes that the message ID has already been checked for CMAS type. 342 * @return the CMAS message class as defined in {@link SmsCbCmasInfo} 343 */ getCmasMessageClass()344 private int getCmasMessageClass() { 345 switch (mMessageIdentifier) { 346 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL: 347 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL_LANGUAGE: 348 return SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT; 349 350 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED: 351 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED_LANGUAGE: 352 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY: 353 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY_LANGUAGE: 354 return SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT; 355 356 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED: 357 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED_LANGUAGE: 358 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY: 359 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY_LANGUAGE: 360 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED: 361 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED_LANGUAGE: 362 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY: 363 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY_LANGUAGE: 364 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED: 365 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED_LANGUAGE: 366 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY: 367 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY_LANGUAGE: 368 return SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT; 369 370 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY: 371 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY_LANGUAGE: 372 return SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY; 373 374 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST: 375 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST_LANGUAGE: 376 return SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST; 377 378 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXERCISE: 379 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXERCISE_LANGUAGE: 380 return SmsCbCmasInfo.CMAS_CLASS_CMAS_EXERCISE; 381 382 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE: 383 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE_LANGUAGE: 384 return SmsCbCmasInfo.CMAS_CLASS_OPERATOR_DEFINED_USE; 385 386 default: 387 return SmsCbCmasInfo.CMAS_CLASS_UNKNOWN; 388 } 389 } 390 391 /** 392 * Returns the severity for a CMAS warning notification. This is only available for extreme 393 * and severe alerts, not for other types such as Presidential Level and AMBER alerts. 394 * This method assumes that the message ID has already been checked for CMAS type. 395 * @return the CMAS severity as defined in {@link SmsCbCmasInfo} 396 */ getCmasSeverity()397 private int getCmasSeverity() { 398 switch (mMessageIdentifier) { 399 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED: 400 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED_LANGUAGE: 401 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY: 402 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY_LANGUAGE: 403 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED: 404 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED_LANGUAGE: 405 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY: 406 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY_LANGUAGE: 407 return SmsCbCmasInfo.CMAS_SEVERITY_EXTREME; 408 409 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED: 410 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED_LANGUAGE: 411 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY: 412 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY_LANGUAGE: 413 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED: 414 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED_LANGUAGE: 415 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY: 416 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY_LANGUAGE: 417 return SmsCbCmasInfo.CMAS_SEVERITY_SEVERE; 418 419 default: 420 return SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN; 421 } 422 } 423 424 /** 425 * Returns the urgency for a CMAS warning notification. This is only available for extreme 426 * and severe alerts, not for other types such as Presidential Level and AMBER alerts. 427 * This method assumes that the message ID has already been checked for CMAS type. 428 * @return the CMAS urgency as defined in {@link SmsCbCmasInfo} 429 */ getCmasUrgency()430 private int getCmasUrgency() { 431 switch (mMessageIdentifier) { 432 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED: 433 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED_LANGUAGE: 434 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY: 435 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY_LANGUAGE: 436 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED: 437 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED_LANGUAGE: 438 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY: 439 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY_LANGUAGE: 440 return SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE; 441 442 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED: 443 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED_LANGUAGE: 444 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY: 445 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY_LANGUAGE: 446 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED: 447 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED_LANGUAGE: 448 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY: 449 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY_LANGUAGE: 450 return SmsCbCmasInfo.CMAS_URGENCY_EXPECTED; 451 452 default: 453 return SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN; 454 } 455 } 456 457 /** 458 * Returns the certainty for a CMAS warning notification. This is only available for extreme 459 * and severe alerts, not for other types such as Presidential Level and AMBER alerts. 460 * This method assumes that the message ID has already been checked for CMAS type. 461 * @return the CMAS certainty as defined in {@link SmsCbCmasInfo} 462 */ getCmasCertainty()463 private int getCmasCertainty() { 464 switch (mMessageIdentifier) { 465 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED: 466 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED_LANGUAGE: 467 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED: 468 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED_LANGUAGE: 469 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED: 470 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED_LANGUAGE: 471 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED: 472 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED_LANGUAGE: 473 return SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED; 474 475 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY: 476 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY_LANGUAGE: 477 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY: 478 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY_LANGUAGE: 479 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY: 480 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY_LANGUAGE: 481 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY: 482 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY_LANGUAGE: 483 return SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY; 484 485 default: 486 return SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN; 487 } 488 } 489 490 @Override toString()491 public String toString() { 492 return "SmsCbHeader{GS=" + mGeographicalScope + ", serialNumber=0x" 493 + Integer.toHexString(mSerialNumber) 494 + ", messageIdentifier=0x" + Integer.toHexString(mMessageIdentifier) 495 + ", format=" + mFormat 496 + ", DCS=0x" + Integer.toHexString(mDataCodingScheme) 497 + ", page " + mPageIndex + " of " + mNrOfPages + '}'; 498 } 499 500 /** 501 * CBS Data Coding Scheme. 502 * Reference: 3GPP TS 23.038 version 15.0.0 section #5, CBS Data Coding Scheme 503 */ 504 public static final class DataCodingScheme { 505 public final int encoding; 506 public final String language; 507 public final boolean hasLanguageIndicator; 508 DataCodingScheme(int dataCodingScheme)509 public DataCodingScheme(int dataCodingScheme) { 510 int encoding = 0; 511 String language = null; 512 boolean hasLanguageIndicator = false; 513 514 // Extract encoding and language from DCS, as defined in 3gpp TS 23.038, 515 // section 5. 516 switch ((dataCodingScheme & 0xf0) >> 4) { 517 case 0x00: 518 encoding = SmsMessage.ENCODING_7BIT; 519 language = LANGUAGE_CODES_GROUP_0[dataCodingScheme & 0x0f]; 520 break; 521 522 case 0x01: 523 hasLanguageIndicator = true; 524 if ((dataCodingScheme & 0x0f) == 0x01) { 525 encoding = SmsMessage.ENCODING_16BIT; 526 } else { 527 encoding = SmsMessage.ENCODING_7BIT; 528 } 529 break; 530 531 case 0x02: 532 encoding = SmsMessage.ENCODING_7BIT; 533 language = LANGUAGE_CODES_GROUP_2[dataCodingScheme & 0x0f]; 534 break; 535 536 case 0x03: 537 encoding = SmsMessage.ENCODING_7BIT; 538 break; 539 540 case 0x04: 541 case 0x05: 542 switch ((dataCodingScheme & 0x0c) >> 2) { 543 case 0x01: 544 encoding = SmsMessage.ENCODING_8BIT; 545 break; 546 547 case 0x02: 548 encoding = SmsMessage.ENCODING_16BIT; 549 break; 550 551 case 0x00: 552 default: 553 encoding = SmsMessage.ENCODING_7BIT; 554 break; 555 } 556 break; 557 558 case 0x06: 559 case 0x07: 560 // Compression not supported 561 case 0x09: 562 // UDH structure not supported 563 case 0x0e: 564 // Defined by the WAP forum not supported 565 throw new IllegalArgumentException("Unsupported GSM dataCodingScheme " 566 + dataCodingScheme); 567 568 case 0x0f: 569 if (((dataCodingScheme & 0x04) >> 2) == 0x01) { 570 encoding = SmsMessage.ENCODING_8BIT; 571 } else { 572 encoding = SmsMessage.ENCODING_7BIT; 573 } 574 break; 575 576 default: 577 // Reserved values are to be treated as 7-bit 578 encoding = SmsMessage.ENCODING_7BIT; 579 break; 580 } 581 582 583 this.encoding = encoding; 584 this.language = language; 585 this.hasLanguageIndicator = hasLanguageIndicator; 586 } 587 } 588 } 589