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