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