Merge "Send protected intent ACTION_SMS_MMS_DB_LOST on mmssms.db corruption or lost mysteriously"
diff --git a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
index f4ae9cc..1dc2106 100644
--- a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
+++ b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
@@ -23,6 +23,8 @@
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
 import android.database.Cursor;
+import android.database.DatabaseErrorHandler;
+import android.database.DefaultDatabaseErrorHandler;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteException;
 import android.database.sqlite.SQLiteOpenHelper;
@@ -239,6 +241,7 @@
 
     private static MmsSmsDatabaseHelper sDeInstance = null;
     private static MmsSmsDatabaseHelper sCeInstance = null;
+    private static MmsSmsDatabaseErrorHandler sDbErrorHandler = null;
 
     private static final String[] BIND_ARGS_NONE = new String[0];
 
@@ -258,8 +261,31 @@
     // cache for INITIAL_CREATE_DONE shared pref so access to it can be avoided when possible
     private static AtomicBoolean sInitialCreateDone = new AtomicBoolean(false);
 
-    private MmsSmsDatabaseHelper(Context context) {
-        super(context, DATABASE_NAME, null, DATABASE_VERSION);
+    /**
+     * The primary purpose of this DatabaseErrorHandler is to broadcast an intent on corruption and
+     * print a Slog.wtf so database corruption can be caught earlier.
+     */
+    private static class MmsSmsDatabaseErrorHandler implements DatabaseErrorHandler {
+        private DefaultDatabaseErrorHandler mDefaultDatabaseErrorHandler
+                = new DefaultDatabaseErrorHandler();
+        private Context mContext;
+
+        MmsSmsDatabaseErrorHandler(Context context) {
+            mContext = context;
+        }
+
+        @Override
+        public void onCorruption(SQLiteDatabase dbObj) {
+            String logMsg = "Corruption reported by sqlite on database: " + dbObj.getPath();
+            localLogWtf(logMsg);
+            sendDbLostIntent(mContext, true);
+            // Let the default error handler take other actions
+            mDefaultDatabaseErrorHandler.onCorruption(dbObj);
+        }
+    }
+
+    private MmsSmsDatabaseHelper(Context context, MmsSmsDatabaseErrorHandler dbErrorHandler) {
+        super(context, DATABASE_NAME, null, DATABASE_VERSION, dbErrorHandler);
         mContext = context;
         // Memory optimization - close idle connections after 30s of inactivity
         setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
@@ -270,12 +296,27 @@
         }
     }
 
+    private static synchronized MmsSmsDatabaseErrorHandler getDbErrorHandler(Context context) {
+        if (sDbErrorHandler == null) {
+            sDbErrorHandler = new MmsSmsDatabaseErrorHandler(context);
+        }
+        return sDbErrorHandler;
+    }
+
+    private static void sendDbLostIntent(Context context, boolean isCorrupted) {
+        // Broadcast ACTION_SMS_MMS_DB_LOST
+        Intent intent = new Intent(Sms.Intents.ACTION_SMS_MMS_DB_LOST);
+        intent.putExtra(Sms.Intents.EXTRA_IS_CORRUPTED, isCorrupted);
+        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        context.sendBroadcast(intent, android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+    }
     /**
      * Returns a singleton helper for the combined MMS and SMS database in device encrypted storage.
      */
     /* package */ static synchronized MmsSmsDatabaseHelper getInstanceForDe(Context context) {
         if (sDeInstance == null) {
-            sDeInstance = new MmsSmsDatabaseHelper(ProviderUtil.getDeviceEncryptedContext(context));
+            Context deContext = ProviderUtil.getDeviceEncryptedContext(context);
+            sDeInstance = new MmsSmsDatabaseHelper(deContext, getDbErrorHandler(deContext));
         }
         return sDeInstance;
     }
@@ -287,8 +328,8 @@
     /* package */ static synchronized MmsSmsDatabaseHelper getInstanceForCe(Context context) {
         if (sCeInstance == null) {
             if (StorageManager.isFileEncryptedNativeOrEmulated()) {
-                sCeInstance = new MmsSmsDatabaseHelper(
-                    ProviderUtil.getCredentialEncryptedContext(context));
+                Context ceContext = ProviderUtil.getCredentialEncryptedContext(context);
+                sCeInstance = new MmsSmsDatabaseHelper(ceContext, getDbErrorHandler(ceContext));
             } else {
                 sCeInstance = getInstanceForDe(context);
             }
@@ -493,6 +534,7 @@
                 // disappeared mysteriously?
                 localLogWtf("onCreate: was already called once earlier");
                 intent.putExtra(Intents.EXTRA_IS_INITIAL_CREATE, false);
+                sendDbLostIntent(mContext, false);
             } else {
                 setInitialCreateDone();
                 intent.putExtra(Intents.EXTRA_IS_INITIAL_CREATE, true);
@@ -509,12 +551,12 @@
         createIndices(db);
     }
 
-    private void localLog(String logMsg) {
+    private static void localLog(String logMsg) {
         Log.d(TAG, logMsg);
         PhoneFactory.localLog(TAG, logMsg);
     }
 
-    private void localLogWtf(String logMsg) {
+    private static void localLogWtf(String logMsg) {
         Slog.wtf(TAG, logMsg);
         PhoneFactory.localLog(TAG, logMsg);
     }