1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of 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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under 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.database.sqlite.SQLiteDatabase; 23 import android.database.sqlite.SQLiteException; 24 import android.database.sqlite.SQLiteOpenHelper; 25 import android.provider.Telephony; 26 import android.telephony.SmsCbCmasInfo; 27 import android.telephony.SmsCbEtwsInfo; 28 import android.telephony.SmsCbMessage; 29 import android.util.Log; 30 31 import com.android.internal.telephony.gsm.SmsCbConstants; 32 33 /** 34 * Open, create, and upgrade the cell broadcast SQLite database. Previously an inner class of 35 * {@code CellBroadcastDatabase}, this is now a top-level class. The column definitions in 36 * {@code CellBroadcastDatabase} have been moved to {@link Telephony.CellBroadcasts} in the 37 * framework, to simplify access to this database from third-party apps. 38 */ 39 public class CellBroadcastDatabaseHelper extends SQLiteOpenHelper { 40 41 private static final String TAG = "CellBroadcastDatabaseHelper"; 42 43 static final String DATABASE_NAME = "cell_broadcasts.db"; 44 static final String TABLE_NAME = "broadcasts"; 45 46 /** Temporary table for upgrading the database version. */ 47 static final String TEMP_TABLE_NAME = "old_broadcasts"; 48 49 /** 50 * Database version 1: initial version 51 * Database version 2-9: (reserved for OEM database customization) 52 * Database version 10: adds ETWS and CMAS columns and CDMA support 53 * Database version 11: adds delivery time index 54 */ 55 static final int DATABASE_VERSION = 11; 56 57 CellBroadcastDatabaseHelper(Context context) { 58 super(context, DATABASE_NAME, null, DATABASE_VERSION); 59 } 60 61 @Override 62 public void onCreate(SQLiteDatabase db) { 63 db.execSQL("CREATE TABLE " + TABLE_NAME + " (" 64 + Telephony.CellBroadcasts._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," 65 + Telephony.CellBroadcasts.GEOGRAPHICAL_SCOPE + " INTEGER," 66 + Telephony.CellBroadcasts.PLMN + " TEXT," 67 + Telephony.CellBroadcasts.LAC + " INTEGER," 68 + Telephony.CellBroadcasts.CID + " INTEGER," 69 + Telephony.CellBroadcasts.SERIAL_NUMBER + " INTEGER," 70 + Telephony.CellBroadcasts.SERVICE_CATEGORY + " INTEGER," 71 + Telephony.CellBroadcasts.LANGUAGE_CODE + " TEXT," 72 + Telephony.CellBroadcasts.MESSAGE_BODY + " TEXT," 73 + Telephony.CellBroadcasts.DELIVERY_TIME + " INTEGER," 74 + Telephony.CellBroadcasts.MESSAGE_READ + " INTEGER," 75 + Telephony.CellBroadcasts.MESSAGE_FORMAT + " INTEGER," 76 + Telephony.CellBroadcasts.MESSAGE_PRIORITY + " INTEGER," 77 + Telephony.CellBroadcasts.ETWS_WARNING_TYPE + " INTEGER," 78 + Telephony.CellBroadcasts.CMAS_MESSAGE_CLASS + " INTEGER," 79 + Telephony.CellBroadcasts.CMAS_CATEGORY + " INTEGER," 80 + Telephony.CellBroadcasts.CMAS_RESPONSE_TYPE + " INTEGER," 81 + Telephony.CellBroadcasts.CMAS_SEVERITY + " INTEGER," 82 + Telephony.CellBroadcasts.CMAS_URGENCY + " INTEGER," 83 + Telephony.CellBroadcasts.CMAS_CERTAINTY + " INTEGER);"); 84 85 createDeliveryTimeIndex(db); 86 } 87 88 private void createDeliveryTimeIndex(SQLiteDatabase db) { 89 db.execSQL("CREATE INDEX IF NOT EXISTS deliveryTimeIndex ON " + TABLE_NAME 90 + " (" + Telephony.CellBroadcasts.DELIVERY_TIME + ");"); 91 } 92 93 /** Columns to copy on database upgrade. */ 94 private static final String[] COLUMNS_V1 = { 95 Telephony.CellBroadcasts.GEOGRAPHICAL_SCOPE, 96 Telephony.CellBroadcasts.SERIAL_NUMBER, 97 Telephony.CellBroadcasts.V1_MESSAGE_CODE, 98 Telephony.CellBroadcasts.V1_MESSAGE_IDENTIFIER, 99 Telephony.CellBroadcasts.LANGUAGE_CODE, 100 Telephony.CellBroadcasts.MESSAGE_BODY, 101 Telephony.CellBroadcasts.DELIVERY_TIME, 102 Telephony.CellBroadcasts.MESSAGE_READ, 103 }; 104 105 private static final int COLUMN_V1_GEOGRAPHICAL_SCOPE = 0; 106 private static final int COLUMN_V1_SERIAL_NUMBER = 1; 107 private static final int COLUMN_V1_MESSAGE_CODE = 2; 108 private static final int COLUMN_V1_MESSAGE_IDENTIFIER = 3; 109 private static final int COLUMN_V1_LANGUAGE_CODE = 4; 110 private static final int COLUMN_V1_MESSAGE_BODY = 5; 111 private static final int COLUMN_V1_DELIVERY_TIME = 6; 112 private static final int COLUMN_V1_MESSAGE_READ = 7; 113 114 @Override 115 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 116 if (oldVersion == newVersion) { 117 return; 118 } 119 // always log database upgrade 120 log("Upgrading DB from version " + oldVersion + " to " + newVersion); 121 122 // Upgrade from V1 to V10 123 if (oldVersion == 1) { 124 db.beginTransaction(); 125 try { 126 // Step 1: rename original table 127 db.execSQL("DROP TABLE IF EXISTS " + TEMP_TABLE_NAME); 128 db.execSQL("ALTER TABLE " + TABLE_NAME + " RENAME TO " + TEMP_TABLE_NAME); 129 130 // Step 2: create new table and indices 131 onCreate(db); 132 133 // Step 3: copy each message into the new table 134 Cursor cursor = db.query(TEMP_TABLE_NAME, COLUMNS_V1, null, null, null, null, 135 null); 136 try { 137 while (cursor.moveToNext()) { 138 upgradeMessageV1ToV2(db, cursor); 139 } 140 } finally { 141 cursor.close(); 142 } 143 144 // Step 4: drop the original table and commit transaction 145 db.execSQL("DROP TABLE " + TEMP_TABLE_NAME); 146 db.setTransactionSuccessful(); 147 } finally { 148 db.endTransaction(); 149 } 150 oldVersion = 10; // skip to version 10 151 } 152 153 // Note to OEMs: if you have customized the database schema since V1, you will need to 154 // add your own code here to convert from your version to version 10. 155 if (oldVersion < 10) { 156 throw new SQLiteException("CellBroadcastDatabase doesn't know how to upgrade " 157 + " DB version " + oldVersion + " (customized by OEM?)"); 158 } 159 160 if (oldVersion == 10) { 161 createDeliveryTimeIndex(db); 162 oldVersion++; 163 } 164 } 165 166 /** 167 * Upgrades a single broadcast message from version 1 to version 2. 168 */ 169 private static void upgradeMessageV1ToV2(SQLiteDatabase db, Cursor cursor) { 170 int geographicalScope = cursor.getInt(COLUMN_V1_GEOGRAPHICAL_SCOPE); 171 int updateNumber = cursor.getInt(COLUMN_V1_SERIAL_NUMBER); 172 int messageCode = cursor.getInt(COLUMN_V1_MESSAGE_CODE); 173 int messageId = cursor.getInt(COLUMN_V1_MESSAGE_IDENTIFIER); 174 String languageCode = cursor.getString(COLUMN_V1_LANGUAGE_CODE); 175 String messageBody = cursor.getString(COLUMN_V1_MESSAGE_BODY); 176 long deliveryTime = cursor.getLong(COLUMN_V1_DELIVERY_TIME); 177 boolean isRead = (cursor.getInt(COLUMN_V1_MESSAGE_READ) != 0); 178 179 int serialNumber = ((geographicalScope & 0x03) << 14) 180 | ((messageCode & 0x3ff) << 4) | (updateNumber & 0x0f); 181 182 ContentValues cv = new ContentValues(16); 183 cv.put(Telephony.CellBroadcasts.GEOGRAPHICAL_SCOPE, geographicalScope); 184 cv.put(Telephony.CellBroadcasts.SERIAL_NUMBER, serialNumber); 185 cv.put(Telephony.CellBroadcasts.SERVICE_CATEGORY, messageId); 186 cv.put(Telephony.CellBroadcasts.LANGUAGE_CODE, languageCode); 187 cv.put(Telephony.CellBroadcasts.MESSAGE_BODY, messageBody); 188 cv.put(Telephony.CellBroadcasts.DELIVERY_TIME, deliveryTime); 189 cv.put(Telephony.CellBroadcasts.MESSAGE_READ, isRead); 190 cv.put(Telephony.CellBroadcasts.MESSAGE_FORMAT, SmsCbMessage.MESSAGE_FORMAT_3GPP); 191 192 int etwsWarningType = SmsCbEtwsInfo.ETWS_WARNING_TYPE_UNKNOWN; 193 int cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_UNKNOWN; 194 int cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN; 195 int cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN; 196 int cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN; 197 switch (messageId) { 198 case SmsCbConstants.MESSAGE_ID_ETWS_EARTHQUAKE_WARNING: 199 etwsWarningType = SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE; 200 break; 201 202 case SmsCbConstants.MESSAGE_ID_ETWS_TSUNAMI_WARNING: 203 etwsWarningType = SmsCbEtwsInfo.ETWS_WARNING_TYPE_TSUNAMI; 204 break; 205 206 case SmsCbConstants.MESSAGE_ID_ETWS_EARTHQUAKE_AND_TSUNAMI_WARNING: 207 etwsWarningType = SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE_AND_TSUNAMI; 208 break; 209 210 case SmsCbConstants.MESSAGE_ID_ETWS_TEST_MESSAGE: 211 etwsWarningType = SmsCbEtwsInfo.ETWS_WARNING_TYPE_TEST_MESSAGE; 212 break; 213 214 case SmsCbConstants.MESSAGE_ID_ETWS_OTHER_EMERGENCY_TYPE: 215 etwsWarningType = SmsCbEtwsInfo.ETWS_WARNING_TYPE_OTHER_EMERGENCY; 216 break; 217 218 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL: 219 cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT; 220 break; 221 222 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED: 223 cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT; 224 cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_EXTREME; 225 cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE; 226 cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED; 227 break; 228 229 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY: 230 cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT; 231 cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_EXTREME; 232 cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE; 233 cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY; 234 break; 235 236 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED: 237 cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT; 238 cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_EXTREME; 239 cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_EXPECTED; 240 cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED; 241 break; 242 243 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY: 244 cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT; 245 cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_EXTREME; 246 cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_EXPECTED; 247 cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY; 248 break; 249 250 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED: 251 cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT; 252 cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_SEVERE; 253 cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE; 254 cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED; 255 break; 256 257 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY: 258 cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT; 259 cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_SEVERE; 260 cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE; 261 cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY; 262 break; 263 264 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED: 265 cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT; 266 cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_SEVERE; 267 cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_EXPECTED; 268 cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED; 269 break; 270 271 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY: 272 cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT; 273 cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_SEVERE; 274 cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_EXPECTED; 275 cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY; 276 break; 277 278 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY: 279 cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY; 280 break; 281 282 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST: 283 cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST; 284 break; 285 286 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXERCISE: 287 cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_CMAS_EXERCISE; 288 break; 289 290 case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE: 291 cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_OPERATOR_DEFINED_USE; 292 break; 293 } 294 295 if (etwsWarningType != SmsCbEtwsInfo.ETWS_WARNING_TYPE_UNKNOWN 296 || cmasMessageClass != SmsCbCmasInfo.CMAS_CLASS_UNKNOWN) { 297 cv.put(Telephony.CellBroadcasts.MESSAGE_PRIORITY, 298 SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY); 299 } else { 300 cv.put(Telephony.CellBroadcasts.MESSAGE_PRIORITY, 301 SmsCbMessage.MESSAGE_PRIORITY_NORMAL); 302 } 303 304 if (etwsWarningType != SmsCbEtwsInfo.ETWS_WARNING_TYPE_UNKNOWN) { 305 cv.put(Telephony.CellBroadcasts.ETWS_WARNING_TYPE, etwsWarningType); 306 } 307 308 if (cmasMessageClass != SmsCbCmasInfo.CMAS_CLASS_UNKNOWN) { 309 cv.put(Telephony.CellBroadcasts.CMAS_MESSAGE_CLASS, cmasMessageClass); 310 } 311 312 if (cmasSeverity != SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN) { 313 cv.put(Telephony.CellBroadcasts.CMAS_SEVERITY, cmasSeverity); 314 } 315 316 if (cmasUrgency != SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN) { 317 cv.put(Telephony.CellBroadcasts.CMAS_URGENCY, cmasUrgency); 318 } 319 320 if (cmasCertainty != SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN) { 321 cv.put(Telephony.CellBroadcasts.CMAS_CERTAINTY, cmasCertainty); 322 } 323 324 db.insert(TABLE_NAME, null, cv); 325 } 326 327 private static void log(String msg) { 328 Log.d(TAG, msg); 329 } 330 } 331