Alan Viverette | 3da604b | 2020-06-10 18:34:39 +0000 | [diff] [blame^] | 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 android.telephony; |
| 18 | |
| 19 | import android.annotation.IntDef; |
| 20 | import android.annotation.NonNull; |
| 21 | import android.annotation.Nullable; |
| 22 | import android.annotation.SystemApi; |
| 23 | import android.content.ContentValues; |
| 24 | import android.database.Cursor; |
| 25 | import android.os.Parcel; |
| 26 | import android.os.Parcelable; |
| 27 | import android.provider.Telephony.CellBroadcasts; |
| 28 | import android.telephony.CbGeoUtils.Geometry; |
| 29 | |
| 30 | import java.lang.annotation.Retention; |
| 31 | import java.lang.annotation.RetentionPolicy; |
| 32 | import java.util.ArrayList; |
| 33 | import 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 |
| 78 | public 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 | } |