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     public CellBroadcastMessage(SmsCbMessage message) {
     53         mSmsCbMessage = message;
     54         mDeliveryTime = System.currentTimeMillis();
     55         mIsRead = false;
     56     }
     57 
     58     private CellBroadcastMessage(SmsCbMessage message, long deliveryTime, boolean isRead) {
     59         mSmsCbMessage = message;
     60         mDeliveryTime = deliveryTime;
     61         mIsRead = isRead;
     62     }
     63 
     64     private CellBroadcastMessage(Parcel in) {
     65         mSmsCbMessage = new SmsCbMessage(in);
     66         mDeliveryTime = in.readLong();
     67         mIsRead = (in.readInt() != 0);
     68     }
     69 
     70     /** Parcelable: no special flags. */
     71     @Override
     72     public int describeContents() {
     73         return 0;
     74     }
     75 
     76     @Override
     77     public void writeToParcel(Parcel out, int flags) {
     78         mSmsCbMessage.writeToParcel(out, flags);
     79         out.writeLong(mDeliveryTime);
     80         out.writeInt(mIsRead ? 1 : 0);
     81     }
     82 
     83     public static final Parcelable.Creator<CellBroadcastMessage> CREATOR
     84             = new Parcelable.Creator<CellBroadcastMessage>() {
     85         @Override
     86         public CellBroadcastMessage createFromParcel(Parcel in) {
     87             return new CellBroadcastMessage(in);
     88         }
     89 
     90         @Override
     91         public CellBroadcastMessage[] newArray(int size) {
     92             return new CellBroadcastMessage[size];
     93         }
     94     };
     95 
     96     /**
     97      * Create a CellBroadcastMessage from a row in the database.
     98      * @param cursor an open SQLite cursor pointing to the row to read
     99      * @return the new CellBroadcastMessage
    100      * @throws IllegalArgumentException if one of the required columns is missing
    101      */
    102     public static CellBroadcastMessage createFromCursor(Cursor cursor) {
    103         int geoScope = cursor.getInt(
    104                 cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.GEOGRAPHICAL_SCOPE));
    105         int serialNum = cursor.getInt(
    106                 cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.SERIAL_NUMBER));
    107         int category = cursor.getInt(
    108                 cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.SERVICE_CATEGORY));
    109         String language = cursor.getString(
    110                 cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.LANGUAGE_CODE));
    111         String body = cursor.getString(
    112                 cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.MESSAGE_BODY));
    113         int format = cursor.getInt(
    114                 cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.MESSAGE_FORMAT));
    115         int priority = cursor.getInt(
    116                 cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.MESSAGE_PRIORITY));
    117 
    118         String plmn;
    119         int plmnColumn = cursor.getColumnIndex(Telephony.CellBroadcasts.PLMN);
    120         if (plmnColumn != -1 && !cursor.isNull(plmnColumn)) {
    121             plmn = cursor.getString(plmnColumn);
    122         } else {
    123             plmn = null;
    124         }
    125 
    126         int lac;
    127         int lacColumn = cursor.getColumnIndex(Telephony.CellBroadcasts.LAC);
    128         if (lacColumn != -1 && !cursor.isNull(lacColumn)) {
    129             lac = cursor.getInt(lacColumn);
    130         } else {
    131             lac = -1;
    132         }
    133 
    134         int cid;
    135         int cidColumn = cursor.getColumnIndex(Telephony.CellBroadcasts.CID);
    136         if (cidColumn != -1 && !cursor.isNull(cidColumn)) {
    137             cid = cursor.getInt(cidColumn);
    138         } else {
    139             cid = -1;
    140         }
    141 
    142         SmsCbLocation location = new SmsCbLocation(plmn, lac, cid);
    143 
    144         SmsCbEtwsInfo etwsInfo;
    145         int etwsWarningTypeColumn = cursor.getColumnIndex(
    146                 Telephony.CellBroadcasts.ETWS_WARNING_TYPE);
    147         if (etwsWarningTypeColumn != -1 && !cursor.isNull(etwsWarningTypeColumn)) {
    148             int warningType = cursor.getInt(etwsWarningTypeColumn);
    149             etwsInfo = new SmsCbEtwsInfo(warningType, false, false, null);
    150         } else {
    151             etwsInfo = null;
    152         }
    153 
    154         SmsCbCmasInfo cmasInfo;
    155         int cmasMessageClassColumn = cursor.getColumnIndex(
    156                 Telephony.CellBroadcasts.CMAS_MESSAGE_CLASS);
    157         if (cmasMessageClassColumn != -1 && !cursor.isNull(cmasMessageClassColumn)) {
    158             int messageClass = cursor.getInt(cmasMessageClassColumn);
    159 
    160             int cmasCategory;
    161             int cmasCategoryColumn = cursor.getColumnIndex(
    162                     Telephony.CellBroadcasts.CMAS_CATEGORY);
    163             if (cmasCategoryColumn != -1 && !cursor.isNull(cmasCategoryColumn)) {
    164                 cmasCategory = cursor.getInt(cmasCategoryColumn);
    165             } else {
    166                 cmasCategory = SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN;
    167             }
    168 
    169             int responseType;
    170             int cmasResponseTypeColumn = cursor.getColumnIndex(
    171                     Telephony.CellBroadcasts.CMAS_RESPONSE_TYPE);
    172             if (cmasResponseTypeColumn != -1 && !cursor.isNull(cmasResponseTypeColumn)) {
    173                 responseType = cursor.getInt(cmasResponseTypeColumn);
    174             } else {
    175                 responseType = SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN;
    176             }
    177 
    178             int severity;
    179             int cmasSeverityColumn = cursor.getColumnIndex(
    180                     Telephony.CellBroadcasts.CMAS_SEVERITY);
    181             if (cmasSeverityColumn != -1 && !cursor.isNull(cmasSeverityColumn)) {
    182                 severity = cursor.getInt(cmasSeverityColumn);
    183             } else {
    184                 severity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN;
    185             }
    186 
    187             int urgency;
    188             int cmasUrgencyColumn = cursor.getColumnIndex(
    189                     Telephony.CellBroadcasts.CMAS_URGENCY);
    190             if (cmasUrgencyColumn != -1 && !cursor.isNull(cmasUrgencyColumn)) {
    191                 urgency = cursor.getInt(cmasUrgencyColumn);
    192             } else {
    193                 urgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN;
    194             }
    195 
    196             int certainty;
    197             int cmasCertaintyColumn = cursor.getColumnIndex(
    198                     Telephony.CellBroadcasts.CMAS_CERTAINTY);
    199             if (cmasCertaintyColumn != -1 && !cursor.isNull(cmasCertaintyColumn)) {
    200                 certainty = cursor.getInt(cmasCertaintyColumn);
    201             } else {
    202                 certainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN;
    203             }
    204 
    205             cmasInfo = new SmsCbCmasInfo(messageClass, cmasCategory, responseType, severity,
    206                     urgency, certainty);
    207         } else {
    208             cmasInfo = null;
    209         }
    210 
    211         SmsCbMessage msg = new SmsCbMessage(format, geoScope, serialNum, location, category,
    212                 language, body, priority, etwsInfo, cmasInfo);
    213 
    214         long deliveryTime = cursor.getLong(cursor.getColumnIndexOrThrow(
    215                 Telephony.CellBroadcasts.DELIVERY_TIME));
    216         boolean isRead = (cursor.getInt(cursor.getColumnIndexOrThrow(
    217                 Telephony.CellBroadcasts.MESSAGE_READ)) != 0);
    218 
    219         return new CellBroadcastMessage(msg, deliveryTime, isRead);
    220     }
    221 
    222     /**
    223      * Return a ContentValues object for insertion into the database.
    224      * @return a new ContentValues object containing this object's data
    225      */
    226     public ContentValues getContentValues() {
    227         ContentValues cv = new ContentValues(16);
    228         SmsCbMessage msg = mSmsCbMessage;
    229         cv.put(Telephony.CellBroadcasts.GEOGRAPHICAL_SCOPE, msg.getGeographicalScope());
    230         SmsCbLocation location = msg.getLocation();
    231         if (location.getPlmn() != null) {
    232             cv.put(Telephony.CellBroadcasts.PLMN, location.getPlmn());
    233         }
    234         if (location.getLac() != -1) {
    235             cv.put(Telephony.CellBroadcasts.LAC, location.getLac());
    236         }
    237         if (location.getCid() != -1) {
    238             cv.put(Telephony.CellBroadcasts.CID, location.getCid());
    239         }
    240         cv.put(Telephony.CellBroadcasts.SERIAL_NUMBER, msg.getSerialNumber());
    241         cv.put(Telephony.CellBroadcasts.SERVICE_CATEGORY, msg.getServiceCategory());
    242         cv.put(Telephony.CellBroadcasts.LANGUAGE_CODE, msg.getLanguageCode());
    243         cv.put(Telephony.CellBroadcasts.MESSAGE_BODY, msg.getMessageBody());
    244         cv.put(Telephony.CellBroadcasts.DELIVERY_TIME, mDeliveryTime);
    245         cv.put(Telephony.CellBroadcasts.MESSAGE_READ, mIsRead);
    246         cv.put(Telephony.CellBroadcasts.MESSAGE_FORMAT, msg.getMessageFormat());
    247         cv.put(Telephony.CellBroadcasts.MESSAGE_PRIORITY, msg.getMessagePriority());
    248 
    249         SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo();
    250         if (etwsInfo != null) {
    251             cv.put(Telephony.CellBroadcasts.ETWS_WARNING_TYPE, etwsInfo.getWarningType());
    252         }
    253 
    254         SmsCbCmasInfo cmasInfo = mSmsCbMessage.getCmasWarningInfo();
    255         if (cmasInfo != null) {
    256             cv.put(Telephony.CellBroadcasts.CMAS_MESSAGE_CLASS, cmasInfo.getMessageClass());
    257             cv.put(Telephony.CellBroadcasts.CMAS_CATEGORY, cmasInfo.getCategory());
    258             cv.put(Telephony.CellBroadcasts.CMAS_RESPONSE_TYPE, cmasInfo.getResponseType());
    259             cv.put(Telephony.CellBroadcasts.CMAS_SEVERITY, cmasInfo.getSeverity());
    260             cv.put(Telephony.CellBroadcasts.CMAS_URGENCY, cmasInfo.getUrgency());
    261             cv.put(Telephony.CellBroadcasts.CMAS_CERTAINTY, cmasInfo.getCertainty());
    262         }
    263 
    264         return cv;
    265     }
    266 
    267     /**
    268      * Set or clear the "read message" flag.
    269      * @param isRead true if the message has been read; false if not
    270      */
    271     public void setIsRead(boolean isRead) {
    272         mIsRead = isRead;
    273     }
    274 
    275     public String getLanguageCode() {
    276         return mSmsCbMessage.getLanguageCode();
    277     }
    278 
    279     public int getServiceCategory() {
    280         return mSmsCbMessage.getServiceCategory();
    281     }
    282 
    283     public long getDeliveryTime() {
    284         return mDeliveryTime;
    285     }
    286 
    287     public String getMessageBody() {
    288         return mSmsCbMessage.getMessageBody();
    289     }
    290 
    291     public boolean isRead() {
    292         return mIsRead;
    293     }
    294 
    295     public int getSerialNumber() {
    296         return mSmsCbMessage.getSerialNumber();
    297     }
    298 
    299     public SmsCbCmasInfo getCmasWarningInfo() {
    300         return mSmsCbMessage.getCmasWarningInfo();
    301     }
    302 
    303     public SmsCbEtwsInfo getEtwsWarningInfo() {
    304         return mSmsCbMessage.getEtwsWarningInfo();
    305     }
    306 
    307     /**
    308      * Return whether the broadcast is an emergency (PWS) message type.
    309      * This includes lower priority test messages and Amber alerts.
    310      *
    311      * All public alerts show the flashing warning icon in the dialog,
    312      * but only emergency alerts play the alert sound and speak the message.
    313      *
    314      * @return true if the message is PWS type; false otherwise
    315      */
    316     public boolean isPublicAlertMessage() {
    317         return mSmsCbMessage.isEmergencyMessage();
    318     }
    319 
    320     /**
    321      * Returns whether the broadcast is an emergency (PWS) message type,
    322      * including test messages and AMBER alerts.
    323      *
    324      * @return true if the message is PWS type (ETWS or CMAS)
    325      */
    326     public boolean isEmergencyAlertMessage() {
    327         return mSmsCbMessage.isEmergencyMessage();
    328     }
    329 
    330     /**
    331      * Return whether the broadcast is an ETWS emergency message type.
    332      * @return true if the message is ETWS emergency type; false otherwise
    333      */
    334     public boolean isEtwsMessage() {
    335         return mSmsCbMessage.isEtwsMessage();
    336     }
    337 
    338     /**
    339      * Return whether the broadcast is a CMAS emergency message type.
    340      * @return true if the message is CMAS emergency type; false otherwise
    341      */
    342     public boolean isCmasMessage() {
    343         return mSmsCbMessage.isCmasMessage();
    344     }
    345 
    346     /**
    347      * Return the CMAS message class.
    348      * @return the CMAS message class, e.g. {@link SmsCbCmasInfo#CMAS_CLASS_SEVERE_THREAT}, or
    349      *  {@link SmsCbCmasInfo#CMAS_CLASS_UNKNOWN} if this is not a CMAS alert
    350      */
    351     public int getCmasMessageClass() {
    352         if (mSmsCbMessage.isCmasMessage()) {
    353             return mSmsCbMessage.getCmasWarningInfo().getMessageClass();
    354         } else {
    355             return SmsCbCmasInfo.CMAS_CLASS_UNKNOWN;
    356         }
    357     }
    358 
    359     /**
    360      * Return whether the broadcast is an ETWS popup alert.
    361      * This method checks the message ID and the message code.
    362      * @return true if the message indicates an ETWS popup alert
    363      */
    364     public boolean isEtwsPopupAlert() {
    365         SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo();
    366         return etwsInfo != null && etwsInfo.isPopupAlert();
    367     }
    368 
    369     /**
    370      * Return whether the broadcast is an ETWS emergency user alert.
    371      * This method checks the message ID and the message code.
    372      * @return true if the message indicates an ETWS emergency user alert
    373      */
    374     public boolean isEtwsEmergencyUserAlert() {
    375         SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo();
    376         return etwsInfo != null && etwsInfo.isEmergencyUserAlert();
    377     }
    378 
    379     /**
    380      * Return whether the broadcast is an ETWS test message.
    381      * @return true if the message is an ETWS test message; false otherwise
    382      */
    383     public boolean isEtwsTestMessage() {
    384         SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo();
    385         return etwsInfo != null &&
    386                 etwsInfo.getWarningType() == SmsCbEtwsInfo.ETWS_WARNING_TYPE_TEST_MESSAGE;
    387     }
    388 
    389     /**
    390      * Return the abbreviated date string for the message delivery time.
    391      * @param context the context object
    392      * @return a String to use in the broadcast list UI
    393      */
    394     public String getDateString(Context context) {
    395         int flags = DateUtils.FORMAT_NO_NOON_MIDNIGHT | DateUtils.FORMAT_SHOW_TIME |
    396                 DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE |
    397                 DateUtils.FORMAT_CAP_AMPM;
    398         return DateUtils.formatDateTime(context, mDeliveryTime, flags);
    399     }
    400 
    401     /**
    402      * Return the date string for the message delivery time, suitable for text-to-speech.
    403      * @param context the context object
    404      * @return a String for populating the list item AccessibilityEvent for TTS
    405      */
    406     public String getSpokenDateString(Context context) {
    407         int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE;
    408         return DateUtils.formatDateTime(context, mDeliveryTime, flags);
    409     }
    410 }
    411