Fix CellBroadcastReceiver to ignore duplicate broadcasts.
Before showing an alert dialog or notification, check to
see if the broadcast message is already in the database.
Bug: 6506785
Change-Id: I3063ef3d5355301e4292437348cf789ff540e0b1
diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertService.java b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertService.java
index 6bf5abb..5161dbb 100644
--- a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertService.java
+++ b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertService.java
@@ -50,6 +50,9 @@
/** Intent extra to indicate a previously unread alert. */
static final String NEW_ALERT_EXTRA = "com.android.cellbroadcastreceiver.NEW_ALERT";
+ /** Intent action to display alert dialog/notification, after verifying the alert is new. */
+ static final String SHOW_NEW_ALERT_ACTION = "cellbroadcastreceiver.SHOW_NEW_ALERT";
+
/** Use the same notification ID for non-emergency alerts. */
static final int NOTIFICATION_ID = 1;
@@ -65,10 +68,11 @@
if (Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION.equals(action) ||
Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION.equals(action)) {
handleCellBroadcastIntent(intent);
+ } else if (SHOW_NEW_ALERT_ACTION.equals(action)) {
+ showNewAlert(intent);
} else {
Log.e(TAG, "Unrecognized intent action: " + action);
}
- stopSelf(); // this service always stops after processing the intent
return START_NOT_STICKY;
}
@@ -93,6 +97,40 @@
return;
}
+ final Intent alertIntent = new Intent(SHOW_NEW_ALERT_ACTION);
+ alertIntent.setClass(this, CellBroadcastAlertService.class);
+ alertIntent.putExtra("message", cbm);
+
+ // write to database on a background thread
+ new CellBroadcastContentProvider.AsyncCellBroadcastTask(getContentResolver())
+ .execute(new CellBroadcastContentProvider.CellBroadcastOperation() {
+ @Override
+ public boolean execute(CellBroadcastContentProvider provider) {
+ if (provider.insertNewBroadcast(cbm)) {
+ // new message, show the alert or notification on UI thread
+ startService(alertIntent);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ });
+ }
+
+ private void showNewAlert(Intent intent) {
+ Bundle extras = intent.getExtras();
+ if (extras == null) {
+ Log.e(TAG, "received SHOW_NEW_ALERT_ACTION with no extras!");
+ return;
+ }
+
+ CellBroadcastMessage cbm = (CellBroadcastMessage) extras.get("message");
+
+ if (cbm == null) {
+ Log.e(TAG, "received SHOW_NEW_ALERT_ACTION with no message extra");
+ return;
+ }
+
if (cbm.isEmergencyAlertMessage() || CellBroadcastConfigService
.isOperatorDefinedEmergencyId(cbm.getServiceCategory())) {
// start alert sound / vibration / TTS and display full-screen alert
@@ -101,15 +139,6 @@
// add notification to the bar
addToNotificationBar(cbm);
}
-
- // write to database on a background thread
- new CellBroadcastContentProvider.AsyncCellBroadcastTask(getContentResolver())
- .execute(new CellBroadcastContentProvider.CellBroadcastOperation() {
- @Override
- public boolean execute(CellBroadcastContentProvider provider) {
- return provider.insertNewBroadcast(cbm);
- }
- });
}
/**
diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastContentProvider.java b/src/com/android/cellbroadcastreceiver/CellBroadcastContentProvider.java
index 7c26c35..f368725 100644
--- a/src/com/android/cellbroadcastreceiver/CellBroadcastContentProvider.java
+++ b/src/com/android/cellbroadcastreceiver/CellBroadcastContentProvider.java
@@ -181,22 +181,72 @@
throw new UnsupportedOperationException("update not supported");
}
+ private static final String QUERY_BY_SERIAL = Telephony.CellBroadcasts.SERIAL_NUMBER + "=?";
+
+ private static final String QUERY_BY_SERIAL_PLMN = QUERY_BY_SERIAL + " AND "
+ + Telephony.CellBroadcasts.PLMN + "=?";
+
+ private static final String QUERY_BY_SERIAL_PLMN_LAC = QUERY_BY_SERIAL_PLMN + " AND "
+ + Telephony.CellBroadcasts.LAC + "=?";
+
+ private static final String QUERY_BY_SERIAL_PLMN_LAC_CID = QUERY_BY_SERIAL_PLMN_LAC + " AND "
+ + Telephony.CellBroadcasts.CID + "=?";
+
+ private static final String[] SELECT_ID_COLUMN = {Telephony.CellBroadcasts._ID};
+
/**
* Internal method to insert a new Cell Broadcast into the database and notify observers.
* @param message the message to insert
- * @return true if the database was updated, false otherwise
+ * @return true if the broadcast is new, false if it's a duplicate broadcast.
*/
boolean insertNewBroadcast(CellBroadcastMessage message) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
ContentValues cv = message.getContentValues();
- long rowId = db.insert(CellBroadcastDatabaseHelper.TABLE_NAME, null, cv);
- if (rowId != -1) {
- return true;
+ // Check for existing alert with same serial number and geo scope
+ String serial = cv.getAsString(Telephony.CellBroadcasts.SERIAL_NUMBER);
+ String plmn = cv.getAsString(Telephony.CellBroadcasts.PLMN);
+ String lac = cv.getAsString(Telephony.CellBroadcasts.LAC);
+ String cid = cv.getAsString(Telephony.CellBroadcasts.CID);
+ String selection;
+ String[] selectionArgs;
+
+ if (plmn != null) {
+ if (lac != null) {
+ if (cid != null) {
+ selection = QUERY_BY_SERIAL_PLMN_LAC_CID;
+ selectionArgs = new String[] {serial, plmn, lac, cid};
+ } else {
+ selection = QUERY_BY_SERIAL_PLMN_LAC;
+ selectionArgs = new String[] {serial, plmn, lac};
+ }
+ } else {
+ selection = QUERY_BY_SERIAL_PLMN;
+ selectionArgs = new String[] {serial, plmn};
+ }
} else {
- Log.e(TAG, "failed to insert new broadcast into database");
+ selection = QUERY_BY_SERIAL;
+ selectionArgs = new String[] {serial};
+ }
+
+ Cursor c = db.query(CellBroadcastDatabaseHelper.TABLE_NAME, SELECT_ID_COLUMN,
+ selection, selectionArgs, null, null, null);
+
+ if (c.getCount() != 0) {
+ Log.d(TAG, "ignoring dup broadcast serial=" + serial + " found " + c.getCount());
return false;
}
+
+ long rowId = db.insert(CellBroadcastDatabaseHelper.TABLE_NAME, null, cv);
+ if (rowId == -1) {
+ Log.e(TAG, "failed to insert new broadcast into database");
+ // Return true on DB write failure because we still want to notify the user.
+ // The CellBroadcastMessage will be passed with the intent, so the message will be
+ // displayed in the emergency alert dialog, or the dialog that is displayed when
+ // the user selects the notification for a non-emergency broadcast, even if the
+ // broadcast could not be written to the database.
+ }
+ return true; // broadcast is not a duplicate
}
/**