Fix race condition when inserting parent directory

If multiple threads are inserting things in the same folder, there
can be multiple simultaneous attempts to insert their parent
directory into the database, all but one of which will fail, with
the failing ones then inserting -1 into the parent id cache.

Also clear the parent id cache when fixing up orphan entries in
the database, since the parent id cache might contain bad data
at that point.

Bug: 30099509
Change-Id: Ic798ad64457ce1b2661cd63c524bd61d93d01e32
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java
index 6234788..203aaae 100644
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -3021,34 +3021,36 @@
                     return 0;
                 }
             }
-            Long cid = mDirectoryCache.get(parentPath);
-            if (cid != null) {
-                if (LOCAL_LOGV) Log.v(TAG, "Returning cached entry for " + parentPath);
-                return cid;
-            }
-
-            String selection = MediaStore.MediaColumns.DATA + "=?";
-            String [] selargs = { parentPath };
-            helper.mNumQueries++;
-            Cursor c = db.query("files", sIdOnlyColumn, selection, selargs, null, null, null);
-            try {
-                long id;
-                if (c == null || c.getCount() == 0) {
-                    // parent isn't in the database - so add it
-                    id = insertDirectory(helper, db, parentPath);
-                    if (LOCAL_LOGV) Log.v(TAG, "Inserted " + parentPath);
-                } else {
-                    if (c.getCount() > 1) {
-                        Log.e(TAG, "more than one match for " + parentPath);
-                    }
-                    c.moveToFirst();
-                    id = c.getLong(0);
-                    if (LOCAL_LOGV) Log.v(TAG, "Queried " + parentPath);
+            synchronized(mDirectoryCache) {
+                Long cid = mDirectoryCache.get(parentPath);
+                if (cid != null) {
+                    if (LOCAL_LOGV) Log.v(TAG, "Returning cached entry for " + parentPath);
+                    return cid;
                 }
-                mDirectoryCache.put(parentPath, id);
-                return id;
-            } finally {
-                IoUtils.closeQuietly(c);
+
+                String selection = MediaStore.MediaColumns.DATA + "=?";
+                String [] selargs = { parentPath };
+                helper.mNumQueries++;
+                Cursor c = db.query("files", sIdOnlyColumn, selection, selargs, null, null, null);
+                try {
+                    long id;
+                    if (c == null || c.getCount() == 0) {
+                        // parent isn't in the database - so add it
+                        id = insertDirectory(helper, db, parentPath);
+                        if (LOCAL_LOGV) Log.v(TAG, "Inserted " + parentPath);
+                    } else {
+                        if (c.getCount() > 1) {
+                            Log.e(TAG, "more than one match for " + parentPath);
+                        }
+                        c.moveToFirst();
+                        id = c.getLong(0);
+                        if (LOCAL_LOGV) Log.v(TAG, "Queried " + parentPath);
+                    }
+                    mDirectoryCache.put(parentPath, id);
+                    return id;
+                } finally {
+                    IoUtils.closeQuietly(c);
+                }
             }
         } else {
             return 0;
@@ -3298,7 +3300,9 @@
                     new String[] { Long.toString(rowId) });
         }
         if (format == MtpConstants.FORMAT_ASSOCIATION) {
-            mDirectoryCache.put(path, rowId);
+            synchronized(mDirectoryCache) {
+                mDirectoryCache.put(path, rowId);
+            }
         }
 
         return rowId;
@@ -3701,6 +3705,7 @@
 
         long lastId = -1;
         int numFound = 0, numFixed = 0;
+        boolean firstIteration = true;
         while (true) {
             // Run a query for any entry with an invalid parent id, and try to fix it up.
             // Limit to 500 rows so that the query results fit in the cursor window. Otherwise
@@ -3717,6 +3722,12 @@
                 }
 
                 numFound += c.getCount();
+                if (firstIteration) {
+                    synchronized(mDirectoryCache) {
+                        mDirectoryCache.clear();
+                    }
+                    firstIteration = false;
+                }
                 while (c.moveToNext()) {
                     final String path = c.getString(0);
                     lastId = c.getLong(1);