Fix ANR in MediaProvider during database migration

When MediaProvider foreground thread does the database migration, and
main thread is handling MediaUpgradeReceiver#onReceive on main thread,
main thread can run into ANR as MediaUpgradeReceiver will be blocked on
foreground thread for migration.
To avoid this, we run MediaUpgradeReceiver#onReceive on a separate
thread so that main thread is available for handling other MediaService
requests.

Also, added two different locks for internal and external database
migration locks so that these migrations can go in parallel if required.

Bug: 171683776
Test: atest /packages/providers/MediaProvider
Merged-In: I9ea073d4c82301d39ffabb1b3829cc943d495284
Change-Id: I9ea073d4c82301d39ffabb1b3829cc943d495284
diff --git a/src/com/android/providers/media/DatabaseHelper.java b/src/com/android/providers/media/DatabaseHelper.java
index e4a4b37..248101d 100644
--- a/src/com/android/providers/media/DatabaseHelper.java
+++ b/src/com/android/providers/media/DatabaseHelper.java
@@ -135,7 +135,8 @@
      */
     private final ReentrantReadWriteLock mSchemaLock = new ReentrantReadWriteLock();
 
-    private static Object sMigrationLock = new Object();
+    private static Object sMigrationLockInternal = new Object();
+    private static Object sMigrationLockExternal = new Object();
 
     public interface OnSchemaChangeListener {
         public void onSchemaChange(@NonNull String volumeName, int versionFrom, int versionTo,
@@ -370,10 +371,15 @@
     @Override
     public void onOpen(final SQLiteDatabase db) {
         Log.v(TAG, "onOpen() for " + mName);
+
+        tryMigrateFromLegacy(db, mInternal ? sMigrationLockInternal : sMigrationLockExternal);
+    }
+
+    private void tryMigrateFromLegacy(SQLiteDatabase db, Object migrationLock) {
         final File migration = new File(mContext.getFilesDir(), mMigrationFileName);
         // Another thread entering migration block will be blocked until the
         // migration is complete from current thread.
-        synchronized (sMigrationLock) {
+        synchronized (migrationLock) {
             if (!migration.exists()) {
                 Log.v(TAG, "onOpen() finished for " + mName);
                 return;
diff --git a/src/com/android/providers/media/MediaUpgradeReceiver.java b/src/com/android/providers/media/MediaUpgradeReceiver.java
index 5864b78..abb326a 100644
--- a/src/com/android/providers/media/MediaUpgradeReceiver.java
+++ b/src/com/android/providers/media/MediaUpgradeReceiver.java
@@ -23,6 +23,7 @@
 import android.provider.Column;
 import android.util.Log;
 
+import com.android.providers.media.util.ForegroundThread;
 import com.android.providers.media.util.Metrics;
 
 import java.io.File;
@@ -45,7 +46,14 @@
     public void onReceive(Context context, Intent intent) {
         // We are now running with the system up, but no apps started,
         // so can do whatever cleanup after an upgrade that we want.
+        ForegroundThread.getExecutor().execute(() -> {
+            // Run database migration on a separate thread so that main thread
+            // is available for handling other MediaService requests.
+            tryMigratingDatabases(context);
+        });
+    }
 
+    private void tryMigratingDatabases(Context context) {
         // Lookup the last known database version
         SharedPreferences prefs = context.getSharedPreferences(TAG, Context.MODE_PRIVATE);
         int prefVersion = prefs.getInt(PREF_DB_VERSION, 0);