am edfa644d: Merge "Notify change of movePlaylistEntry after transaction ends"

# By Oscar Rydhé
# Via Gerrit Code Review (1) and Henrik Baard (1)
* commit 'edfa644dd785f91949b3cb865e28037bf0acae14':
  Notify change of movePlaylistEntry after transaction ends
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index c7e479c..52daa72 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2,7 +2,7 @@
         package="com.android.providers.media"
         android:sharedUserId="android.media"
         android:sharedUserLabel="@string/uid_label"
-        android:versionCode="511">
+        android:versionCode="600">
         
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java
index 2d1ddc5..42f0cee 100644
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -567,8 +567,6 @@
         iFilter.addDataScheme("file");
         context.registerReceiver(mUnmountReceiver, iFilter);
 
-        mCaseInsensitivePaths = true;
-
         StorageManager storageManager =
                 (StorageManager)context.getSystemService(Context.STORAGE_SERVICE);
         mExternalStoragePaths = storageManager.getVolumePaths();
@@ -1575,6 +1573,8 @@
                     + FileColumns.MEDIA_TYPE_IMAGE + ";");
         }
 
+        // Honeycomb went up to version 307, ICS started at 401
+
         // Database version 401 did not add storage_id to the internal database.
         // We need it there too, so add it in version 402
         if (fromVersion < 401 || (fromVersion == 401 && internal)) {
@@ -1644,6 +1644,8 @@
             db.execSQL("DELETE FROM audio_genres");
         }
 
+        // ICS went out with database version 409, JB started at 500
+
         if (fromVersion < 500) {
             // we're now deleting the file in mediaprovider code, rather than via a trigger
             db.execSQL("DROP TRIGGER IF EXISTS videothumbnails_cleanup;");
@@ -1751,6 +1753,60 @@
             updateBucketNames(db);
         }
 
+        // JB 4.2 went out with database version 511, starting next release with 600
+
+        if (fromVersion < 600) {
+            // modify _data column to be unique and collate nocase. Because this drops the original
+            // table and replaces it with a new one by the same name, we need to also recreate all
+            // indices and triggers that refer to the files table.
+            // Views don't need to be recreated.
+
+            db.execSQL("CREATE TABLE files2 (_id INTEGER PRIMARY KEY AUTOINCREMENT," +
+                    "_data TEXT UNIQUE" +
+                    // the internal filesystem is case-sensitive
+                    (internal ? "," : " COLLATE NOCASE,") +
+                    "_size INTEGER,format INTEGER,parent INTEGER,date_added INTEGER," +
+                    "date_modified INTEGER,mime_type TEXT,title TEXT,description TEXT," +
+                    "_display_name TEXT,picasa_id TEXT,orientation INTEGER,latitude DOUBLE," +
+                    "longitude DOUBLE,datetaken INTEGER,mini_thumb_magic INTEGER,bucket_id TEXT," +
+                    "bucket_display_name TEXT,isprivate INTEGER,title_key TEXT,artist_id INTEGER," +
+                    "album_id INTEGER,composer TEXT,track INTEGER,year INTEGER CHECK(year!=0)," +
+                    "is_ringtone INTEGER,is_music INTEGER,is_alarm INTEGER," +
+                    "is_notification INTEGER,is_podcast INTEGER,album_artist TEXT," +
+                    "duration INTEGER,bookmark INTEGER,artist TEXT,album TEXT,resolution TEXT," +
+                    "tags TEXT,category TEXT,language TEXT,mini_thumb_data TEXT,name TEXT," +
+                    "media_type INTEGER,old_id INTEGER,storage_id INTEGER,is_drm INTEGER," +
+                    "width INTEGER, height INTEGER);");
+
+            // copy data from old table, squashing entries with duplicate _data
+            db.execSQL("INSERT OR REPLACE INTO files2 SELECT * FROM files;");
+            db.execSQL("DROP TABLE files;");
+            db.execSQL("ALTER TABLE files2 RENAME TO files;");
+
+            // recreate indices and triggers
+            db.execSQL("CREATE INDEX album_id_idx ON files(album_id);");
+            db.execSQL("CREATE INDEX artist_id_idx ON files(artist_id);");
+            db.execSQL("CREATE INDEX bucket_index on files(bucket_id,media_type," +
+                    "datetaken, _id);");
+            db.execSQL("CREATE INDEX bucket_name on files(bucket_id,media_type," +
+                    "bucket_display_name);");
+            db.execSQL("CREATE INDEX format_index ON files(format);");
+            db.execSQL("CREATE INDEX media_type_index ON files(media_type);");
+            db.execSQL("CREATE INDEX parent_index ON files(parent);");
+            db.execSQL("CREATE INDEX path_index ON files(_data);");
+            db.execSQL("CREATE INDEX sort_index ON files(datetaken ASC, _id ASC);");
+            db.execSQL("CREATE INDEX title_idx ON files(title);");
+            db.execSQL("CREATE INDEX titlekey_index ON files(title_key);");
+            if (!internal) {
+                db.execSQL("CREATE TRIGGER audio_playlists_cleanup DELETE ON files" +
+                        " WHEN old.media_type=4" +
+                        " BEGIN DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" +
+                        "SELECT _DELETE_FILE(old._data);END;");
+                db.execSQL("CREATE TRIGGER files_cleanup DELETE ON files" +
+                        " BEGIN SELECT _OBJECT_REMOVED(old._id);END;");
+            }
+        }
+
         sanityCheck(db, fromVersion);
         long elapsedSeconds = (SystemClock.currentTimeMicro() - startTime) / 1000000;
         logToDb(db, "Database upgraded from version " + fromVersion + " to " + toVersion
@@ -1761,7 +1817,8 @@
      * Write a persistent diagnostic message to the log table.
      */
     static void logToDb(SQLiteDatabase db, String message) {
-        db.execSQL("INSERT INTO log (time,message) VALUES (strftime('%Y-%m-%d %H:%M:%f','now'),?);",
+        db.execSQL("INSERT OR REPLACE" +
+                " INTO log (time,message) VALUES (strftime('%Y-%m-%d %H:%M:%f','now'),?);",
                 new String[] { message });
         // delete all but the last 500 rows
         db.execSQL("DELETE FROM log WHERE rowid IN" +
@@ -2442,7 +2499,10 @@
                 combine(prependArgs, selectionArgs), groupBy, null, sort, limit);
 
         if (c != null) {
-            c.setNotificationUri(getContext().getContentResolver(), uri);
+            String nonotify = uri.getQueryParameter("nonotify");
+            if (nonotify == null || !nonotify.equals("1")) {
+                c.setNotificationUri(getContext().getContentResolver(), uri);
+            }
         }
 
         return c;
@@ -2587,9 +2647,7 @@
             values = initialValues;
         }
 
-        if (!ensureFileExists(file)) {
-            throw new IllegalStateException("Unable to create new file: " + file);
-        }
+        // we used to create the file here, but now defer this until openFile() is called
         return values;
     }
 
@@ -2758,17 +2816,7 @@
                 return cid;
             }
 
-            // Use "LIKE" instead of "=" on case insensitive file systems so we do a
-            // case insensitive match when looking for parent directory.
-            // TODO: investigate whether a "nocase" constraint on the column and
-            // using "=" would give the same result faster.
-            String selection = (mCaseInsensitivePaths ? MediaStore.MediaColumns.DATA + " LIKE ?1"
-                    // The like above makes it use the index.
-                    // The comparison below makes it correct when the path has wildcard chars
-                    + " AND lower(_data)=lower(?1)"
-                    // search only directories.
-                    + " AND format=" + MtpConstants.FORMAT_ASSOCIATION
-                    : MediaStore.MediaColumns.DATA + "=?");
+            String selection = MediaStore.MediaColumns.DATA + "=?";
             String [] selargs = { parentPath };
             helper.mNumQueries++;
             Cursor c = db.query("files", sIdOnlyColumn, selection, selargs, null, null, null);
@@ -3004,7 +3052,9 @@
                 File file = new File(path);
                 if (file.exists()) {
                     values.put(FileColumns.DATE_MODIFIED, file.lastModified() / 1000);
-                    values.put(FileColumns.SIZE, file.length());
+                    if (!values.containsKey(FileColumns.SIZE)) {
+                        values.put(FileColumns.SIZE, file.length());
+                    }
                 }
             }
 
diff --git a/src/com/android/providers/media/MtpService.java b/src/com/android/providers/media/MtpService.java
index 04033e9..74fd747 100644
--- a/src/com/android/providers/media/MtpService.java
+++ b/src/com/android/providers/media/MtpService.java
@@ -71,13 +71,23 @@
         public void onReceive(Context context, Intent intent) {
             final String action = intent.getAction();
             if (Intent.ACTION_USER_PRESENT.equals(action)) {
-                synchronized (mBinder) {
-                    // Unhide the storage units when the user has unlocked the lockscreen
-                    if (mMtpDisabled) {
-                        addStorageDevicesLocked();
-                        mMtpDisabled = false;
-                    }
-                }
+                // If the media scanner is running, it may currently be calling
+                // sendObjectAdded/Removed, which also synchronizes on mBinder
+                // (and in addition to that, all the native MtpServer methods
+                // lock the same Mutex). If it happens to be in an mtp device
+                // write(), it may block for some time, so process this broadcast
+                // in a thread.
+                new Thread(new Runnable() {
+                    @Override
+                    public void run() {
+                        synchronized (mBinder) {
+                            // Unhide the storage units when the user has unlocked the lockscreen
+                            if (mMtpDisabled) {
+                                addStorageDevicesLocked();
+                                mMtpDisabled = false;
+                            }
+                        }
+                    }}, "addStorageDevices").start();
             }
         }
     };
diff --git a/tools/genfiles/genfiles.sh b/tools/genfiles/genfiles.sh
index 32d2352..2a139a5 100755
--- a/tools/genfiles/genfiles.sh
+++ b/tools/genfiles/genfiles.sh
@@ -139,7 +139,7 @@
 }
 
 echo mkfiles.sh generated. Now run:
-grep sdcard0\/proto mkfiles.sh |sed 's/cat \/storage\/sdcard0\//adb push /' | sed 's/ > .*/ \/storage\/sdcard0/'|sort -u
+grep sdcard0\/proto mkfiles.sh |sed 's/cat \/storage\/sdcard0\//adb push protos\//' | sed 's/ > .*/ \/storage\/sdcard0\//'|sort -u
 echo adb push mkfiles.sh /storage/sdcard0
 echo adb shell sh /storage/sdcard0/mkfiles.sh