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