Merge pi-dr1-dev to aosp-master am: a7712d7cd7
am: 34a4f7a103

Change-Id: Ide8d7bb95120894c415c54e043c47b30e2cadda9
diff --git a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
index 75885bd..b0265fa 100644
--- a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
+++ b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
@@ -16,16 +16,22 @@
 
 package com.android.providers.telephony;
 
+import static android.Manifest.permission.MODIFY_PHONE_STATE;
+
 import android.content.BroadcastReceiver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 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;
 import android.os.storage.StorageManager;
+import android.preference.PreferenceManager;
 import android.provider.BaseColumns;
 import android.provider.Telephony;
 import android.provider.Telephony.Mms;
@@ -37,16 +43,22 @@
 import android.provider.Telephony.Sms;
 import android.provider.Telephony.Threads;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.LocalLog;
 import android.util.Log;
+import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.PhoneFactory;
 import com.google.android.mms.pdu.EncodedStringValue;
 import com.google.android.mms.pdu.PduHeaders;
 
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -233,6 +245,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];
 
@@ -246,12 +259,57 @@
     private final Context mContext;
     private LowStorageMonitor mLowStorageMonitor;
 
+    // SharedPref key used to check if initial create has been done (if onCreate has already been
+    // called once)
+    private static final String INITIAL_CREATE_DONE = "initial_create_done";
 
-    private MmsSmsDatabaseHelper(Context context) {
-        super(context, DATABASE_NAME, null, DATABASE_VERSION);
+    /**
+     * The primary purpose of this DatabaseErrorHandler is to 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();
+            Slog.wtf(TAG, logMsg);
+            PhoneFactory.localLog(TAG, logMsg);
+            sendDbErrorIntents(mContext, true);
+            mDefaultDatabaseErrorHandler.onCorruption(dbObj);
+        }
+    }
+
+    private MmsSmsDatabaseHelper(Context context, MmsSmsDatabaseErrorHandler dbErrHandler) {
+        super(context, DATABASE_NAME, null, DATABASE_VERSION, dbErrHandler);
         mContext = context;
         // Memory optimization - close idle connections after 30s of inactivity
         setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
+        try {
+            PhoneFactory.addLocalLog(TAG, 100);
+        } catch (IllegalArgumentException e) {
+            // ignore
+        }
+    }
+
+    private static void sendDbErrorIntents(Context context, boolean isCorrupted) {
+        Intent intent = new Intent(TelephonyManager.ACTION_MMSSMS_DATABASE_LOST);
+        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        intent.putExtra(TelephonyManager.EXTRA_IS_CORRUPTED, isCorrupted);
+        context.sendBroadcast(intent, MODIFY_PHONE_STATE);
+    }
+
+    private static synchronized MmsSmsDatabaseErrorHandler getDbErrorHandler(Context context) {
+        if (sDbErrorHandler == null) {
+            sDbErrorHandler = new MmsSmsDatabaseErrorHandler(context);
+        }
+        return sDbErrorHandler;
     }
 
     /**
@@ -259,7 +317,9 @@
      */
     /* package */ static synchronized MmsSmsDatabaseHelper getInstanceForDe(Context context) {
         if (sDeInstance == null) {
-            sDeInstance = new MmsSmsDatabaseHelper(ProviderUtil.getDeviceEncryptedContext(context));
+            sDeInstance = new MmsSmsDatabaseHelper(
+                ProviderUtil.getDeviceEncryptedContext(context),
+                getDbErrorHandler(context));
         }
         return sDeInstance;
     }
@@ -272,7 +332,8 @@
         if (sCeInstance == null) {
             if (StorageManager.isFileEncryptedNativeOrEmulated()) {
                 sCeInstance = new MmsSmsDatabaseHelper(
-                    ProviderUtil.getCredentialEncryptedContext(context));
+                    ProviderUtil.getCredentialEncryptedContext(context),
+                    getDbErrorHandler(context));
             } else {
                 sCeInstance = getInstanceForDe(context);
             }
@@ -463,6 +524,17 @@
 
     @Override
     public void onCreate(SQLiteDatabase db) {
+        PhoneFactory.localLog(TAG, "onCreate: Creating all SMS-MMS tables.");
+        if (isInitialCreateDone()) {
+            // this onCreate is called after onCreate was called once initially. The db file
+            // disappeared mysteriously?
+            String logMsg = "onCreate: was already called once earlier";
+            Slog.wtf(TAG, logMsg);
+            PhoneFactory.localLog(TAG, logMsg);
+            sendDbErrorIntents(mContext, false);
+        } else {
+            setInitialCreateDone();
+        }
         createMmsTables(db);
         createSmsTables(db);
         createCommonTables(db);
@@ -472,6 +544,18 @@
         createIndices(db);
     }
 
+    private boolean isInitialCreateDone() {
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+        return sp.getBoolean(INITIAL_CREATE_DONE, false);
+    }
+
+    private void setInitialCreateDone() {
+        SharedPreferences.Editor editor
+                = PreferenceManager.getDefaultSharedPreferences(mContext).edit();
+        editor.putBoolean(INITIAL_CREATE_DONE, true);
+        editor.commit();
+    }
+
     // When upgrading the database we need to populate the words
     // table with the rows out of sms and part.
     private void populateWordsTable(SQLiteDatabase db) {
@@ -1526,6 +1610,8 @@
         }
 
         Log.e(TAG, "Destroying all old data.");
+        PhoneFactory.localLog(TAG, "onUpgrade: Calling dropAll() and onCreate(). Upgrading database from version "
+                        + oldVersion + " to " + currentVersion + "failed.");
         dropAll(db);
         onCreate(db);
     }
@@ -1534,6 +1620,7 @@
         // Clean the database out in order to start over from scratch.
         // We don't need to drop our triggers here because SQLite automatically
         // drops a trigger when its attached database is dropped.
+        PhoneFactory.localLog(TAG, "****DROPPING ALL SMS-MMS TABLES****");
         db.execSQL("DROP TABLE IF EXISTS canonical_addresses");
         db.execSQL("DROP TABLE IF EXISTS threads");
         db.execSQL("DROP TABLE IF EXISTS " + MmsSmsProvider.TABLE_PENDING_MSG);
@@ -1834,10 +1921,13 @@
             boolean hasAutoIncrementAddresses = hasAutoIncrement(db, "canonical_addresses");
             boolean hasAutoIncrementPart = hasAutoIncrement(db, "part");
             boolean hasAutoIncrementPdu = hasAutoIncrement(db, "pdu");
-            Log.d(TAG, "[getWritableDatabase] hasAutoIncrementThreads: " + hasAutoIncrementThreads +
+            String logMsg = "[getWritableDatabase]" +
+                    " hasAutoIncrementThreads: " + hasAutoIncrementThreads +
                     " hasAutoIncrementAddresses: " + hasAutoIncrementAddresses +
                     " hasAutoIncrementPart: " + hasAutoIncrementPart +
-                    " hasAutoIncrementPdu: " + hasAutoIncrementPdu);
+                    " hasAutoIncrementPdu: " + hasAutoIncrementPdu;
+            Log.d(TAG, logMsg);
+            PhoneFactory.localLog(TAG, logMsg);
             boolean autoIncrementThreadsSuccess = true;
             boolean autoIncrementAddressesSuccess = true;
             boolean autoIncrementPartSuccess = true;
@@ -2139,4 +2229,8 @@
                    "   JOIN pdu ON pdu._id=part.mid " +
                    "   WHERE part.ct != 'text/plain' AND part.ct != 'application/smil')");
     }
+
+    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+        writer.println("MmsSmsDatabaseHelper:");
+    }
 }
diff --git a/src/com/android/providers/telephony/MmsSmsProvider.java b/src/com/android/providers/telephony/MmsSmsProvider.java
index 1653cd9..80c6d7a 100644
--- a/src/com/android/providers/telephony/MmsSmsProvider.java
+++ b/src/com/android/providers/telephony/MmsSmsProvider.java
@@ -1392,6 +1392,9 @@
             defaultSmsApp = "None";
         }
         writer.println("Default SMS app: " + defaultSmsApp);
+        if (mOpenHelper != null) {
+            ((MmsSmsDatabaseHelper) mOpenHelper).dump(fd, writer, args);
+        }
     }
 
     @Override