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 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