blob: 752707e5a5dceeaab81329a251b6b20603ab62df [file] [log] [blame]
Alan Viverette3da604b2020-06-10 18:34:39 +00001/*
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 android.telephony;
18
19import android.annotation.IntDef;
20import android.annotation.NonNull;
21import android.annotation.Nullable;
22import android.annotation.SystemApi;
23import android.content.ContentValues;
24import android.database.Cursor;
25import android.os.Parcel;
26import android.os.Parcelable;
27import android.provider.Telephony.CellBroadcasts;
28import android.telephony.CbGeoUtils.Geometry;
29
30import java.lang.annotation.Retention;
31import java.lang.annotation.RetentionPolicy;
32import java.util.ArrayList;
33import java.util.List;
34
35/**
36 * Parcelable object containing a received cell broadcast message. There are four different types
37 * of Cell Broadcast messages:
38 *
39 * <ul>
40 * <li>opt-in informational broadcasts, e.g. news, weather, stock quotes, sports scores</li>
41 * <li>cell information messages, broadcast on channel 50, indicating the current cell name for
42 * roaming purposes (required to display on the idle screen in Brazil)</li>
43 * <li>emergency broadcasts for the Japanese Earthquake and Tsunami Warning System (ETWS)</li>
44 * <li>emergency broadcasts for the American Commercial Mobile Alert Service (CMAS)</li>
45 * </ul>
46 *
47 * <p>There are also four different CB message formats: GSM, ETWS Primary Notification (GSM only),
48 * UMTS, and CDMA. Some fields are only applicable for some message formats. Other fields were
49 * unified under a common name, avoiding some names, such as "Message Identifier", that refer to
50 * two completely different concepts in 3GPP and CDMA.
51 *
52 * <p>The GSM/UMTS Message Identifier field is available via {@link #getServiceCategory}, the name
53 * of the equivalent field in CDMA. In both cases the service category is a 16-bit value, but 3GPP
54 * and 3GPP2 have completely different meanings for the respective values. For ETWS and CMAS, the
55 * application should
56 *
57 * <p>The CDMA Message Identifier field is available via {@link #getSerialNumber}, which is used
58 * to detect the receipt of a duplicate message to be discarded. In CDMA, the message ID is
59 * unique to the current PLMN. In GSM/UMTS, there is a 16-bit serial number containing a 2-bit
60 * Geographical Scope field which indicates whether the 10-bit message code and 4-bit update number
61 * are considered unique to the PLMN, to the current cell, or to the current Location Area (or
62 * Service Area in UMTS). The relevant values are concatenated into a single String which will be
63 * unique if the messages are not duplicates.
64 *
65 * <p>The SMS dispatcher does not detect duplicate messages. However, it does concatenate the
66 * pages of a GSM multi-page cell broadcast into a single SmsCbMessage object.
67 *
68 * <p>Interested applications with {@code RECEIVE_SMS_PERMISSION} can register to receive
69 * {@code SMS_CB_RECEIVED_ACTION} broadcast intents for incoming non-emergency broadcasts.
70 * Only system applications such as the CellBroadcastReceiver may receive notifications for
71 * emergency broadcasts (ETWS and CMAS). This is intended to prevent any potential for delays or
72 * interference with the immediate display of the alert message and playing of the alert sound and
73 * vibration pattern, which could be caused by poorly written or malicious non-system code.
74 *
75 * @hide
76 */
77@SystemApi
78public final class SmsCbMessage implements Parcelable {
79
80 /** @hide */
81 public static final String LOG_TAG = "SMSCB";
82
83 /** Cell wide geographical scope with immediate display (GSM/UMTS only). */
84 public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE = 0;
85
86 /** PLMN wide geographical scope (GSM/UMTS and all CDMA broadcasts). */
87 public static final int GEOGRAPHICAL_SCOPE_PLMN_WIDE = 1;
88
89 /** Location / service area wide geographical scope (GSM/UMTS only). */
90 public static final int GEOGRAPHICAL_SCOPE_LOCATION_AREA_WIDE = 2;
91
92 /** Cell wide geographical scope (GSM/UMTS only). */
93 public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE = 3;
94
95 /** @hide */
96 @IntDef(prefix = { "GEOGRAPHICAL_SCOPE_" }, value = {
97 GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE,
98 GEOGRAPHICAL_SCOPE_PLMN_WIDE,
99 GEOGRAPHICAL_SCOPE_LOCATION_AREA_WIDE,
100 GEOGRAPHICAL_SCOPE_CELL_WIDE,
101 })
102 @Retention(RetentionPolicy.SOURCE)
103 public @interface GeographicalScope {}
104
105 /** GSM or UMTS format cell broadcast. */
106 public static final int MESSAGE_FORMAT_3GPP = 1;
107
108 /** CDMA format cell broadcast. */
109 public static final int MESSAGE_FORMAT_3GPP2 = 2;
110
111 /** @hide */
112 @IntDef(prefix = { "MESSAGE_FORMAT_" }, value = {
113 MESSAGE_FORMAT_3GPP,
114 MESSAGE_FORMAT_3GPP2
115 })
116 @Retention(RetentionPolicy.SOURCE)
117 public @interface MessageFormat {}
118
119 /** Normal message priority. */
120 public static final int MESSAGE_PRIORITY_NORMAL = 0;
121
122 /** Interactive message priority. */
123 public static final int MESSAGE_PRIORITY_INTERACTIVE = 1;
124
125 /** Urgent message priority. */
126 public static final int MESSAGE_PRIORITY_URGENT = 2;
127
128 /** Emergency message priority. */
129 public static final int MESSAGE_PRIORITY_EMERGENCY = 3;
130
131 /** @hide */
132 @IntDef(prefix = { "MESSAGE_PRIORITY_" }, value = {
133 MESSAGE_PRIORITY_NORMAL,
134 MESSAGE_PRIORITY_INTERACTIVE,
135 MESSAGE_PRIORITY_URGENT,
136 MESSAGE_PRIORITY_EMERGENCY,
137 })
138 @Retention(RetentionPolicy.SOURCE)
139 public @interface MessagePriority {}
140
141 /**
142 * Integer indicating that the maximum wait time is not set.
143 * Based on ATIS-0700041 Section 5.2.8 WAC Geo-Fencing Maximum Wait Time Table 12.
144 */
145 public static final int MAXIMUM_WAIT_TIME_NOT_SET = 255;
146
147 /** Format of this message (for interpretation of service category values). */
148 private final int mMessageFormat;
149
150 /** Geographical scope of broadcast. */
151 private final int mGeographicalScope;
152
153 /**
154 * Serial number of broadcast (message identifier for CDMA, geographical scope + message code +
155 * update number for GSM/UMTS). The serial number plus the location code uniquely identify
156 * a cell broadcast for duplicate detection.
157 */
158 private final int mSerialNumber;
159
160 /**
161 * Location identifier for this message. It consists of the current operator MCC/MNC as a
162 * 5 or 6-digit decimal string. In addition, for GSM/UMTS, if the Geographical Scope of the
163 * message is not binary 01, the Location Area is included for comparison. If the GS is
164 * 00 or 11, the Cell ID is also included. LAC and Cell ID are -1 if not specified.
165 */
166 @NonNull
167 private final SmsCbLocation mLocation;
168
169 /**
170 * 16-bit CDMA service category or GSM/UMTS message identifier. For ETWS and CMAS warnings,
171 * the information provided by the category is also available via {@link #getEtwsWarningInfo()}
172 * or {@link #getCmasWarningInfo()}.
173 */
174 private final int mServiceCategory;
175
176 /** Message language, as a two-character string, e.g. "en". */
177 @Nullable
178 private final String mLanguage;
179
180 /** The 8-bit data coding scheme defined in 3GPP TS 23.038 section 4. */
181 private final int mDataCodingScheme;
182
183 /** Message body, as a String. */
184 @Nullable
185 private final String mBody;
186
187 /** Message priority (including emergency priority). */
188 private final int mPriority;
189
190 /** ETWS warning notification information (ETWS warnings only). */
191 @Nullable
192 private final SmsCbEtwsInfo mEtwsWarningInfo;
193
194 /** CMAS warning notification information (CMAS warnings only). */
195 @Nullable
196 private final SmsCbCmasInfo mCmasWarningInfo;
197
198 /**
199 * Geo-Fencing Maximum Wait Time in second, a device shall allow to determine its position
200 * meeting operator policy. If the device is unable to determine its position meeting operator
201 * policy within the GeoFencing Maximum Wait Time, it shall present the alert to the user and
202 * discontinue further positioning determination for the alert.
203 */
204 private final int mMaximumWaitTimeSec;
205
206 /** UNIX timestamp of when the message was received. */
207 private final long mReceivedTimeMillis;
208
209 /** CMAS warning area coordinates. */
210 private final List<Geometry> mGeometries;
211
212 private final int mSlotIndex;
213
214 private final int mSubId;
215
216 /**
217 * Create a new SmsCbMessage with the specified data.
218 * @hide
219 */
220 public SmsCbMessage(int messageFormat, int geographicalScope, int serialNumber,
221 @NonNull SmsCbLocation location, int serviceCategory, @Nullable String language,
222 @Nullable String body, int priority, @Nullable SmsCbEtwsInfo etwsWarningInfo,
223 @Nullable SmsCbCmasInfo cmasWarningInfo, int slotIndex, int subId) {
224
225 this(messageFormat, geographicalScope, serialNumber, location, serviceCategory, language,
226 0, body, priority, etwsWarningInfo, cmasWarningInfo, 0 /* maximumWaitingTime */,
227 null /* geometries */, System.currentTimeMillis(), slotIndex, subId);
228 }
229
230 /**
231 * Create a new {@link SmsCbMessage} with the specified data, including warning area
232 * coordinates information.
233 */
234 public SmsCbMessage(int messageFormat, int geographicalScope, int serialNumber,
235 @NonNull SmsCbLocation location, int serviceCategory,
236 @Nullable String language, int dataCodingScheme, @Nullable String body,
237 int priority, @Nullable SmsCbEtwsInfo etwsWarningInfo,
238 @Nullable SmsCbCmasInfo cmasWarningInfo, int maximumWaitTimeSec,
239 @Nullable List<Geometry> geometries, long receivedTimeMillis, int slotIndex,
240 int subId) {
241 mMessageFormat = messageFormat;
242 mGeographicalScope = geographicalScope;
243 mSerialNumber = serialNumber;
244 mLocation = location;
245 mServiceCategory = serviceCategory;
246 mLanguage = language;
247 mDataCodingScheme = dataCodingScheme;
248 mBody = body;
249 mPriority = priority;
250 mEtwsWarningInfo = etwsWarningInfo;
251 mCmasWarningInfo = cmasWarningInfo;
252 mReceivedTimeMillis = receivedTimeMillis;
253 mGeometries = geometries;
254 mMaximumWaitTimeSec = maximumWaitTimeSec;
255 mSlotIndex = slotIndex;
256 mSubId = subId;
257 }
258
259 /**
260 * Create a new SmsCbMessage object from a Parcel.
261 * @hide
262 */
263 public SmsCbMessage(@NonNull Parcel in) {
264 mMessageFormat = in.readInt();
265 mGeographicalScope = in.readInt();
266 mSerialNumber = in.readInt();
267 mLocation = new SmsCbLocation(in);
268 mServiceCategory = in.readInt();
269 mLanguage = in.readString();
270 mDataCodingScheme = in.readInt();
271 mBody = in.readString();
272 mPriority = in.readInt();
273 int type = in.readInt();
274 switch (type) {
275 case 'E':
276 // unparcel ETWS warning information
277 mEtwsWarningInfo = new SmsCbEtwsInfo(in);
278 mCmasWarningInfo = null;
279 break;
280
281 case 'C':
282 // unparcel CMAS warning information
283 mEtwsWarningInfo = null;
284 mCmasWarningInfo = new SmsCbCmasInfo(in);
285 break;
286
287 default:
288 mEtwsWarningInfo = null;
289 mCmasWarningInfo = null;
290 }
291 mReceivedTimeMillis = in.readLong();
292 String geoStr = in.readString();
293 mGeometries = geoStr != null ? CbGeoUtils.parseGeometriesFromString(geoStr) : null;
294 mMaximumWaitTimeSec = in.readInt();
295 mSlotIndex = in.readInt();
296 mSubId = in.readInt();
297 }
298
299 /**
300 * Flatten this object into a Parcel.
301 *
302 * @param dest The Parcel in which the object should be written.
303 * @param flags Additional flags about how the object should be written (ignored).
304 */
305 @Override
306 public void writeToParcel(Parcel dest, int flags) {
307 dest.writeInt(mMessageFormat);
308 dest.writeInt(mGeographicalScope);
309 dest.writeInt(mSerialNumber);
310 mLocation.writeToParcel(dest, flags);
311 dest.writeInt(mServiceCategory);
312 dest.writeString(mLanguage);
313 dest.writeInt(mDataCodingScheme);
314 dest.writeString(mBody);
315 dest.writeInt(mPriority);
316 if (mEtwsWarningInfo != null) {
317 // parcel ETWS warning information
318 dest.writeInt('E');
319 mEtwsWarningInfo.writeToParcel(dest, flags);
320 } else if (mCmasWarningInfo != null) {
321 // parcel CMAS warning information
322 dest.writeInt('C');
323 mCmasWarningInfo.writeToParcel(dest, flags);
324 } else {
325 // no ETWS or CMAS warning information
326 dest.writeInt('0');
327 }
328 dest.writeLong(mReceivedTimeMillis);
329 dest.writeString(
330 mGeometries != null ? CbGeoUtils.encodeGeometriesToString(mGeometries) : null);
331 dest.writeInt(mMaximumWaitTimeSec);
332 dest.writeInt(mSlotIndex);
333 dest.writeInt(mSubId);
334 }
335
336 @NonNull
337 public static final Parcelable.Creator<SmsCbMessage> CREATOR =
338 new Parcelable.Creator<SmsCbMessage>() {
339 @Override
340 public SmsCbMessage createFromParcel(Parcel in) {
341 return new SmsCbMessage(in);
342 }
343
344 @Override
345 public SmsCbMessage[] newArray(int size) {
346 return new SmsCbMessage[size];
347 }
348 };
349
350 /**
351 * Return the geographical scope of this message (GSM/UMTS only).
352 *
353 * @return Geographical scope
354 */
355 public @GeographicalScope int getGeographicalScope() {
356 return mGeographicalScope;
357 }
358
359 /**
360 * Return the broadcast serial number of broadcast (message identifier for CDMA, or
361 * geographical scope + message code + update number for GSM/UMTS). The serial number plus
362 * the location code uniquely identify a cell broadcast for duplicate detection.
363 *
364 * @return the 16-bit CDMA message identifier or GSM/UMTS serial number
365 */
366 public int getSerialNumber() {
367 return mSerialNumber;
368 }
369
370 /**
371 * Return the location identifier for this message, consisting of the MCC/MNC as a
372 * 5 or 6-digit decimal string. In addition, for GSM/UMTS, if the Geographical Scope of the
373 * message is not binary 01, the Location Area is included. If the GS is 00 or 11, the
374 * cell ID is also included. The {@link SmsCbLocation} object includes a method to test
375 * if the location is included within another location area or within a PLMN and CellLocation.
376 *
377 * @return the geographical location code for duplicate message detection
378 */
379 @NonNull
380 public android.telephony.SmsCbLocation getLocation() {
381 return mLocation;
382 }
383
384 /**
385 * Return the 16-bit CDMA service category or GSM/UMTS message identifier. The interpretation
386 * of the category is radio technology specific. For ETWS and CMAS warnings, the information
387 * provided by the category is available via {@link #getEtwsWarningInfo()} or
388 * {@link #getCmasWarningInfo()} in a radio technology independent format.
389 *
390 * @return the radio technology specific service category
391 */
392 public int getServiceCategory() {
393 return mServiceCategory;
394 }
395
396 /**
397 * Get the ISO-639-1 language code for this message, or null if unspecified
398 *
399 * @return Language code
400 */
401 @Nullable
402 public String getLanguageCode() {
403 return mLanguage;
404 }
405
406 /**
407 * Get data coding scheme of the message
408 *
409 * @return The 8-bit data coding scheme defined in 3GPP TS 23.038 section 4.
410 */
411 public int getDataCodingScheme() {
412 return mDataCodingScheme;
413 }
414
415 /**
416 * Get the body of this message, or null if no body available
417 *
418 * @return Body, or null
419 */
420 @Nullable
421 public String getMessageBody() {
422 return mBody;
423 }
424
425 /**
426 * Get the warning area coordinates information represented by polygons and circles.
427 * @return a list of geometries, or an empty list if there is no coordinate information
428 * associated with this message.
429 * @hide
430 */
431 @SystemApi
432 @NonNull
433 public List<Geometry> getGeometries() {
434 if (mGeometries == null) {
435 return new ArrayList<>();
436 }
437 return mGeometries;
438 }
439
440 /**
441 * Get the Geo-Fencing Maximum Wait Time.
442 * @return the time in second.
443 */
444 public int getMaximumWaitingDuration() {
445 return mMaximumWaitTimeSec;
446 }
447
448 /**
449 * Get the time when this message was received.
450 * @return the time in millisecond
451 */
452 public long getReceivedTime() {
453 return mReceivedTimeMillis;
454 }
455
456 /**
457 * Get the slot index associated with this message.
458 * @return the slot index associated with this message
459 */
460 public int getSlotIndex() {
461 return mSlotIndex;
462 }
463
464 /**
465 * Get the subscription id associated with this message.
466 * @return the subscription id associated with this message
467 */
468 public int getSubscriptionId() {
469 return mSubId;
470 }
471
472 /**
473 * Get the message format ({@link #MESSAGE_FORMAT_3GPP} or {@link #MESSAGE_FORMAT_3GPP2}).
474 * @return an integer representing 3GPP or 3GPP2 message format
475 */
476 public @MessageFormat int getMessageFormat() {
477 return mMessageFormat;
478 }
479
480 /**
481 * Get the message priority. Normal broadcasts return {@link #MESSAGE_PRIORITY_NORMAL}
482 * and emergency broadcasts return {@link #MESSAGE_PRIORITY_EMERGENCY}. CDMA also may return
483 * {@link #MESSAGE_PRIORITY_INTERACTIVE} or {@link #MESSAGE_PRIORITY_URGENT}.
484 * @return an integer representing the message priority
485 */
486 public @MessagePriority int getMessagePriority() {
487 return mPriority;
488 }
489
490 /**
491 * If this is an ETWS warning notification then this method will return an object containing
492 * the ETWS warning type, the emergency user alert flag, and the popup flag. If this is an
493 * ETWS primary notification (GSM only), there will also be a 7-byte timestamp and 43-byte
494 * digital signature. As of Release 10, 3GPP TS 23.041 states that the UE shall ignore the
495 * ETWS primary notification timestamp and digital signature if received.
496 *
497 * @return an SmsCbEtwsInfo object, or null if this is not an ETWS warning notification
498 */
499 @Nullable
500 public SmsCbEtwsInfo getEtwsWarningInfo() {
501 return mEtwsWarningInfo;
502 }
503
504 /**
505 * If this is a CMAS warning notification then this method will return an object containing
506 * the CMAS message class, category, response type, severity, urgency and certainty.
507 * The message class is always present. Severity, urgency and certainty are present for CDMA
508 * warning notifications containing a type 1 elements record and for GSM and UMTS warnings
509 * except for the Presidential-level alert category. Category and response type are only
510 * available for CDMA notifications containing a type 1 elements record.
511 *
512 * @return an SmsCbCmasInfo object, or null if this is not a CMAS warning notification
513 */
514 @Nullable
515 public SmsCbCmasInfo getCmasWarningInfo() {
516 return mCmasWarningInfo;
517 }
518
519 /**
520 * Return whether this message is an emergency (PWS) message type.
521 * @return true if the message is an emergency notification; false otherwise
522 */
523 public boolean isEmergencyMessage() {
524 return mPriority == MESSAGE_PRIORITY_EMERGENCY;
525 }
526
527 /**
528 * Return whether this message is an ETWS warning alert.
529 * @return true if the message is an ETWS warning notification; false otherwise
530 */
531 public boolean isEtwsMessage() {
532 return mEtwsWarningInfo != null;
533 }
534
535 /**
536 * Return whether this message is a CMAS warning alert.
537 * @return true if the message is a CMAS warning notification; false otherwise
538 */
539 public boolean isCmasMessage() {
540 return mCmasWarningInfo != null;
541 }
542
543 @Override
544 public String toString() {
545 return "SmsCbMessage{geographicalScope=" + mGeographicalScope + ", serialNumber="
546 + mSerialNumber + ", location=" + mLocation + ", serviceCategory="
547 + mServiceCategory + ", language=" + mLanguage + ", body=" + mBody
548 + ", priority=" + mPriority
549 + (mEtwsWarningInfo != null ? (", " + mEtwsWarningInfo.toString()) : "")
550 + (mCmasWarningInfo != null ? (", " + mCmasWarningInfo.toString()) : "")
551 + ", maximumWaitingTime=" + mMaximumWaitTimeSec
552 + ", received time=" + mReceivedTimeMillis
553 + ", slotIndex = " + mSlotIndex
554 + ", geo=" + (mGeometries != null
555 ? CbGeoUtils.encodeGeometriesToString(mGeometries) : "null")
556 + '}';
557 }
558
559 /**
560 * Describe the kinds of special objects contained in the marshalled representation.
561 * @return a bitmask indicating this Parcelable contains no special objects
562 */
563 @Override
564 public int describeContents() {
565 return 0;
566 }
567
568 /**
569 * @return the {@link ContentValues} instance that includes the cell broadcast data.
570 */
571 @NonNull
572 public ContentValues getContentValues() {
573 ContentValues cv = new ContentValues(16);
574 cv.put(CellBroadcasts.SLOT_INDEX, mSlotIndex);
575 cv.put(CellBroadcasts.SUBSCRIPTION_ID, mSubId);
576 cv.put(CellBroadcasts.GEOGRAPHICAL_SCOPE, mGeographicalScope);
577 if (mLocation.getPlmn() != null) {
578 cv.put(CellBroadcasts.PLMN, mLocation.getPlmn());
579 }
580 if (mLocation.getLac() != -1) {
581 cv.put(CellBroadcasts.LAC, mLocation.getLac());
582 }
583 if (mLocation.getCid() != -1) {
584 cv.put(CellBroadcasts.CID, mLocation.getCid());
585 }
586 cv.put(CellBroadcasts.SERIAL_NUMBER, getSerialNumber());
587 cv.put(CellBroadcasts.SERVICE_CATEGORY, getServiceCategory());
588 cv.put(CellBroadcasts.LANGUAGE_CODE, getLanguageCode());
589 cv.put(CellBroadcasts.DATA_CODING_SCHEME, getDataCodingScheme());
590 cv.put(CellBroadcasts.MESSAGE_BODY, getMessageBody());
591 cv.put(CellBroadcasts.MESSAGE_FORMAT, getMessageFormat());
592 cv.put(CellBroadcasts.MESSAGE_PRIORITY, getMessagePriority());
593
594 SmsCbEtwsInfo etwsInfo = getEtwsWarningInfo();
595 if (etwsInfo != null) {
596 cv.put(CellBroadcasts.ETWS_WARNING_TYPE, etwsInfo.getWarningType());
597 }
598
599 SmsCbCmasInfo cmasInfo = getCmasWarningInfo();
600 if (cmasInfo != null) {
601 cv.put(CellBroadcasts.CMAS_MESSAGE_CLASS, cmasInfo.getMessageClass());
602 cv.put(CellBroadcasts.CMAS_CATEGORY, cmasInfo.getCategory());
603 cv.put(CellBroadcasts.CMAS_RESPONSE_TYPE, cmasInfo.getResponseType());
604 cv.put(CellBroadcasts.CMAS_SEVERITY, cmasInfo.getSeverity());
605 cv.put(CellBroadcasts.CMAS_URGENCY, cmasInfo.getUrgency());
606 cv.put(CellBroadcasts.CMAS_CERTAINTY, cmasInfo.getCertainty());
607 }
608
609 cv.put(CellBroadcasts.RECEIVED_TIME, mReceivedTimeMillis);
610
611 if (mGeometries != null) {
612 cv.put(CellBroadcasts.GEOMETRIES, CbGeoUtils.encodeGeometriesToString(mGeometries));
613 } else {
614 cv.put(CellBroadcasts.GEOMETRIES, (String) null);
615 }
616
617 cv.put(CellBroadcasts.MAXIMUM_WAIT_TIME, mMaximumWaitTimeSec);
618
619 return cv;
620 }
621
622 /**
623 * Create a {@link SmsCbMessage} instance from a row in the cell broadcast database.
624 * @param cursor an open SQLite cursor pointing to the row to read
625 * @return a {@link SmsCbMessage} instance.
626 * @throws IllegalArgumentException if one of the required columns is missing
627 */
628 @NonNull
629 public static SmsCbMessage createFromCursor(@NonNull Cursor cursor) {
630 int geoScope = cursor.getInt(
631 cursor.getColumnIndexOrThrow(CellBroadcasts.GEOGRAPHICAL_SCOPE));
632 int serialNum = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SERIAL_NUMBER));
633 int category = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SERVICE_CATEGORY));
634 String language = cursor.getString(
635 cursor.getColumnIndexOrThrow(CellBroadcasts.LANGUAGE_CODE));
636 String body = cursor.getString(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_BODY));
637 int format = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_FORMAT));
638 int priority = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_PRIORITY));
639 int slotIndex = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SLOT_INDEX));
640 int subId = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SUBSCRIPTION_ID));
641
642 String plmn;
643 int plmnColumn = cursor.getColumnIndex(CellBroadcasts.PLMN);
644 if (plmnColumn != -1 && !cursor.isNull(plmnColumn)) {
645 plmn = cursor.getString(plmnColumn);
646 } else {
647 plmn = null;
648 }
649
650 int lac;
651 int lacColumn = cursor.getColumnIndex(CellBroadcasts.LAC);
652 if (lacColumn != -1 && !cursor.isNull(lacColumn)) {
653 lac = cursor.getInt(lacColumn);
654 } else {
655 lac = -1;
656 }
657
658 int cid;
659 int cidColumn = cursor.getColumnIndex(CellBroadcasts.CID);
660 if (cidColumn != -1 && !cursor.isNull(cidColumn)) {
661 cid = cursor.getInt(cidColumn);
662 } else {
663 cid = -1;
664 }
665
666 SmsCbLocation location = new SmsCbLocation(plmn, lac, cid);
667
668 SmsCbEtwsInfo etwsInfo;
669 int etwsWarningTypeColumn = cursor.getColumnIndex(CellBroadcasts.ETWS_WARNING_TYPE);
670 if (etwsWarningTypeColumn != -1 && !cursor.isNull(etwsWarningTypeColumn)) {
671 int warningType = cursor.getInt(etwsWarningTypeColumn);
672 etwsInfo = new SmsCbEtwsInfo(warningType, false, false, false, null);
673 } else {
674 etwsInfo = null;
675 }
676
677 SmsCbCmasInfo cmasInfo = null;
678 int cmasMessageClassColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_MESSAGE_CLASS);
679 if (cmasMessageClassColumn != -1 && !cursor.isNull(cmasMessageClassColumn)) {
680 int messageClass = cursor.getInt(cmasMessageClassColumn);
681
682 int cmasCategory;
683 int cmasCategoryColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_CATEGORY);
684 if (cmasCategoryColumn != -1 && !cursor.isNull(cmasCategoryColumn)) {
685 cmasCategory = cursor.getInt(cmasCategoryColumn);
686 } else {
687 cmasCategory = SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN;
688 }
689
690 int responseType;
691 int cmasResponseTypeColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_RESPONSE_TYPE);
692 if (cmasResponseTypeColumn != -1 && !cursor.isNull(cmasResponseTypeColumn)) {
693 responseType = cursor.getInt(cmasResponseTypeColumn);
694 } else {
695 responseType = SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN;
696 }
697
698 int severity;
699 int cmasSeverityColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_SEVERITY);
700 if (cmasSeverityColumn != -1 && !cursor.isNull(cmasSeverityColumn)) {
701 severity = cursor.getInt(cmasSeverityColumn);
702 } else {
703 severity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN;
704 }
705
706 int urgency;
707 int cmasUrgencyColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_URGENCY);
708 if (cmasUrgencyColumn != -1 && !cursor.isNull(cmasUrgencyColumn)) {
709 urgency = cursor.getInt(cmasUrgencyColumn);
710 } else {
711 urgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN;
712 }
713
714 int certainty;
715 int cmasCertaintyColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_CERTAINTY);
716 if (cmasCertaintyColumn != -1 && !cursor.isNull(cmasCertaintyColumn)) {
717 certainty = cursor.getInt(cmasCertaintyColumn);
718 } else {
719 certainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN;
720 }
721
722 cmasInfo = new SmsCbCmasInfo(messageClass, cmasCategory, responseType, severity,
723 urgency, certainty);
724 }
725
726 String geoStr = cursor.getString(cursor.getColumnIndex(CellBroadcasts.GEOMETRIES));
727 List<Geometry> geometries =
728 geoStr != null ? CbGeoUtils.parseGeometriesFromString(geoStr) : null;
729
730 long receivedTimeMillis = cursor.getLong(
731 cursor.getColumnIndexOrThrow(CellBroadcasts.RECEIVED_TIME));
732
733 int maximumWaitTimeSec = cursor.getInt(
734 cursor.getColumnIndexOrThrow(CellBroadcasts.MAXIMUM_WAIT_TIME));
735
736 return new SmsCbMessage(format, geoScope, serialNum, location, category,
737 language, 0, body, priority, etwsInfo, cmasInfo, maximumWaitTimeSec, geometries,
738 receivedTimeMillis, slotIndex, subId);
739 }
740
741 /**
742 * @return {@code True} if this message needs geo-fencing check.
743 */
744 public boolean needGeoFencingCheck() {
745 return mMaximumWaitTimeSec > 0 && mGeometries != null && !mGeometries.isEmpty();
746 }
747}