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 com.android.cellbroadcastreceiver; 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.telephony.SmsCbConstants; 25 import android.telephony.SmsCbMessage; 26 import android.text.format.DateUtils; 27 28 import com.android.internal.telephony.gsm.SmsCbHeader; 29 30 /** 31 * Application wrapper for {@link SmsCbMessage}. This is Parcelable so that 32 * decoded broadcast message objects can be passed between running Services. 33 * New broadcasts are received by {@link CellBroadcastReceiver}, 34 * displayed by {@link CellBroadcastAlertService}, and saved to SQLite by 35 * {@link CellBroadcastDatabaseService}. 36 */ 37 public class CellBroadcastMessage implements Parcelable { 38 39 /** Identifier for getExtra() when adding this object to an Intent. */ 40 public static final String SMS_CB_MESSAGE_EXTRA = 41 "com.android.cellbroadcastreceiver.SMS_CB_MESSAGE"; 42 43 private final int mGeographicalScope; 44 private final int mSerialNumber; 45 private final int mMessageCode; 46 private final int mMessageIdentifier; 47 private final String mLanguageCode; 48 private final String mMessageBody; 49 private final long mDeliveryTime; 50 private boolean mIsRead; 51 CellBroadcastMessage(SmsCbMessage message)52 public CellBroadcastMessage(SmsCbMessage message) { 53 mGeographicalScope = message.getGeographicalScope(); 54 mSerialNumber = message.getUpdateNumber(); 55 mMessageCode = message.getMessageCode(); 56 mMessageIdentifier = message.getMessageIdentifier(); 57 mLanguageCode = message.getLanguageCode(); 58 mMessageBody = message.getMessageBody(); 59 mDeliveryTime = System.currentTimeMillis(); 60 mIsRead = false; 61 } 62 CellBroadcastMessage(int geoScope, int serialNumber, int messageCode, int messageId, String languageCode, String messageBody, long deliveryTime, boolean isRead)63 private CellBroadcastMessage(int geoScope, int serialNumber, 64 int messageCode, int messageId, String languageCode, 65 String messageBody, long deliveryTime, boolean isRead) { 66 mGeographicalScope = geoScope; 67 mSerialNumber = serialNumber; 68 mMessageCode = messageCode; 69 mMessageIdentifier = messageId; 70 mLanguageCode = languageCode; 71 mMessageBody = messageBody; 72 mDeliveryTime = deliveryTime; 73 mIsRead = isRead; 74 } 75 76 /** Parcelable: no special flags. */ describeContents()77 public int describeContents() { 78 return 0; 79 } 80 writeToParcel(Parcel out, int flags)81 public void writeToParcel(Parcel out, int flags) { 82 out.writeInt(mGeographicalScope); 83 out.writeInt(mSerialNumber); 84 out.writeInt(mMessageCode); 85 out.writeInt(mMessageIdentifier); 86 out.writeString(mLanguageCode); 87 out.writeString(mMessageBody); 88 out.writeLong(mDeliveryTime); 89 out.writeInt(mIsRead ? 1 : 0); 90 } 91 92 public static final Parcelable.Creator<CellBroadcastMessage> CREATOR 93 = new Parcelable.Creator<CellBroadcastMessage>() { 94 public CellBroadcastMessage createFromParcel(Parcel in) { 95 return new CellBroadcastMessage( 96 in.readInt(), in.readInt(), 97 in.readInt(), in.readInt(), in.readString(), 98 in.readString(), in.readLong(), (in.readInt() != 0)); 99 } 100 101 public CellBroadcastMessage[] newArray(int size) { 102 return new CellBroadcastMessage[size]; 103 } 104 }; 105 106 /** 107 * Create a CellBroadcastMessage from a row in the database. 108 * @param cursor an open SQLite cursor pointing to the row to read 109 * @return the new CellBroadcastMessage 110 */ createFromCursor(Cursor cursor)111 public static CellBroadcastMessage createFromCursor(Cursor cursor) { 112 int geoScope = cursor.getInt(CellBroadcastDatabase.COLUMN_GEOGRAPHICAL_SCOPE); 113 int serialNum = cursor.getInt(CellBroadcastDatabase.COLUMN_SERIAL_NUMBER); 114 int messageCode = cursor.getInt(CellBroadcastDatabase.COLUMN_MESSAGE_CODE); 115 int messageId = cursor.getInt(CellBroadcastDatabase.COLUMN_MESSAGE_IDENTIFIER); 116 String language = cursor.getString(CellBroadcastDatabase.COLUMN_LANGUAGE_CODE); 117 String body = cursor.getString(CellBroadcastDatabase.COLUMN_MESSAGE_BODY); 118 long deliveryTime = cursor.getLong(CellBroadcastDatabase.COLUMN_DELIVERY_TIME); 119 boolean isRead = (cursor.getInt(CellBroadcastDatabase.COLUMN_MESSAGE_READ) != 0); 120 return new CellBroadcastMessage(geoScope, serialNum, messageCode, messageId, 121 language, body, deliveryTime, isRead); 122 } 123 124 /** 125 * Return a ContentValues object for insertion into the database. 126 * @return a new ContentValues object containing this object's data 127 */ getContentValues()128 public ContentValues getContentValues() { 129 ContentValues cv = new ContentValues(8); 130 cv.put(CellBroadcastDatabase.Columns.GEOGRAPHICAL_SCOPE, mGeographicalScope); 131 cv.put(CellBroadcastDatabase.Columns.SERIAL_NUMBER, mSerialNumber); 132 cv.put(CellBroadcastDatabase.Columns.MESSAGE_CODE, mMessageCode); 133 cv.put(CellBroadcastDatabase.Columns.MESSAGE_IDENTIFIER, mMessageIdentifier); 134 cv.put(CellBroadcastDatabase.Columns.LANGUAGE_CODE, mLanguageCode); 135 cv.put(CellBroadcastDatabase.Columns.MESSAGE_BODY, mMessageBody); 136 cv.put(CellBroadcastDatabase.Columns.DELIVERY_TIME, mDeliveryTime); 137 cv.put(CellBroadcastDatabase.Columns.MESSAGE_READ, mIsRead); 138 return cv; 139 } 140 141 /** 142 * Set or clear the "read message" flag. 143 * @param isRead true if the message has been read; false if not 144 */ setIsRead(boolean isRead)145 public void setIsRead(boolean isRead) { 146 mIsRead = isRead; 147 } 148 getGeographicalScope()149 public int getGeographicalScope() { 150 return mGeographicalScope; 151 } 152 getSerialNumber()153 public int getSerialNumber() { 154 return mSerialNumber; 155 } 156 getMessageCode()157 public int getMessageCode() { 158 return mMessageCode; 159 } 160 getMessageIdentifier()161 public int getMessageIdentifier() { 162 return mMessageIdentifier; 163 } 164 getLanguageCode()165 public String getLanguageCode() { 166 return mLanguageCode; 167 } 168 getDeliveryTime()169 public long getDeliveryTime() { 170 return mDeliveryTime; 171 } 172 getMessageBody()173 public String getMessageBody() { 174 return mMessageBody; 175 } 176 isRead()177 public boolean isRead() { 178 return mIsRead; 179 } 180 181 /** 182 * Return whether the broadcast is an emergency (PWS) message type. 183 * This includes lower priority test messages and Amber alerts. 184 * 185 * All public alerts show the flashing warning icon in the dialog, 186 * but only emergency alerts play the alert sound and speak the message. 187 * 188 * @return true if the message is PWS type; false otherwise 189 */ isPublicAlertMessage()190 public boolean isPublicAlertMessage() { 191 return SmsCbHeader.isEmergencyMessage(mMessageIdentifier); 192 } 193 194 /** 195 * Returns whether the broadcast is an emergency (PWS) message type, 196 * including test messages, but excluding lower priority Amber alert broadcasts. 197 * 198 * @return true if the message is PWS type, excluding Amber alerts 199 */ isEmergencyAlertMessage()200 public boolean isEmergencyAlertMessage() { 201 int id = mMessageIdentifier; 202 return SmsCbHeader.isEmergencyMessage(id) && 203 id != SmsCbConstants.MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY; 204 } 205 206 /** 207 * Return whether the broadcast is an ETWS emergency message type. 208 * @return true if the message is ETWS emergency type; false otherwise 209 */ isEtwsMessage()210 public boolean isEtwsMessage() { 211 return SmsCbHeader.isEtwsMessage(mMessageIdentifier); 212 } 213 214 /** 215 * Return whether the broadcast is a CMAS emergency message type. 216 * @return true if the message is CMAS emergency type; false otherwise 217 */ isCmasMessage()218 public boolean isCmasMessage() { 219 return SmsCbHeader.isCmasMessage(mMessageIdentifier); 220 } 221 222 /** 223 * Return whether the broadcast is an ETWS popup alert. 224 * This method checks the message ID and the message code. 225 * @return true if the message indicates an ETWS popup alert 226 */ isEtwsPopupAlert()227 public boolean isEtwsPopupAlert() { 228 return SmsCbHeader.isEtwsMessage(mMessageIdentifier) && 229 SmsCbHeader.isEtwsPopupAlert(mMessageCode); 230 } 231 232 /** 233 * Return whether the broadcast is an ETWS emergency user alert. 234 * This method checks the message ID and the message code. 235 * @return true if the message indicates an ETWS emergency user alert 236 */ isEtwsEmergencyUserAlert()237 public boolean isEtwsEmergencyUserAlert() { 238 return SmsCbHeader.isEtwsMessage(mMessageIdentifier) && 239 SmsCbHeader.isEtwsEmergencyUserAlert(mMessageCode); 240 } 241 getDialogTitleResource()242 public int getDialogTitleResource() { 243 switch (mMessageIdentifier) { 244 case SmsCbConstants.MESSAGE_ID_ETWS_EARTHQUAKE_WARNING: 245 return R.string.etws_earthquake_warning; 246 247 case SmsCbConstants.MESSAGE_ID_ETWS_TSUNAMI_WARNING: 248 return R.string.etws_tsunami_warning; 249 250 case SmsCbConstants.MESSAGE_ID_ETWS_EARTHQUAKE_AND_TSUNAMI_WARNING: 251 return R.string.etws_earthquake_and_tsunami_warning; 252 253 case SmsCbConstants.MESSAGE_ID_ETWS_TEST_MESSAGE: 254 return R.string.etws_test_message; 255 256 case SmsCbConstants.MESSAGE_ID_ETWS_OTHER_EMERGENCY_TYPE: 257 return R.string.etws_other_emergency_type; 258 259 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL: 260 return R.string.cmas_presidential_level_alert; 261 262 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED: 263 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY: 264 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED: 265 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY: 266 return R.string.cmas_extreme_alert; 267 268 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED: 269 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY: 270 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED: 271 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY: 272 return R.string.cmas_severe_alert; 273 274 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY: 275 return R.string.cmas_amber_alert; 276 277 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST: 278 return R.string.cmas_required_monthly_test; 279 280 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXERCISE: 281 return R.string.cmas_exercise_alert; 282 283 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE: 284 return R.string.cmas_operator_defined_alert; 285 286 default: 287 if (SmsCbHeader.isEmergencyMessage(mMessageIdentifier) || 288 CellBroadcastConfigService.isOperatorDefinedEmergencyId( 289 mMessageIdentifier)) { 290 return R.string.pws_other_message_identifiers; 291 } else { 292 return R.string.cb_other_message_identifiers; 293 } 294 } 295 } 296 297 /** 298 * Return the abbreviated date string for the message delivery time. 299 * @param context the context object 300 * @return a String to use in the broadcast list UI 301 */ getDateString(Context context)302 String getDateString(Context context) { 303 int flags = DateUtils.FORMAT_NO_NOON_MIDNIGHT | DateUtils.FORMAT_SHOW_TIME | 304 DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE | 305 DateUtils.FORMAT_CAP_AMPM; 306 return DateUtils.formatDateTime(context, mDeliveryTime, flags); 307 } 308 309 /** 310 * Return the date string for the message delivery time, suitable for text-to-speech. 311 * @param context the context object 312 * @return a String for populating the list item AccessibilityEvent for TTS 313 */ getSpokenDateString(Context context)314 String getSpokenDateString(Context context) { 315 int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE; 316 return DateUtils.formatDateTime(context, mDeliveryTime, flags); 317 } 318 } 319