Home | History | Annotate | Download | only in telephony
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy of
      6  * 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, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations under
     14  * the License.
     15  */
     16 
     17 package android.telephony;
     18 
     19 import android.content.ContentValues;
     20 import android.content.Context;
     21 import android.database.Cursor;
     22 import android.os.Parcel;
     23 import android.os.Parcelable;
     24 import android.provider.Telephony;
     25 import android.text.format.DateUtils;
     26 
     27 /**
     28  * Application wrapper for {@link SmsCbMessage}. This is Parcelable so that
     29  * decoded broadcast message objects can be passed between running Services.
     30  * New broadcasts are received by the CellBroadcastReceiver app, which exports
     31  * the database of previously received broadcasts at "content://cellbroadcasts/".
     32  * The "android.permission.READ_CELL_BROADCASTS" permission is required to read
     33  * from the ContentProvider, and writes to the database are not allowed.<p>
     34  *
     35  * Use {@link #createFromCursor} to create CellBroadcastMessage objects from rows
     36  * in the database cursor returned by the ContentProvider.
     37  *
     38  * {@hide}
     39  */
     40 public class CellBroadcastMessage implements Parcelable {
     41 
     42     /** Identifier for getExtra() when adding this object to an Intent. */
     43     public static final String SMS_CB_MESSAGE_EXTRA =
     44             "com.android.cellbroadcastreceiver.SMS_CB_MESSAGE";
     45 
     46     /** SmsCbMessage. */
     47     private final SmsCbMessage mSmsCbMessage;
     48 
     49     private final long mDeliveryTime;
     50     private boolean mIsRead;
     51 
     52     /**
     53      * Indicates the subId
     54      *
     55      * @hide
     56      */
     57     private int mSubId = 0;
     58 
     59     /**
     60      * set Subscription information
     61      *
     62      * @hide
     63      */
     64     public void setSubId(int subId) {
     65         mSubId = subId;
     66     }
     67 
     68     /**
     69      * get Subscription information
     70      *
     71      * @hide
     72      */
     73     public int getSubId() {
     74         return mSubId;
     75     }
     76 
     77     public CellBroadcastMessage(SmsCbMessage message) {
     78         mSmsCbMessage = message;
     79         mDeliveryTime = System.currentTimeMillis();
     80         mIsRead = false;
     81     }
     82 
     83     private CellBroadcastMessage(SmsCbMessage message, long deliveryTime, boolean isRead) {
     84         mSmsCbMessage = message;
     85         mDeliveryTime = deliveryTime;
     86         mIsRead = isRead;
     87     }
     88 
     89     private CellBroadcastMessage(Parcel in) {
     90         mSmsCbMessage = new SmsCbMessage(in);
     91         mDeliveryTime = in.readLong();
     92         mIsRead = (in.readInt() != 0);
     93     }
     94 
     95     /** Parcelable: no special flags. */
     96     @Override
     97     public int describeContents() {
     98         return 0;
     99     }
    100 
    101     @Override
    102     public void writeToParcel(Parcel out, int flags) {
    103         mSmsCbMessage.writeToParcel(out, flags);
    104         out.writeLong(mDeliveryTime);
    105         out.writeInt(mIsRead ? 1 : 0);
    106     }
    107 
    108     public static final Parcelable.Creator<CellBroadcastMessage> CREATOR
    109             = new Parcelable.Creator<CellBroadcastMessage>() {
    110         @Override
    111         public CellBroadcastMessage createFromParcel(Parcel in) {
    112             return new CellBroadcastMessage(in);
    113         }
    114 
    115         @Override
    116         public CellBroadcastMessage[] newArray(int size) {
    117             return new CellBroadcastMessage[size];
    118         }
    119     };
    120 
    121     /**
    122      * Create a CellBroadcastMessage from a row in the database.
    123      * @param cursor an open SQLite cursor pointing to the row to read
    124      * @return the new CellBroadcastMessage
    125      * @throws IllegalArgumentException if one of the required columns is missing
    126      */
    127     public static CellBroadcastMessage createFromCursor(Cursor cursor) {
    128         int geoScope = cursor.getInt(
    129                 cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.GEOGRAPHICAL_SCOPE));
    130         int serialNum = cursor.getInt(
    131                 cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.SERIAL_NUMBER));
    132         int category = cursor.getInt(
    133                 cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.SERVICE_CATEGORY));
    134         String language = cursor.getString(
    135                 cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.LANGUAGE_CODE));
    136         String body = cursor.getString(
    137                 cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.MESSAGE_BODY));
    138         int format = cursor.getInt(
    139                 cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.MESSAGE_FORMAT));
    140         int priority = cursor.getInt(
    141                 cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.MESSAGE_PRIORITY));
    142 
    143         String plmn;
    144         int plmnColumn = cursor.getColumnIndex(Telephony.CellBroadcasts.PLMN);
    145         if (plmnColumn != -1 && !cursor.isNull(plmnColumn)) {
    146             plmn = cursor.getString(plmnColumn);
    147         } else {
    148             plmn = null;
    149         }
    150 
    151         int lac;
    152         int lacColumn = cursor.getColumnIndex(Telephony.CellBroadcasts.LAC);
    153         if (lacColumn != -1 && !cursor.isNull(lacColumn)) {
    154             lac = cursor.getInt(lacColumn);
    155         } else {
    156             lac = -1;
    157         }
    158 
    159         int cid;
    160         int cidColumn = cursor.getColumnIndex(Telephony.CellBroadcasts.CID);
    161         if (cidColumn != -1 && !cursor.isNull(cidColumn)) {
    162             cid = cursor.getInt(cidColumn);
    163         } else {
    164             cid = -1;
    165         }
    166 
    167         SmsCbLocation location = new SmsCbLocation(plmn, lac, cid);
    168 
    169         SmsCbEtwsInfo etwsInfo;
    170         int etwsWarningTypeColumn = cursor.getColumnIndex(
    171                 Telephony.CellBroadcasts.ETWS_WARNING_TYPE);
    172         if (etwsWarningTypeColumn != -1 && !cursor.isNull(etwsWarningTypeColumn)) {
    173             int warningType = cursor.getInt(etwsWarningTypeColumn);
    174             etwsInfo = new SmsCbEtwsInfo(warningType, false, false, null);
    175         } else {
    176             etwsInfo = null;
    177         }
    178 
    179         SmsCbCmasInfo cmasInfo;
    180         int cmasMessageClassColumn = cursor.getColumnIndex(
    181                 Telephony.CellBroadcasts.CMAS_MESSAGE_CLASS);
    182         if (cmasMessageClassColumn != -1 && !cursor.isNull(cmasMessageClassColumn)) {
    183             int messageClass = cursor.getInt(cmasMessageClassColumn);
    184 
    185             int cmasCategory;
    186             int cmasCategoryColumn = cursor.getColumnIndex(
    187                     Telephony.CellBroadcasts.CMAS_CATEGORY);
    188             if (cmasCategoryColumn != -1 && !cursor.isNull(cmasCategoryColumn)) {
    189                 cmasCategory = cursor.getInt(cmasCategoryColumn);
    190             } else {
    191                 cmasCategory = SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN;
    192             }
    193 
    194             int responseType;
    195             int cmasResponseTypeColumn = cursor.getColumnIndex(
    196                     Telephony.CellBroadcasts.CMAS_RESPONSE_TYPE);
    197             if (cmasResponseTypeColumn != -1 && !cursor.isNull(cmasResponseTypeColumn)) {
    198                 responseType = cursor.getInt(cmasResponseTypeColumn);
    199             } else {
    200                 responseType = SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN;
    201             }
    202 
    203             int severity;
    204             int cmasSeverityColumn = cursor.getColumnIndex(
    205                     Telephony.CellBroadcasts.CMAS_SEVERITY);
    206             if (cmasSeverityColumn != -1 && !cursor.isNull(cmasSeverityColumn)) {
    207                 severity = cursor.getInt(cmasSeverityColumn);
    208             } else {
    209                 severity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN;
    210             }
    211 
    212             int urgency;
    213             int cmasUrgencyColumn = cursor.getColumnIndex(
    214                     Telephony.CellBroadcasts.CMAS_URGENCY);
    215             if (cmasUrgencyColumn != -1 && !cursor.isNull(cmasUrgencyColumn)) {
    216                 urgency = cursor.getInt(cmasUrgencyColumn);
    217             } else {
    218                 urgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN;
    219             }
    220 
    221             int certainty;
    222             int cmasCertaintyColumn = cursor.getColumnIndex(
    223                     Telephony.CellBroadcasts.CMAS_CERTAINTY);
    224             if (cmasCertaintyColumn != -1 && !cursor.isNull(cmasCertaintyColumn)) {
    225                 certainty = cursor.getInt(cmasCertaintyColumn);
    226             } else {
    227                 certainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN;
    228             }
    229 
    230             cmasInfo = new SmsCbCmasInfo(messageClass, cmasCategory, responseType, severity,
    231                     urgency, certainty);
    232         } else {
    233             cmasInfo = null;
    234         }
    235 
    236         SmsCbMessage msg = new SmsCbMessage(format, geoScope, serialNum, location, category,
    237                 language, body, priority, etwsInfo, cmasInfo);
    238 
    239         long deliveryTime = cursor.getLong(cursor.getColumnIndexOrThrow(
    240                 Telephony.CellBroadcasts.DELIVERY_TIME));
    241         boolean isRead = (cursor.getInt(cursor.getColumnIndexOrThrow(
    242                 Telephony.CellBroadcasts.MESSAGE_READ)) != 0);
    243 
    244         return new CellBroadcastMessage(msg, deliveryTime, isRead);
    245     }
    246 
    247     /**
    248      * Return a ContentValues object for insertion into the database.
    249      * @return a new ContentValues object containing this object's data
    250      */
    251     public ContentValues getContentValues() {
    252         ContentValues cv = new ContentValues(16);
    253         SmsCbMessage msg = mSmsCbMessage;
    254         cv.put(Telephony.CellBroadcasts.GEOGRAPHICAL_SCOPE, msg.getGeographicalScope());
    255         SmsCbLocation location = msg.getLocation();
    256         if (location.getPlmn() != null) {
    257             cv.put(Telephony.CellBroadcasts.PLMN, location.getPlmn());
    258         }
    259         if (location.getLac() != -1) {
    260             cv.put(Telephony.CellBroadcasts.LAC, location.getLac());
    261         }
    262         if (location.getCid() != -1) {
    263             cv.put(Telephony.CellBroadcasts.CID, location.getCid());
    264         }
    265         cv.put(Telephony.CellBroadcasts.SERIAL_NUMBER, msg.getSerialNumber());
    266         cv.put(Telephony.CellBroadcasts.SERVICE_CATEGORY, msg.getServiceCategory());
    267         cv.put(Telephony.CellBroadcasts.LANGUAGE_CODE, msg.getLanguageCode());
    268         cv.put(Telephony.CellBroadcasts.MESSAGE_BODY, msg.getMessageBody());
    269         cv.put(Telephony.CellBroadcasts.DELIVERY_TIME, mDeliveryTime);
    270         cv.put(Telephony.CellBroadcasts.MESSAGE_READ, mIsRead);
    271         cv.put(Telephony.CellBroadcasts.MESSAGE_FORMAT, msg.getMessageFormat());
    272         cv.put(Telephony.CellBroadcasts.MESSAGE_PRIORITY, msg.getMessagePriority());
    273 
    274         SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo();
    275         if (etwsInfo != null) {
    276             cv.put(Telephony.CellBroadcasts.ETWS_WARNING_TYPE, etwsInfo.getWarningType());
    277         }
    278 
    279         SmsCbCmasInfo cmasInfo = mSmsCbMessage.getCmasWarningInfo();
    280         if (cmasInfo != null) {
    281             cv.put(Telephony.CellBroadcasts.CMAS_MESSAGE_CLASS, cmasInfo.getMessageClass());
    282             cv.put(Telephony.CellBroadcasts.CMAS_CATEGORY, cmasInfo.getCategory());
    283             cv.put(Telephony.CellBroadcasts.CMAS_RESPONSE_TYPE, cmasInfo.getResponseType());
    284             cv.put(Telephony.CellBroadcasts.CMAS_SEVERITY, cmasInfo.getSeverity());
    285             cv.put(Telephony.CellBroadcasts.CMAS_URGENCY, cmasInfo.getUrgency());
    286             cv.put(Telephony.CellBroadcasts.CMAS_CERTAINTY, cmasInfo.getCertainty());
    287         }
    288 
    289         return cv;
    290     }
    291 
    292     /**
    293      * Set or clear the "read message" flag.
    294      * @param isRead true if the message has been read; false if not
    295      */
    296     public void setIsRead(boolean isRead) {
    297         mIsRead = isRead;
    298     }
    299 
    300     public String getLanguageCode() {
    301         return mSmsCbMessage.getLanguageCode();
    302     }
    303 
    304     public int getServiceCategory() {
    305         return mSmsCbMessage.getServiceCategory();
    306     }
    307 
    308     public long getDeliveryTime() {
    309         return mDeliveryTime;
    310     }
    311 
    312     public String getMessageBody() {
    313         return mSmsCbMessage.getMessageBody();
    314     }
    315 
    316     public boolean isRead() {
    317         return mIsRead;
    318     }
    319 
    320     public int getSerialNumber() {
    321         return mSmsCbMessage.getSerialNumber();
    322     }
    323 
    324     public SmsCbCmasInfo getCmasWarningInfo() {
    325         return mSmsCbMessage.getCmasWarningInfo();
    326     }
    327 
    328     public SmsCbEtwsInfo getEtwsWarningInfo() {
    329         return mSmsCbMessage.getEtwsWarningInfo();
    330     }
    331 
    332     /**
    333      * Return whether the broadcast is an emergency (PWS) message type.
    334      * This includes lower priority test messages and Amber alerts.
    335      *
    336      * All public alerts show the flashing warning icon in the dialog,
    337      * but only emergency alerts play the alert sound and speak the message.
    338      *
    339      * @return true if the message is PWS type; false otherwise
    340      */
    341     public boolean isPublicAlertMessage() {
    342         return mSmsCbMessage.isEmergencyMessage();
    343     }
    344 
    345     /**
    346      * Returns whether the broadcast is an emergency (PWS) message type,
    347      * including test messages and AMBER alerts.
    348      *
    349      * @return true if the message is PWS type (ETWS or CMAS)
    350      */
    351     public boolean isEmergencyAlertMessage() {
    352         return mSmsCbMessage.isEmergencyMessage();
    353     }
    354 
    355     /**
    356      * Return whether the broadcast is an ETWS emergency message type.
    357      * @return true if the message is ETWS emergency type; false otherwise
    358      */
    359     public boolean isEtwsMessage() {
    360         return mSmsCbMessage.isEtwsMessage();
    361     }
    362 
    363     /**
    364      * Return whether the broadcast is a CMAS emergency message type.
    365      * @return true if the message is CMAS emergency type; false otherwise
    366      */
    367     public boolean isCmasMessage() {
    368         return mSmsCbMessage.isCmasMessage();
    369     }
    370 
    371     /**
    372      * Return the CMAS message class.
    373      * @return the CMAS message class, e.g. {@link SmsCbCmasInfo#CMAS_CLASS_SEVERE_THREAT}, or
    374      *  {@link SmsCbCmasInfo#CMAS_CLASS_UNKNOWN} if this is not a CMAS alert
    375      */
    376     public int getCmasMessageClass() {
    377         if (mSmsCbMessage.isCmasMessage()) {
    378             return mSmsCbMessage.getCmasWarningInfo().getMessageClass();
    379         } else {
    380             return SmsCbCmasInfo.CMAS_CLASS_UNKNOWN;
    381         }
    382     }
    383 
    384     /**
    385      * Return whether the broadcast is an ETWS popup alert.
    386      * This method checks the message ID and the message code.
    387      * @return true if the message indicates an ETWS popup alert
    388      */
    389     public boolean isEtwsPopupAlert() {
    390         SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo();
    391         return etwsInfo != null && etwsInfo.isPopupAlert();
    392     }
    393 
    394     /**
    395      * Return whether the broadcast is an ETWS emergency user alert.
    396      * This method checks the message ID and the message code.
    397      * @return true if the message indicates an ETWS emergency user alert
    398      */
    399     public boolean isEtwsEmergencyUserAlert() {
    400         SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo();
    401         return etwsInfo != null && etwsInfo.isEmergencyUserAlert();
    402     }
    403 
    404     /**
    405      * Return whether the broadcast is an ETWS test message.
    406      * @return true if the message is an ETWS test message; false otherwise
    407      */
    408     public boolean isEtwsTestMessage() {
    409         SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo();
    410         return etwsInfo != null &&
    411                 etwsInfo.getWarningType() == SmsCbEtwsInfo.ETWS_WARNING_TYPE_TEST_MESSAGE;
    412     }
    413 
    414     /**
    415      * Return the abbreviated date string for the message delivery time.
    416      * @param context the context object
    417      * @return a String to use in the broadcast list UI
    418      */
    419     public String getDateString(Context context) {
    420         int flags = DateUtils.FORMAT_NO_NOON_MIDNIGHT | DateUtils.FORMAT_SHOW_TIME |
    421                 DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE |
    422                 DateUtils.FORMAT_CAP_AMPM;
    423         return DateUtils.formatDateTime(context, mDeliveryTime, flags);
    424     }
    425 
    426     /**
    427      * Return the date string for the message delivery time, suitable for text-to-speech.
    428      * @param context the context object
    429      * @return a String for populating the list item AccessibilityEvent for TTS
    430      */
    431     public String getSpokenDateString(Context context) {
    432         int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE;
    433         return DateUtils.formatDateTime(context, mDeliveryTime, flags);
    434     }
    435 }
    436