Automatic sources dropoff on 2020-06-10 18:32:38.095721 The change is generated with prebuilt drop tool. Change-Id: I24cbf6ba6db262a1ae1445db1427a08fee35b3b4
diff --git a/android/telephony/SmsCbMessage.java b/android/telephony/SmsCbMessage.java new file mode 100644 index 0000000..752707e --- /dev/null +++ b/android/telephony/SmsCbMessage.java
@@ -0,0 +1,747 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.content.ContentValues; +import android.database.Cursor; +import android.os.Parcel; +import android.os.Parcelable; +import android.provider.Telephony.CellBroadcasts; +import android.telephony.CbGeoUtils.Geometry; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; + +/** + * Parcelable object containing a received cell broadcast message. There are four different types + * of Cell Broadcast messages: + * + * <ul> + * <li>opt-in informational broadcasts, e.g. news, weather, stock quotes, sports scores</li> + * <li>cell information messages, broadcast on channel 50, indicating the current cell name for + * roaming purposes (required to display on the idle screen in Brazil)</li> + * <li>emergency broadcasts for the Japanese Earthquake and Tsunami Warning System (ETWS)</li> + * <li>emergency broadcasts for the American Commercial Mobile Alert Service (CMAS)</li> + * </ul> + * + * <p>There are also four different CB message formats: GSM, ETWS Primary Notification (GSM only), + * UMTS, and CDMA. Some fields are only applicable for some message formats. Other fields were + * unified under a common name, avoiding some names, such as "Message Identifier", that refer to + * two completely different concepts in 3GPP and CDMA. + * + * <p>The GSM/UMTS Message Identifier field is available via {@link #getServiceCategory}, the name + * of the equivalent field in CDMA. In both cases the service category is a 16-bit value, but 3GPP + * and 3GPP2 have completely different meanings for the respective values. For ETWS and CMAS, the + * application should + * + * <p>The CDMA Message Identifier field is available via {@link #getSerialNumber}, which is used + * to detect the receipt of a duplicate message to be discarded. In CDMA, the message ID is + * unique to the current PLMN. In GSM/UMTS, there is a 16-bit serial number containing a 2-bit + * Geographical Scope field which indicates whether the 10-bit message code and 4-bit update number + * are considered unique to the PLMN, to the current cell, or to the current Location Area (or + * Service Area in UMTS). The relevant values are concatenated into a single String which will be + * unique if the messages are not duplicates. + * + * <p>The SMS dispatcher does not detect duplicate messages. However, it does concatenate the + * pages of a GSM multi-page cell broadcast into a single SmsCbMessage object. + * + * <p>Interested applications with {@code RECEIVE_SMS_PERMISSION} can register to receive + * {@code SMS_CB_RECEIVED_ACTION} broadcast intents for incoming non-emergency broadcasts. + * Only system applications such as the CellBroadcastReceiver may receive notifications for + * emergency broadcasts (ETWS and CMAS). This is intended to prevent any potential for delays or + * interference with the immediate display of the alert message and playing of the alert sound and + * vibration pattern, which could be caused by poorly written or malicious non-system code. + * + * @hide + */ +@SystemApi +public final class SmsCbMessage implements Parcelable { + + /** @hide */ + public static final String LOG_TAG = "SMSCB"; + + /** Cell wide geographical scope with immediate display (GSM/UMTS only). */ + public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE = 0; + + /** PLMN wide geographical scope (GSM/UMTS and all CDMA broadcasts). */ + public static final int GEOGRAPHICAL_SCOPE_PLMN_WIDE = 1; + + /** Location / service area wide geographical scope (GSM/UMTS only). */ + public static final int GEOGRAPHICAL_SCOPE_LOCATION_AREA_WIDE = 2; + + /** Cell wide geographical scope (GSM/UMTS only). */ + public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE = 3; + + /** @hide */ + @IntDef(prefix = { "GEOGRAPHICAL_SCOPE_" }, value = { + GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE, + GEOGRAPHICAL_SCOPE_PLMN_WIDE, + GEOGRAPHICAL_SCOPE_LOCATION_AREA_WIDE, + GEOGRAPHICAL_SCOPE_CELL_WIDE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface GeographicalScope {} + + /** GSM or UMTS format cell broadcast. */ + public static final int MESSAGE_FORMAT_3GPP = 1; + + /** CDMA format cell broadcast. */ + public static final int MESSAGE_FORMAT_3GPP2 = 2; + + /** @hide */ + @IntDef(prefix = { "MESSAGE_FORMAT_" }, value = { + MESSAGE_FORMAT_3GPP, + MESSAGE_FORMAT_3GPP2 + }) + @Retention(RetentionPolicy.SOURCE) + public @interface MessageFormat {} + + /** Normal message priority. */ + public static final int MESSAGE_PRIORITY_NORMAL = 0; + + /** Interactive message priority. */ + public static final int MESSAGE_PRIORITY_INTERACTIVE = 1; + + /** Urgent message priority. */ + public static final int MESSAGE_PRIORITY_URGENT = 2; + + /** Emergency message priority. */ + public static final int MESSAGE_PRIORITY_EMERGENCY = 3; + + /** @hide */ + @IntDef(prefix = { "MESSAGE_PRIORITY_" }, value = { + MESSAGE_PRIORITY_NORMAL, + MESSAGE_PRIORITY_INTERACTIVE, + MESSAGE_PRIORITY_URGENT, + MESSAGE_PRIORITY_EMERGENCY, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface MessagePriority {} + + /** + * Integer indicating that the maximum wait time is not set. + * Based on ATIS-0700041 Section 5.2.8 WAC Geo-Fencing Maximum Wait Time Table 12. + */ + public static final int MAXIMUM_WAIT_TIME_NOT_SET = 255; + + /** Format of this message (for interpretation of service category values). */ + private final int mMessageFormat; + + /** Geographical scope of broadcast. */ + private final int mGeographicalScope; + + /** + * Serial number of broadcast (message identifier for CDMA, geographical scope + message code + + * update number for GSM/UMTS). The serial number plus the location code uniquely identify + * a cell broadcast for duplicate detection. + */ + private final int mSerialNumber; + + /** + * Location identifier for this message. It consists of the current operator MCC/MNC as a + * 5 or 6-digit decimal string. In addition, for GSM/UMTS, if the Geographical Scope of the + * message is not binary 01, the Location Area is included for comparison. If the GS is + * 00 or 11, the Cell ID is also included. LAC and Cell ID are -1 if not specified. + */ + @NonNull + private final SmsCbLocation mLocation; + + /** + * 16-bit CDMA service category or GSM/UMTS message identifier. For ETWS and CMAS warnings, + * the information provided by the category is also available via {@link #getEtwsWarningInfo()} + * or {@link #getCmasWarningInfo()}. + */ + private final int mServiceCategory; + + /** Message language, as a two-character string, e.g. "en". */ + @Nullable + private final String mLanguage; + + /** The 8-bit data coding scheme defined in 3GPP TS 23.038 section 4. */ + private final int mDataCodingScheme; + + /** Message body, as a String. */ + @Nullable + private final String mBody; + + /** Message priority (including emergency priority). */ + private final int mPriority; + + /** ETWS warning notification information (ETWS warnings only). */ + @Nullable + private final SmsCbEtwsInfo mEtwsWarningInfo; + + /** CMAS warning notification information (CMAS warnings only). */ + @Nullable + private final SmsCbCmasInfo mCmasWarningInfo; + + /** + * Geo-Fencing Maximum Wait Time in second, a device shall allow to determine its position + * meeting operator policy. If the device is unable to determine its position meeting operator + * policy within the GeoFencing Maximum Wait Time, it shall present the alert to the user and + * discontinue further positioning determination for the alert. + */ + private final int mMaximumWaitTimeSec; + + /** UNIX timestamp of when the message was received. */ + private final long mReceivedTimeMillis; + + /** CMAS warning area coordinates. */ + private final List<Geometry> mGeometries; + + private final int mSlotIndex; + + private final int mSubId; + + /** + * Create a new SmsCbMessage with the specified data. + * @hide + */ + public 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) { + + this(messageFormat, geographicalScope, serialNumber, location, serviceCategory, language, + 0, body, priority, etwsWarningInfo, cmasWarningInfo, 0 /* maximumWaitingTime */, + null /* geometries */, System.currentTimeMillis(), slotIndex, subId); + } + + /** + * Create a new {@link SmsCbMessage} with the specified data, including warning area + * coordinates information. + */ + public 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) { + mMessageFormat = messageFormat; + mGeographicalScope = geographicalScope; + mSerialNumber = serialNumber; + mLocation = location; + mServiceCategory = serviceCategory; + mLanguage = language; + mDataCodingScheme = dataCodingScheme; + mBody = body; + mPriority = priority; + mEtwsWarningInfo = etwsWarningInfo; + mCmasWarningInfo = cmasWarningInfo; + mReceivedTimeMillis = receivedTimeMillis; + mGeometries = geometries; + mMaximumWaitTimeSec = maximumWaitTimeSec; + mSlotIndex = slotIndex; + mSubId = subId; + } + + /** + * Create a new SmsCbMessage object from a Parcel. + * @hide + */ + public SmsCbMessage(@NonNull Parcel in) { + mMessageFormat = in.readInt(); + mGeographicalScope = in.readInt(); + mSerialNumber = in.readInt(); + mLocation = new SmsCbLocation(in); + mServiceCategory = in.readInt(); + mLanguage = in.readString(); + mDataCodingScheme = in.readInt(); + mBody = in.readString(); + mPriority = in.readInt(); + int type = in.readInt(); + switch (type) { + case 'E': + // unparcel ETWS warning information + mEtwsWarningInfo = new SmsCbEtwsInfo(in); + mCmasWarningInfo = null; + break; + + case 'C': + // unparcel CMAS warning information + mEtwsWarningInfo = null; + mCmasWarningInfo = new SmsCbCmasInfo(in); + break; + + default: + mEtwsWarningInfo = null; + mCmasWarningInfo = null; + } + mReceivedTimeMillis = in.readLong(); + String geoStr = in.readString(); + mGeometries = geoStr != null ? CbGeoUtils.parseGeometriesFromString(geoStr) : null; + mMaximumWaitTimeSec = in.readInt(); + mSlotIndex = in.readInt(); + mSubId = in.readInt(); + } + + /** + * Flatten this object into a Parcel. + * + * @param dest The Parcel in which the object should be written. + * @param flags Additional flags about how the object should be written (ignored). + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mMessageFormat); + dest.writeInt(mGeographicalScope); + dest.writeInt(mSerialNumber); + mLocation.writeToParcel(dest, flags); + dest.writeInt(mServiceCategory); + dest.writeString(mLanguage); + dest.writeInt(mDataCodingScheme); + dest.writeString(mBody); + dest.writeInt(mPriority); + if (mEtwsWarningInfo != null) { + // parcel ETWS warning information + dest.writeInt('E'); + mEtwsWarningInfo.writeToParcel(dest, flags); + } else if (mCmasWarningInfo != null) { + // parcel CMAS warning information + dest.writeInt('C'); + mCmasWarningInfo.writeToParcel(dest, flags); + } else { + // no ETWS or CMAS warning information + dest.writeInt('0'); + } + dest.writeLong(mReceivedTimeMillis); + dest.writeString( + mGeometries != null ? CbGeoUtils.encodeGeometriesToString(mGeometries) : null); + dest.writeInt(mMaximumWaitTimeSec); + dest.writeInt(mSlotIndex); + dest.writeInt(mSubId); + } + + @NonNull + public static final Parcelable.Creator<SmsCbMessage> CREATOR = + new Parcelable.Creator<SmsCbMessage>() { + @Override + public SmsCbMessage createFromParcel(Parcel in) { + return new SmsCbMessage(in); + } + + @Override + public SmsCbMessage[] newArray(int size) { + return new SmsCbMessage[size]; + } + }; + + /** + * Return the geographical scope of this message (GSM/UMTS only). + * + * @return Geographical scope + */ + public @GeographicalScope int getGeographicalScope() { + return mGeographicalScope; + } + + /** + * Return the broadcast serial number of broadcast (message identifier for CDMA, or + * geographical scope + message code + update number for GSM/UMTS). The serial number plus + * the location code uniquely identify a cell broadcast for duplicate detection. + * + * @return the 16-bit CDMA message identifier or GSM/UMTS serial number + */ + public int getSerialNumber() { + return mSerialNumber; + } + + /** + * Return the location identifier for this message, consisting of the MCC/MNC as a + * 5 or 6-digit decimal string. In addition, for GSM/UMTS, if the Geographical Scope of the + * message is not binary 01, the Location Area is included. If the GS is 00 or 11, the + * cell ID is also included. The {@link SmsCbLocation} object includes a method to test + * if the location is included within another location area or within a PLMN and CellLocation. + * + * @return the geographical location code for duplicate message detection + */ + @NonNull + public android.telephony.SmsCbLocation getLocation() { + return mLocation; + } + + /** + * Return the 16-bit CDMA service category or GSM/UMTS message identifier. The interpretation + * of the category is radio technology specific. For ETWS and CMAS warnings, the information + * provided by the category is available via {@link #getEtwsWarningInfo()} or + * {@link #getCmasWarningInfo()} in a radio technology independent format. + * + * @return the radio technology specific service category + */ + public int getServiceCategory() { + return mServiceCategory; + } + + /** + * Get the ISO-639-1 language code for this message, or null if unspecified + * + * @return Language code + */ + @Nullable + public String getLanguageCode() { + return mLanguage; + } + + /** + * Get data coding scheme of the message + * + * @return The 8-bit data coding scheme defined in 3GPP TS 23.038 section 4. + */ + public int getDataCodingScheme() { + return mDataCodingScheme; + } + + /** + * Get the body of this message, or null if no body available + * + * @return Body, or null + */ + @Nullable + public String getMessageBody() { + return mBody; + } + + /** + * Get the warning area coordinates information represented by polygons and circles. + * @return a list of geometries, or an empty list if there is no coordinate information + * associated with this message. + * @hide + */ + @SystemApi + @NonNull + public List<Geometry> getGeometries() { + if (mGeometries == null) { + return new ArrayList<>(); + } + return mGeometries; + } + + /** + * Get the Geo-Fencing Maximum Wait Time. + * @return the time in second. + */ + public int getMaximumWaitingDuration() { + return mMaximumWaitTimeSec; + } + + /** + * Get the time when this message was received. + * @return the time in millisecond + */ + public long getReceivedTime() { + return mReceivedTimeMillis; + } + + /** + * Get the slot index associated with this message. + * @return the slot index associated with this message + */ + public int getSlotIndex() { + return mSlotIndex; + } + + /** + * Get the subscription id associated with this message. + * @return the subscription id associated with this message + */ + public int getSubscriptionId() { + return mSubId; + } + + /** + * Get the message format ({@link #MESSAGE_FORMAT_3GPP} or {@link #MESSAGE_FORMAT_3GPP2}). + * @return an integer representing 3GPP or 3GPP2 message format + */ + public @MessageFormat int getMessageFormat() { + return mMessageFormat; + } + + /** + * Get the message priority. Normal broadcasts return {@link #MESSAGE_PRIORITY_NORMAL} + * and emergency broadcasts return {@link #MESSAGE_PRIORITY_EMERGENCY}. CDMA also may return + * {@link #MESSAGE_PRIORITY_INTERACTIVE} or {@link #MESSAGE_PRIORITY_URGENT}. + * @return an integer representing the message priority + */ + public @MessagePriority int getMessagePriority() { + return mPriority; + } + + /** + * If this is an ETWS warning notification then this method will return an object containing + * the ETWS warning type, the emergency user alert flag, and the popup flag. If this is an + * ETWS primary notification (GSM only), there will also be a 7-byte timestamp and 43-byte + * digital signature. As of Release 10, 3GPP TS 23.041 states that the UE shall ignore the + * ETWS primary notification timestamp and digital signature if received. + * + * @return an SmsCbEtwsInfo object, or null if this is not an ETWS warning notification + */ + @Nullable + public SmsCbEtwsInfo getEtwsWarningInfo() { + return mEtwsWarningInfo; + } + + /** + * If this is a CMAS warning notification then this method will return an object containing + * the CMAS message class, category, response type, severity, urgency and certainty. + * The message class is always present. Severity, urgency and certainty are present for CDMA + * warning notifications containing a type 1 elements record and for GSM and UMTS warnings + * except for the Presidential-level alert category. Category and response type are only + * available for CDMA notifications containing a type 1 elements record. + * + * @return an SmsCbCmasInfo object, or null if this is not a CMAS warning notification + */ + @Nullable + public SmsCbCmasInfo getCmasWarningInfo() { + return mCmasWarningInfo; + } + + /** + * Return whether this message is an emergency (PWS) message type. + * @return true if the message is an emergency notification; false otherwise + */ + public boolean isEmergencyMessage() { + return mPriority == MESSAGE_PRIORITY_EMERGENCY; + } + + /** + * Return whether this message is an ETWS warning alert. + * @return true if the message is an ETWS warning notification; false otherwise + */ + public boolean isEtwsMessage() { + return mEtwsWarningInfo != null; + } + + /** + * Return whether this message is a CMAS warning alert. + * @return true if the message is a CMAS warning notification; false otherwise + */ + public boolean isCmasMessage() { + return mCmasWarningInfo != null; + } + + @Override + public String toString() { + return "SmsCbMessage{geographicalScope=" + mGeographicalScope + ", serialNumber=" + + mSerialNumber + ", location=" + mLocation + ", serviceCategory=" + + mServiceCategory + ", language=" + mLanguage + ", body=" + mBody + + ", priority=" + mPriority + + (mEtwsWarningInfo != null ? (", " + mEtwsWarningInfo.toString()) : "") + + (mCmasWarningInfo != null ? (", " + mCmasWarningInfo.toString()) : "") + + ", maximumWaitingTime=" + mMaximumWaitTimeSec + + ", received time=" + mReceivedTimeMillis + + ", slotIndex = " + mSlotIndex + + ", geo=" + (mGeometries != null + ? CbGeoUtils.encodeGeometriesToString(mGeometries) : "null") + + '}'; + } + + /** + * Describe the kinds of special objects contained in the marshalled representation. + * @return a bitmask indicating this Parcelable contains no special objects + */ + @Override + public int describeContents() { + return 0; + } + + /** + * @return the {@link ContentValues} instance that includes the cell broadcast data. + */ + @NonNull + public ContentValues getContentValues() { + ContentValues cv = new ContentValues(16); + cv.put(CellBroadcasts.SLOT_INDEX, mSlotIndex); + cv.put(CellBroadcasts.SUBSCRIPTION_ID, mSubId); + cv.put(CellBroadcasts.GEOGRAPHICAL_SCOPE, mGeographicalScope); + if (mLocation.getPlmn() != null) { + cv.put(CellBroadcasts.PLMN, mLocation.getPlmn()); + } + if (mLocation.getLac() != -1) { + cv.put(CellBroadcasts.LAC, mLocation.getLac()); + } + if (mLocation.getCid() != -1) { + cv.put(CellBroadcasts.CID, mLocation.getCid()); + } + cv.put(CellBroadcasts.SERIAL_NUMBER, getSerialNumber()); + cv.put(CellBroadcasts.SERVICE_CATEGORY, getServiceCategory()); + cv.put(CellBroadcasts.LANGUAGE_CODE, getLanguageCode()); + cv.put(CellBroadcasts.DATA_CODING_SCHEME, getDataCodingScheme()); + cv.put(CellBroadcasts.MESSAGE_BODY, getMessageBody()); + cv.put(CellBroadcasts.MESSAGE_FORMAT, getMessageFormat()); + cv.put(CellBroadcasts.MESSAGE_PRIORITY, getMessagePriority()); + + SmsCbEtwsInfo etwsInfo = getEtwsWarningInfo(); + if (etwsInfo != null) { + cv.put(CellBroadcasts.ETWS_WARNING_TYPE, etwsInfo.getWarningType()); + } + + SmsCbCmasInfo cmasInfo = getCmasWarningInfo(); + if (cmasInfo != null) { + cv.put(CellBroadcasts.CMAS_MESSAGE_CLASS, cmasInfo.getMessageClass()); + cv.put(CellBroadcasts.CMAS_CATEGORY, cmasInfo.getCategory()); + cv.put(CellBroadcasts.CMAS_RESPONSE_TYPE, cmasInfo.getResponseType()); + cv.put(CellBroadcasts.CMAS_SEVERITY, cmasInfo.getSeverity()); + cv.put(CellBroadcasts.CMAS_URGENCY, cmasInfo.getUrgency()); + cv.put(CellBroadcasts.CMAS_CERTAINTY, cmasInfo.getCertainty()); + } + + cv.put(CellBroadcasts.RECEIVED_TIME, mReceivedTimeMillis); + + if (mGeometries != null) { + cv.put(CellBroadcasts.GEOMETRIES, CbGeoUtils.encodeGeometriesToString(mGeometries)); + } else { + cv.put(CellBroadcasts.GEOMETRIES, (String) null); + } + + cv.put(CellBroadcasts.MAXIMUM_WAIT_TIME, mMaximumWaitTimeSec); + + return cv; + } + + /** + * Create a {@link SmsCbMessage} instance from a row in the cell broadcast database. + * @param cursor an open SQLite cursor pointing to the row to read + * @return a {@link SmsCbMessage} instance. + * @throws IllegalArgumentException if one of the required columns is missing + */ + @NonNull + public static SmsCbMessage createFromCursor(@NonNull Cursor cursor) { + int geoScope = cursor.getInt( + cursor.getColumnIndexOrThrow(CellBroadcasts.GEOGRAPHICAL_SCOPE)); + int serialNum = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SERIAL_NUMBER)); + int category = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SERVICE_CATEGORY)); + String language = cursor.getString( + cursor.getColumnIndexOrThrow(CellBroadcasts.LANGUAGE_CODE)); + String body = cursor.getString(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_BODY)); + int format = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_FORMAT)); + int priority = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_PRIORITY)); + int slotIndex = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SLOT_INDEX)); + int subId = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SUBSCRIPTION_ID)); + + String plmn; + int plmnColumn = cursor.getColumnIndex(CellBroadcasts.PLMN); + if (plmnColumn != -1 && !cursor.isNull(plmnColumn)) { + plmn = cursor.getString(plmnColumn); + } else { + plmn = null; + } + + int lac; + int lacColumn = cursor.getColumnIndex(CellBroadcasts.LAC); + if (lacColumn != -1 && !cursor.isNull(lacColumn)) { + lac = cursor.getInt(lacColumn); + } else { + lac = -1; + } + + int cid; + int cidColumn = cursor.getColumnIndex(CellBroadcasts.CID); + if (cidColumn != -1 && !cursor.isNull(cidColumn)) { + cid = cursor.getInt(cidColumn); + } else { + cid = -1; + } + + SmsCbLocation location = new SmsCbLocation(plmn, lac, cid); + + SmsCbEtwsInfo etwsInfo; + int etwsWarningTypeColumn = cursor.getColumnIndex(CellBroadcasts.ETWS_WARNING_TYPE); + if (etwsWarningTypeColumn != -1 && !cursor.isNull(etwsWarningTypeColumn)) { + int warningType = cursor.getInt(etwsWarningTypeColumn); + etwsInfo = new SmsCbEtwsInfo(warningType, false, false, false, null); + } else { + etwsInfo = null; + } + + SmsCbCmasInfo cmasInfo = null; + int cmasMessageClassColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_MESSAGE_CLASS); + if (cmasMessageClassColumn != -1 && !cursor.isNull(cmasMessageClassColumn)) { + int messageClass = cursor.getInt(cmasMessageClassColumn); + + int cmasCategory; + int cmasCategoryColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_CATEGORY); + if (cmasCategoryColumn != -1 && !cursor.isNull(cmasCategoryColumn)) { + cmasCategory = cursor.getInt(cmasCategoryColumn); + } else { + cmasCategory = SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN; + } + + int responseType; + int cmasResponseTypeColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_RESPONSE_TYPE); + if (cmasResponseTypeColumn != -1 && !cursor.isNull(cmasResponseTypeColumn)) { + responseType = cursor.getInt(cmasResponseTypeColumn); + } else { + responseType = SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN; + } + + int severity; + int cmasSeverityColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_SEVERITY); + if (cmasSeverityColumn != -1 && !cursor.isNull(cmasSeverityColumn)) { + severity = cursor.getInt(cmasSeverityColumn); + } else { + severity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN; + } + + int urgency; + int cmasUrgencyColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_URGENCY); + if (cmasUrgencyColumn != -1 && !cursor.isNull(cmasUrgencyColumn)) { + urgency = cursor.getInt(cmasUrgencyColumn); + } else { + urgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN; + } + + int certainty; + int cmasCertaintyColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_CERTAINTY); + if (cmasCertaintyColumn != -1 && !cursor.isNull(cmasCertaintyColumn)) { + certainty = cursor.getInt(cmasCertaintyColumn); + } else { + certainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN; + } + + cmasInfo = new SmsCbCmasInfo(messageClass, cmasCategory, responseType, severity, + urgency, certainty); + } + + String geoStr = cursor.getString(cursor.getColumnIndex(CellBroadcasts.GEOMETRIES)); + List<Geometry> geometries = + geoStr != null ? CbGeoUtils.parseGeometriesFromString(geoStr) : null; + + long receivedTimeMillis = cursor.getLong( + cursor.getColumnIndexOrThrow(CellBroadcasts.RECEIVED_TIME)); + + int maximumWaitTimeSec = cursor.getInt( + cursor.getColumnIndexOrThrow(CellBroadcasts.MAXIMUM_WAIT_TIME)); + + return new SmsCbMessage(format, geoScope, serialNum, location, category, + language, 0, body, priority, etwsInfo, cmasInfo, maximumWaitTimeSec, geometries, + receivedTimeMillis, slotIndex, subId); + } + + /** + * @return {@code True} if this message needs geo-fencing check. + */ + public boolean needGeoFencingCheck() { + return mMaximumWaitTimeSec > 0 && mGeometries != null && !mGeometries.isEmpty(); + } +}