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 */ SmsCbMessage(int messageFormat, int geographicalScope, int serialNumber, @NonNull SmsCbLocation location, int serviceCategory, @Nullable String language, @Nullable String body, int priority, @Nullable SmsCbEtwsInfo etwsWarningInfo, @Nullable SmsCbCmasInfo cmasWarningInfo, int slotIndex, int subId)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 */ SmsCbMessage(int messageFormat, int geographicalScope, int serialNumber, @NonNull SmsCbLocation location, int serviceCategory, @Nullable String language, int dataCodingScheme, @Nullable String body, int priority, @Nullable SmsCbEtwsInfo etwsWarningInfo, @Nullable SmsCbCmasInfo cmasWarningInfo, int maximumWaitTimeSec, @Nullable List<Geometry> geometries, long receivedTimeMillis, int slotIndex, int subId)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 */ SmsCbMessage(@onNull Parcel in)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 writeToParcel(Parcel dest, int flags)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 */ getGeographicalScope()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 */ getSerialNumber()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 getLocation()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 */ getServiceCategory()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 getLanguageCode()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 */ getDataCodingScheme()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 getMessageBody()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 getGeometries()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 */ getMaximumWaitingDuration()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 */ getReceivedTime()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 */ getSlotIndex()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 */ getSubscriptionId()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 */ getMessageFormat()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 */ getMessagePriority()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 getEtwsWarningInfo()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 getCmasWarningInfo()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 */ isEmergencyMessage()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 */ isEtwsMessage()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 */ isCmasMessage()539 public boolean isCmasMessage() { 540 return mCmasWarningInfo != null; 541 } 542 543 @Override toString()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 describeContents()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 getContentValues()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 cv.put(CellBroadcasts.ETWS_IS_PRIMARY, etwsInfo.isPrimary()); 598 } 599 600 SmsCbCmasInfo cmasInfo = getCmasWarningInfo(); 601 if (cmasInfo != null) { 602 cv.put(CellBroadcasts.CMAS_MESSAGE_CLASS, cmasInfo.getMessageClass()); 603 cv.put(CellBroadcasts.CMAS_CATEGORY, cmasInfo.getCategory()); 604 cv.put(CellBroadcasts.CMAS_RESPONSE_TYPE, cmasInfo.getResponseType()); 605 cv.put(CellBroadcasts.CMAS_SEVERITY, cmasInfo.getSeverity()); 606 cv.put(CellBroadcasts.CMAS_URGENCY, cmasInfo.getUrgency()); 607 cv.put(CellBroadcasts.CMAS_CERTAINTY, cmasInfo.getCertainty()); 608 } 609 610 cv.put(CellBroadcasts.RECEIVED_TIME, mReceivedTimeMillis); 611 612 if (mGeometries != null) { 613 cv.put(CellBroadcasts.GEOMETRIES, CbGeoUtils.encodeGeometriesToString(mGeometries)); 614 } else { 615 cv.put(CellBroadcasts.GEOMETRIES, (String) null); 616 } 617 618 cv.put(CellBroadcasts.MAXIMUM_WAIT_TIME, mMaximumWaitTimeSec); 619 620 return cv; 621 } 622 623 /** 624 * Create a {@link SmsCbMessage} instance from a row in the cell broadcast database. 625 * @param cursor an open SQLite cursor pointing to the row to read 626 * @return a {@link SmsCbMessage} instance. 627 * @throws IllegalArgumentException if one of the required columns is missing 628 */ 629 @NonNull createFromCursor(@onNull Cursor cursor)630 public static SmsCbMessage createFromCursor(@NonNull Cursor cursor) { 631 int geoScope = cursor.getInt( 632 cursor.getColumnIndexOrThrow(CellBroadcasts.GEOGRAPHICAL_SCOPE)); 633 int serialNum = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SERIAL_NUMBER)); 634 int category = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SERVICE_CATEGORY)); 635 String language = cursor.getString( 636 cursor.getColumnIndexOrThrow(CellBroadcasts.LANGUAGE_CODE)); 637 String body = cursor.getString(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_BODY)); 638 int format = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_FORMAT)); 639 int priority = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_PRIORITY)); 640 int slotIndex = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SLOT_INDEX)); 641 int subId = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SUBSCRIPTION_ID)); 642 643 String plmn; 644 int plmnColumn = cursor.getColumnIndex(CellBroadcasts.PLMN); 645 if (plmnColumn != -1 && !cursor.isNull(plmnColumn)) { 646 plmn = cursor.getString(plmnColumn); 647 } else { 648 plmn = null; 649 } 650 651 int lac; 652 int lacColumn = cursor.getColumnIndex(CellBroadcasts.LAC); 653 if (lacColumn != -1 && !cursor.isNull(lacColumn)) { 654 lac = cursor.getInt(lacColumn); 655 } else { 656 lac = -1; 657 } 658 659 int cid; 660 int cidColumn = cursor.getColumnIndex(CellBroadcasts.CID); 661 if (cidColumn != -1 && !cursor.isNull(cidColumn)) { 662 cid = cursor.getInt(cidColumn); 663 } else { 664 cid = -1; 665 } 666 667 SmsCbLocation location = new SmsCbLocation(plmn, lac, cid); 668 669 SmsCbEtwsInfo etwsInfo; 670 int etwsWarningTypeColumn = cursor.getColumnIndex(CellBroadcasts.ETWS_WARNING_TYPE); 671 int etwsIsPrimaryColumn = cursor.getColumnIndex(CellBroadcasts.ETWS_IS_PRIMARY); 672 if (etwsWarningTypeColumn != -1 && !cursor.isNull(etwsWarningTypeColumn) 673 && etwsIsPrimaryColumn != -1 && !cursor.isNull(etwsIsPrimaryColumn)) { 674 int warningType = cursor.getInt(etwsWarningTypeColumn); 675 boolean isPrimary = cursor.getInt(etwsIsPrimaryColumn) != 0; 676 etwsInfo = new SmsCbEtwsInfo(warningType, false, false, isPrimary, null); 677 } else { 678 etwsInfo = null; 679 } 680 681 SmsCbCmasInfo cmasInfo = null; 682 int cmasMessageClassColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_MESSAGE_CLASS); 683 if (cmasMessageClassColumn != -1 && !cursor.isNull(cmasMessageClassColumn)) { 684 int messageClass = cursor.getInt(cmasMessageClassColumn); 685 686 int cmasCategory; 687 int cmasCategoryColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_CATEGORY); 688 if (cmasCategoryColumn != -1 && !cursor.isNull(cmasCategoryColumn)) { 689 cmasCategory = cursor.getInt(cmasCategoryColumn); 690 } else { 691 cmasCategory = SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN; 692 } 693 694 int responseType; 695 int cmasResponseTypeColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_RESPONSE_TYPE); 696 if (cmasResponseTypeColumn != -1 && !cursor.isNull(cmasResponseTypeColumn)) { 697 responseType = cursor.getInt(cmasResponseTypeColumn); 698 } else { 699 responseType = SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN; 700 } 701 702 int severity; 703 int cmasSeverityColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_SEVERITY); 704 if (cmasSeverityColumn != -1 && !cursor.isNull(cmasSeverityColumn)) { 705 severity = cursor.getInt(cmasSeverityColumn); 706 } else { 707 severity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN; 708 } 709 710 int urgency; 711 int cmasUrgencyColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_URGENCY); 712 if (cmasUrgencyColumn != -1 && !cursor.isNull(cmasUrgencyColumn)) { 713 urgency = cursor.getInt(cmasUrgencyColumn); 714 } else { 715 urgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN; 716 } 717 718 int certainty; 719 int cmasCertaintyColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_CERTAINTY); 720 if (cmasCertaintyColumn != -1 && !cursor.isNull(cmasCertaintyColumn)) { 721 certainty = cursor.getInt(cmasCertaintyColumn); 722 } else { 723 certainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN; 724 } 725 726 cmasInfo = new SmsCbCmasInfo(messageClass, cmasCategory, responseType, severity, 727 urgency, certainty); 728 } 729 730 String geoStr = cursor.getString(cursor.getColumnIndex(CellBroadcasts.GEOMETRIES)); 731 List<Geometry> geometries = 732 geoStr != null ? CbGeoUtils.parseGeometriesFromString(geoStr) : null; 733 734 long receivedTimeMillis = cursor.getLong( 735 cursor.getColumnIndexOrThrow(CellBroadcasts.RECEIVED_TIME)); 736 737 int maximumWaitTimeSec = cursor.getInt( 738 cursor.getColumnIndexOrThrow(CellBroadcasts.MAXIMUM_WAIT_TIME)); 739 740 return new SmsCbMessage(format, geoScope, serialNum, location, category, 741 language, 0, body, priority, etwsInfo, cmasInfo, maximumWaitTimeSec, geometries, 742 receivedTimeMillis, slotIndex, subId); 743 } 744 745 /** 746 * @return {@code True} if this message needs geo-fencing check. 747 */ needGeoFencingCheck()748 public boolean needGeoFencingCheck() { 749 return mMaximumWaitTimeSec > 0 && mGeometries != null && !mGeometries.isEmpty(); 750 } 751 } 752