diff options
Diffstat (limited to 'src/com/android/cellbroadcastreceiver/CellBroadcastDatabase.java')
-rw-r--r-- | src/com/android/cellbroadcastreceiver/CellBroadcastDatabase.java | 389 |
1 files changed, 361 insertions, 28 deletions
diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastDatabase.java b/src/com/android/cellbroadcastreceiver/CellBroadcastDatabase.java index a27be4b76..0eb48c292 100644 --- a/src/com/android/cellbroadcastreceiver/CellBroadcastDatabase.java +++ b/src/com/android/cellbroadcastreceiver/CellBroadcastDatabase.java @@ -16,21 +16,37 @@ package com.android.cellbroadcastreceiver; +import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.provider.BaseColumns; +import android.telephony.SmsCbCmasInfo; +import android.telephony.SmsCbEtwsInfo; +import android.telephony.SmsCbMessage; +import android.util.Log; + +import com.android.internal.telephony.gsm.SmsCbConstants; public class CellBroadcastDatabase { - private static final String TAG = "CellBroadcastDatabase"; + // package local for efficient access from inner class + static final String TAG = "CellBroadcastDatabase"; private CellBroadcastDatabase() {} static final String DATABASE_NAME = "cell_broadcasts.db"; static final String TABLE_NAME = "broadcasts"; - static final int DATABASE_VERSION = 1; + /** Temporary table for upgrading the database version. */ + static final String TEMP_TABLE_NAME = "old_broadcasts"; + + /** + * Database version 1: initial version + * Database version 2-9: (reserved for OEM database customization) + * Database version 10: adds ETWS and CMAS columns and CDMA support + */ + static final int DATABASE_VERSION = 10; static final class Columns implements BaseColumns { @@ -49,16 +65,43 @@ public class CellBroadcastDatabase { public static final String SERIAL_NUMBER = "serial_number"; /** - * Message code. + * PLMN of broadcast sender. (SERIAL_NUMBER + PLMN + LAC + CID) uniquely identifies a + * broadcast for duplicate detection purposes. + * <P>Type: TEXT</P> + */ + public static final String PLMN = "plmn"; + + /** + * Location Area (GSM) or Service Area (UMTS) of broadcast sender. Unused for CDMA. + * Only included if Geographical Scope of message is not PLMN wide (01). + * <P>Type: INTEGER</P> + */ + public static final String LAC = "lac"; + + /** + * Cell ID of message sender (GSM/UMTS). Unused for CDMA. Only included when the + * Geographical Scope of message is cell wide (00 or 11). * <P>Type: INTEGER</P> */ - public static final String MESSAGE_CODE = "message_code"; + public static final String CID = "cid"; /** - * Message identifier. + * Message code (OBSOLETE: merged into SERIAL_NUMBER). * <P>Type: INTEGER</P> */ - public static final String MESSAGE_IDENTIFIER = "message_id"; + public static final String V1_MESSAGE_CODE = "message_code"; + + /** + * Message identifier (OBSOLETE: renamed to SERVICE_CATEGORY). + * <P>Type: INTEGER</P> + */ + public static final String V1_MESSAGE_IDENTIFIER = "message_id"; + + /** + * Service category (GSM/UMTS message identifier, CDMA service category). + * <P>Type: INTEGER</P> + */ + public static final String SERVICE_CATEGORY = "service_category"; /** * Message language code. @@ -85,18 +128,83 @@ public class CellBroadcastDatabase { public static final String MESSAGE_READ = "read"; /** + * Message format (3GPP or 3GPP2). + * <P>Type: INTEGER</P> + */ + public static final String MESSAGE_FORMAT = "format"; + + /** + * Message priority (including emergency). + * <P>Type: INTEGER</P> + */ + public static final String MESSAGE_PRIORITY = "priority"; + + /** + * ETWS warning type (ETWS alerts only). + * <P>Type: INTEGER</P> + */ + public static final String ETWS_WARNING_TYPE = "etws_warning_type"; + + /** + * CMAS message class (CMAS alerts only). + * <P>Type: INTEGER</P> + */ + public static final String CMAS_MESSAGE_CLASS = "cmas_message_class"; + + /** + * CMAS category (CMAS alerts only). + * <P>Type: INTEGER</P> + */ + public static final String CMAS_CATEGORY = "cmas_category"; + + /** + * CMAS response type (CMAS alerts only). + * <P>Type: INTEGER</P> + */ + public static final String CMAS_RESPONSE_TYPE = "cmas_response_type"; + + /** + * CMAS severity (CMAS alerts only). + * <P>Type: INTEGER</P> + */ + public static final String CMAS_SEVERITY = "cmas_severity"; + + /** + * CMAS urgency (CMAS alerts only). + * <P>Type: INTEGER</P> + */ + public static final String CMAS_URGENCY = "cmas_urgency"; + + /** + * CMAS certainty (CMAS alerts only). + * <P>Type: INTEGER</P> + */ + public static final String CMAS_CERTAINTY = "cmas_certainty"; + + /** * Query for list view adapter. */ static final String[] QUERY_COLUMNS = { _ID, GEOGRAPHICAL_SCOPE, + PLMN, + LAC, + CID, SERIAL_NUMBER, - MESSAGE_CODE, - MESSAGE_IDENTIFIER, + SERVICE_CATEGORY, LANGUAGE_CODE, MESSAGE_BODY, DELIVERY_TIME, MESSAGE_READ, + MESSAGE_FORMAT, + MESSAGE_PRIORITY, + ETWS_WARNING_TYPE, + CMAS_MESSAGE_CLASS, + CMAS_CATEGORY, + CMAS_RESPONSE_TYPE, + CMAS_SEVERITY, + CMAS_URGENCY, + CMAS_CERTAINTY }; } @@ -104,13 +212,24 @@ public class CellBroadcastDatabase { static final int COLUMN_ID = 0; static final int COLUMN_GEOGRAPHICAL_SCOPE = 1; - static final int COLUMN_SERIAL_NUMBER = 2; - static final int COLUMN_MESSAGE_CODE = 3; - static final int COLUMN_MESSAGE_IDENTIFIER = 4; - static final int COLUMN_LANGUAGE_CODE = 5; - static final int COLUMN_MESSAGE_BODY = 6; - static final int COLUMN_DELIVERY_TIME = 7; - static final int COLUMN_MESSAGE_READ = 8; + static final int COLUMN_PLMN = 2; + static final int COLUMN_LAC = 3; + static final int COLUMN_CID = 4; + static final int COLUMN_SERIAL_NUMBER = 5; + static final int COLUMN_SERVICE_CATEGORY = 6; // was COLUMN_MESSAGE_IDENTIFIER + static final int COLUMN_LANGUAGE_CODE = 7; + static final int COLUMN_MESSAGE_BODY = 8; + static final int COLUMN_DELIVERY_TIME = 9; + static final int COLUMN_MESSAGE_READ = 10; + static final int COLUMN_MESSAGE_FORMAT = 11; + static final int COLUMN_MESSAGE_PRIORITY = 12; + static final int COLUMN_ETWS_WARNING_TYPE = 13; + static final int COLUMN_CMAS_MESSAGE_CLASS = 14; + static final int COLUMN_CMAS_CATEGORY = 15; + static final int COLUMN_CMAS_RESPONSE_TYPE = 16; + static final int COLUMN_CMAS_SEVERITY = 17; + static final int COLUMN_CMAS_URGENCY = 18; + static final int COLUMN_CMAS_CERTAINTY = 19; static class DatabaseHelper extends SQLiteOpenHelper { @@ -123,28 +242,242 @@ public class CellBroadcastDatabase { db.execSQL("CREATE TABLE " + TABLE_NAME + " (" + Columns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + Columns.GEOGRAPHICAL_SCOPE + " INTEGER," + + Columns.PLMN + " TEXT," + + Columns.LAC + " INTEGER," + + Columns.CID + " INTEGER," + Columns.SERIAL_NUMBER + " INTEGER," - + Columns.MESSAGE_CODE + " INTEGER," - + Columns.MESSAGE_IDENTIFIER + " INTEGER," + + Columns.SERVICE_CATEGORY + " INTEGER," + Columns.LANGUAGE_CODE + " TEXT," + Columns.MESSAGE_BODY + " TEXT," + Columns.DELIVERY_TIME + " INTEGER," - + Columns.MESSAGE_READ + " INTEGER);"); + + Columns.MESSAGE_READ + " INTEGER," + + Columns.MESSAGE_FORMAT + " INTEGER," + + Columns.MESSAGE_PRIORITY + " INTEGER," + + Columns.ETWS_WARNING_TYPE + " INTEGER," + + Columns.CMAS_MESSAGE_CLASS + " INTEGER," + + Columns.CMAS_CATEGORY + " INTEGER," + + Columns.CMAS_RESPONSE_TYPE + " INTEGER," + + Columns.CMAS_SEVERITY + " INTEGER," + + Columns.CMAS_URGENCY + " INTEGER," + + Columns.CMAS_CERTAINTY + " INTEGER);"); } + /** Columns to copy on database upgrade. */ + private static final String[] COLUMNS_V1 = { + Columns.GEOGRAPHICAL_SCOPE, + Columns.SERIAL_NUMBER, + Columns.V1_MESSAGE_CODE, + Columns.V1_MESSAGE_IDENTIFIER, + Columns.LANGUAGE_CODE, + Columns.MESSAGE_BODY, + Columns.DELIVERY_TIME, + Columns.MESSAGE_READ, + }; + + private static final int COLUMN_V1_GEOGRAPHICAL_SCOPE = 0; + private static final int COLUMN_V1_SERIAL_NUMBER = 1; + private static final int COLUMN_V1_MESSAGE_CODE = 2; + private static final int COLUMN_V1_MESSAGE_IDENTIFIER = 3; + private static final int COLUMN_V1_LANGUAGE_CODE = 4; + private static final int COLUMN_V1_MESSAGE_BODY = 5; + private static final int COLUMN_V1_DELIVERY_TIME = 6; + private static final int COLUMN_V1_MESSAGE_READ = 7; + @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - // ignored for now + if (oldVersion == newVersion) { + return; + } + Log.i(TAG, "Upgrading DB from version " + oldVersion + " to " + newVersion); + + if (oldVersion == 1) { + db.beginTransaction(); + try { + // Step 1: rename original table + db.execSQL("DROP TABLE IF EXISTS " + TEMP_TABLE_NAME); + db.execSQL("ALTER TABLE " + TABLE_NAME + " RENAME TO " + TEMP_TABLE_NAME); + + // Step 2: create new table and indices + onCreate(db); + + // Step 3: copy each message into the new table + Cursor cursor = db.query(TEMP_TABLE_NAME, COLUMNS_V1, null, null, null, null, + null); + try { + while (cursor.moveToNext()) { + upgradeMessageV1ToV2(db, cursor); + } + } finally { + cursor.close(); + } + + // Step 4: drop the original table and commit transaction + db.execSQL("DROP TABLE " + TEMP_TABLE_NAME); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + oldVersion = 2; + } } - } - /** - * Returns a Cursor for the list view adapter, in reverse chronological order. - * @param db an open readable database - * @return the cursor for the list view adapter - */ - static Cursor getCursor(SQLiteDatabase db) { - return db.query(false, TABLE_NAME, Columns.QUERY_COLUMNS, - null, null, null, null, Columns.DELIVERY_TIME + " DESC", null); + /** + * Upgrades a single broadcast message from version 1 to version 2. + */ + private static void upgradeMessageV1ToV2(SQLiteDatabase db, Cursor cursor) { + int geographicalScope = cursor.getInt(COLUMN_V1_GEOGRAPHICAL_SCOPE); + int updateNumber = cursor.getInt(COLUMN_V1_SERIAL_NUMBER); + int messageCode = cursor.getInt(COLUMN_V1_MESSAGE_CODE); + int messageId = cursor.getInt(COLUMN_V1_MESSAGE_IDENTIFIER); + String languageCode = cursor.getString(COLUMN_V1_LANGUAGE_CODE); + String messageBody = cursor.getString(COLUMN_V1_MESSAGE_BODY); + long deliveryTime = cursor.getLong(COLUMN_V1_DELIVERY_TIME); + boolean isRead = (cursor.getInt(COLUMN_V1_MESSAGE_READ) != 0); + + int serialNumber = ((geographicalScope & 0x03) << 14) + | ((messageCode & 0x3ff) << 4) | (updateNumber & 0x0f); + + ContentValues cv = new ContentValues(16); + cv.put(Columns.GEOGRAPHICAL_SCOPE, geographicalScope); + cv.put(Columns.SERIAL_NUMBER, serialNumber); + cv.put(Columns.SERVICE_CATEGORY, messageId); + cv.put(Columns.LANGUAGE_CODE, languageCode); + cv.put(Columns.MESSAGE_BODY, messageBody); + cv.put(Columns.DELIVERY_TIME, deliveryTime); + cv.put(Columns.MESSAGE_READ, isRead); + cv.put(Columns.MESSAGE_FORMAT, SmsCbMessage.MESSAGE_FORMAT_3GPP); + + int etwsWarningType = SmsCbEtwsInfo.ETWS_WARNING_TYPE_UNKNOWN; + int cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_UNKNOWN; + int cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN; + int cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN; + int cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN; + switch (messageId) { + case SmsCbConstants.MESSAGE_ID_ETWS_EARTHQUAKE_WARNING: + etwsWarningType = SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE; + break; + + case SmsCbConstants.MESSAGE_ID_ETWS_TSUNAMI_WARNING: + etwsWarningType = SmsCbEtwsInfo.ETWS_WARNING_TYPE_TSUNAMI; + break; + + case SmsCbConstants.MESSAGE_ID_ETWS_EARTHQUAKE_AND_TSUNAMI_WARNING: + etwsWarningType = SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE_AND_TSUNAMI; + break; + + case SmsCbConstants.MESSAGE_ID_ETWS_TEST_MESSAGE: + etwsWarningType = SmsCbEtwsInfo.ETWS_WARNING_TYPE_TEST_MESSAGE; + break; + + case SmsCbConstants.MESSAGE_ID_ETWS_OTHER_EMERGENCY_TYPE: + etwsWarningType = SmsCbEtwsInfo.ETWS_WARNING_TYPE_OTHER_EMERGENCY; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT; + cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_EXTREME; + cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE; + cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT; + cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_EXTREME; + cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE; + cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT; + cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_EXTREME; + cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_EXPECTED; + cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT; + cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_EXTREME; + cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_EXPECTED; + cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT; + cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_SEVERE; + cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE; + cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT; + cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_SEVERE; + cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE; + cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT; + cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_SEVERE; + cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_EXPECTED; + cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT; + cmasSeverity = SmsCbCmasInfo.CMAS_SEVERITY_SEVERE; + cmasUrgency = SmsCbCmasInfo.CMAS_URGENCY_EXPECTED; + cmasCertainty = SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXERCISE: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_CMAS_EXERCISE; + break; + + case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE: + cmasMessageClass = SmsCbCmasInfo.CMAS_CLASS_OPERATOR_DEFINED_USE; + break; + } + + if (etwsWarningType != SmsCbEtwsInfo.ETWS_WARNING_TYPE_UNKNOWN + || cmasMessageClass != SmsCbCmasInfo.CMAS_CLASS_UNKNOWN) { + cv.put(Columns.MESSAGE_PRIORITY, SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY); + } else { + cv.put(Columns.MESSAGE_PRIORITY, SmsCbMessage.MESSAGE_PRIORITY_NORMAL); + } + + if (etwsWarningType != SmsCbEtwsInfo.ETWS_WARNING_TYPE_UNKNOWN) { + cv.put(Columns.ETWS_WARNING_TYPE, etwsWarningType); + } + + if (cmasMessageClass != SmsCbCmasInfo.CMAS_CLASS_UNKNOWN) { + cv.put(Columns.CMAS_MESSAGE_CLASS, cmasMessageClass); + } + + if (cmasSeverity != SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN) { + cv.put(Columns.CMAS_SEVERITY, cmasSeverity); + } + + if (cmasUrgency != SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN) { + cv.put(Columns.CMAS_URGENCY, cmasUrgency); + } + + if (cmasCertainty != SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN) { + cv.put(Columns.CMAS_CERTAINTY, cmasCertainty); + } + + db.insert(TABLE_NAME, null, cv); + } } }
\ No newline at end of file |