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 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 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. */ 77 public int describeContents() { 78 return 0; 79 } 80 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 */ 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 */ 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 */ 145 public void setIsRead(boolean isRead) { 146 mIsRead = isRead; 147 } 148 149 public int getGeographicalScope() { 150 return mGeographicalScope; 151 } 152 153 public int getSerialNumber() { 154 return mSerialNumber; 155 } 156 157 public int getMessageCode() { 158 return mMessageCode; 159 } 160 161 public int getMessageIdentifier() { 162 return mMessageIdentifier; 163 } 164 165 public String getLanguageCode() { 166 return mLanguageCode; 167 } 168 169 public long getDeliveryTime() { 170 return mDeliveryTime; 171 } 172 173 public String getMessageBody() { 174 return mMessageBody; 175 } 176 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 237 public boolean isEtwsEmergencyUserAlert() { 238 return SmsCbHeader.isEtwsMessage(mMessageIdentifier) && 239 SmsCbHeader.isEtwsEmergencyUserAlert(mMessageCode); 240 } 241 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 */ 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 */ 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