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.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.provider.Telephony; 25 import 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 */ 40 public 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 */ setSubId(int subId)64 public void setSubId(int subId) { 65 mSubId = subId; 66 } 67 68 /** 69 * get Subscription information 70 * 71 * @hide 72 */ getSubId()73 public int getSubId() { 74 return mSubId; 75 } 76 CellBroadcastMessage(SmsCbMessage message)77 public CellBroadcastMessage(SmsCbMessage message) { 78 mSmsCbMessage = message; 79 mDeliveryTime = System.currentTimeMillis(); 80 mIsRead = false; 81 } 82 CellBroadcastMessage(SmsCbMessage message, long deliveryTime, boolean isRead)83 private CellBroadcastMessage(SmsCbMessage message, long deliveryTime, boolean isRead) { 84 mSmsCbMessage = message; 85 mDeliveryTime = deliveryTime; 86 mIsRead = isRead; 87 } 88 CellBroadcastMessage(Parcel in)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 describeContents()97 public int describeContents() { 98 return 0; 99 } 100 101 @Override writeToParcel(Parcel out, int flags)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 */ createFromCursor(Cursor cursor)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 */ getContentValues()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 */ setIsRead(boolean isRead)296 public void setIsRead(boolean isRead) { 297 mIsRead = isRead; 298 } 299 getLanguageCode()300 public String getLanguageCode() { 301 return mSmsCbMessage.getLanguageCode(); 302 } 303 getServiceCategory()304 public int getServiceCategory() { 305 return mSmsCbMessage.getServiceCategory(); 306 } 307 getDeliveryTime()308 public long getDeliveryTime() { 309 return mDeliveryTime; 310 } 311 getMessageBody()312 public String getMessageBody() { 313 return mSmsCbMessage.getMessageBody(); 314 } 315 isRead()316 public boolean isRead() { 317 return mIsRead; 318 } 319 getSerialNumber()320 public int getSerialNumber() { 321 return mSmsCbMessage.getSerialNumber(); 322 } 323 getCmasWarningInfo()324 public SmsCbCmasInfo getCmasWarningInfo() { 325 return mSmsCbMessage.getCmasWarningInfo(); 326 } 327 getEtwsWarningInfo()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 */ isPublicAlertMessage()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 */ isEmergencyAlertMessage()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 */ isEtwsMessage()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 */ isCmasMessage()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 */ getCmasMessageClass()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 */ isEtwsPopupAlert()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 */ isEtwsEmergencyUserAlert()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 */ isEtwsTestMessage()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 */ getDateString(Context context)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 */ getSpokenDateString(Context context)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