CellBroadcastMessage.java revision a8467dd0c524787104b1ccdddc5e8af10ba729ed
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
17package android.telephony;
18
19import android.content.ContentValues;
20import android.content.Context;
21import android.database.Cursor;
22import android.os.Parcel;
23import android.os.Parcelable;
24import android.provider.Telephony;
25import 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 */
40public 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 long mSubId = 0;
58
59    /**
60     * set Subscription information
61     *
62     * @hide
63     */
64    public void setSubId(long subId) {
65        mSubId = subId;
66    }
67
68    /**
69     * get Subscription information
70     *
71     * @hide
72     */
73    public long 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