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