am 411252cd: Merge "Guarantee that db, shm and wal files are deleted together."
* commit '411252cd75a29c7e184ee13cd15e8598851afb64':
Guarantee that db, shm and wal files are deleted together.
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 25265d9..693d20c 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="512">
+ android:versionCode="601">
<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 af9e5f8..3179ba6 100755
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -102,6 +102,7 @@
import java.util.Stack;
import libcore.io.ErrnoException;
+import libcore.io.IoUtils;
import libcore.io.Libcore;
/**
@@ -566,8 +567,6 @@
iFilter.addDataScheme("file");
context.registerReceiver(mUnmountReceiver, iFilter);
- mCaseInsensitivePaths = true;
-
StorageManager storageManager =
(StorageManager)context.getSystemService(Context.STORAGE_SERVICE);
mExternalStoragePaths = storageManager.getVolumePaths();
@@ -1574,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)) {
@@ -1643,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;");
@@ -1750,11 +1753,65 @@
updateBucketNames(db);
}
- if (fromVersion < 512) {
+ // 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;");
+ }
+ }
+
+ if (fromVersion < 601) {
// remove primary key constraint because column time is not necessarily unique
db.execSQL("CREATE TABLE IF NOT EXISTS log_tmp (time DATETIME, message TEXT);");
db.execSQL("DELETE FROM log_tmp;");
- db.execSQL("INSERT INTO log_tmp SELECT time, message FROM log;");
+ db.execSQL("INSERT INTO log_tmp SELECT time, message FROM log order by rowid;");
db.execSQL("DROP TABLE log;");
db.execSQL("ALTER TABLE log_tmp RENAME TO log;");
}
@@ -1769,7 +1826,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" +
@@ -2448,7 +2506,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;
@@ -2574,7 +2635,7 @@
/**
* Ensures there is a file in the _data column of values, if one isn't
- * present a new file is created.
+ * present a new filename is generated. The file itself is not created.
*
* @param initialValues the values passed to insert by the caller
* @return the new values
@@ -2591,9 +2652,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;
}
@@ -2762,11 +2821,7 @@
return cid;
}
- String selection = (mCaseInsensitivePaths ? MediaStore.MediaColumns.DATA
- + " =?1 COLLATE nocase"
- // 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);
@@ -3002,7 +3057,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());
+ }
// make sure date taken time is set
if (mediaType == FileColumns.MEDIA_TYPE_IMAGE
|| mediaType == FileColumns.MEDIA_TYPE_VIDEO) {
@@ -3459,7 +3516,7 @@
ContentValues mediatype = new ContentValues();
mediatype.put("media_type", 0);
int numrows = db.update("files", mediatype,
- "_data >= ? COLLATE nocase AND _data < ? COLLATE nocase",
+ "_data >= ? AND _data < ?",
new String[] { hiddenroot + "/", hiddenroot + "0"});
helper.mNumUpdates += numrows;
ContentResolver res = getContext().getContentResolver();
@@ -3498,7 +3555,7 @@
@Override
public void onMediaScannerConnected() {
Cursor c = mDb.query("files", openFileColumns,
- "_data >= ? COLLATE nocase AND _data < ? COLLATE nocase",
+ "_data >= ? AND _data < ?",
new String[] { mPath + "/", mPath + "0"},
null, null, null);
while (c.moveToNext()) {
@@ -3993,7 +4050,7 @@
// also update bucket_display_name
",bucket_display_name=?6" +
",bucket_id=?7" +
- " WHERE _data >= ?3 COLLATE nocase AND _data < ?4 COLLATE nocase;",
+ " WHERE _data >= ?3 AND _data < ?4;",
bindArgs);
}
@@ -4398,14 +4455,10 @@
WRITE_EXTERNAL_STORAGE, "External path: " + path);
}
- // bypass emulation layer when file is opened for reading, but only
+ // Bypass emulation layer when file is opened for reading, but only
// when opening read-only and we have an exact match.
- if (modeBits == MODE_READ_ONLY && Environment.isExternalStorageEmulated()) {
- final File directFile = new File(Environment.getMediaStorageDirectory(), path
- .substring(sExternalPath.length()));
- if (directFile.exists()) {
- file = directFile;
- }
+ if (modeBits == MODE_READ_ONLY) {
+ file = Environment.maybeTranslateEmulatedPathToInternal(file);
}
} else if (path.startsWith(sCachePath)) {
@@ -4597,6 +4650,9 @@
long rowId = db.insert("album_art", MediaStore.MediaColumns.DATA, values);
if (rowId > 0) {
out = ContentUris.withAppendedId(ALBUMART_URI, rowId);
+ // ensure the parent directory exists
+ String albumart_path = values.getAsString(MediaStore.MediaColumns.DATA);
+ ensureFileExists(albumart_path);
}
} catch (IllegalStateException ex) {
Log.e(TAG, "error creating album thumb file");
@@ -4608,29 +4664,22 @@
// Write out the album art to the output URI, recompresses the given Bitmap
// if necessary, otherwise writes the compressed data.
private void writeAlbumArt(
- boolean need_to_recompress, Uri out, byte[] compressed, Bitmap bm) {
- boolean success = false;
+ boolean need_to_recompress, Uri out, byte[] compressed, Bitmap bm) throws IOException {
+ OutputStream outstream = null;
try {
- OutputStream outstream = getContext().getContentResolver().openOutputStream(out);
+ outstream = getContext().getContentResolver().openOutputStream(out);
if (!need_to_recompress) {
// No need to recompress here, just write out the original
// compressed data here.
outstream.write(compressed);
- success = true;
} else {
- success = bm.compress(Bitmap.CompressFormat.JPEG, 85, outstream);
+ if (!bm.compress(Bitmap.CompressFormat.JPEG, 85, outstream)) {
+ throw new IOException("failed to compress bitmap");
+ }
}
-
- outstream.close();
- } catch (FileNotFoundException ex) {
- Log.e(TAG, "error creating file", ex);
- } catch (IOException ex) {
- Log.e(TAG, "error creating file", ex);
- }
- if (!success) {
- // the thumbnail was not written successfully, delete the entry that refers to it
- getContext().getContentResolver().delete(out, null, null);
+ } finally {
+ IoUtils.closeQuietly(outstream);
}
}
@@ -4709,17 +4758,19 @@
// that could go wrong while generating the thumbnail, and we only want
// to update the database when all steps succeeded.
d.db.beginTransaction();
+ Uri out = null;
+ ParcelFileDescriptor pfd = null;
try {
- Uri out = getAlbumArtOutputUri(d.helper, d.db, d.album_id, d.albumart_uri);
+ out = getAlbumArtOutputUri(d.helper, d.db, d.album_id, d.albumart_uri);
if (out != null) {
writeAlbumArt(need_to_recompress, out, compressed, bm);
getContext().getContentResolver().notifyChange(MEDIA_URI, null);
- ParcelFileDescriptor pfd = openFileHelper(out, "r");
+ pfd = openFileHelper(out, "r");
d.db.setTransactionSuccessful();
return pfd;
}
- } catch (FileNotFoundException ex) {
+ } catch (IOException ex) {
// do nothing, just return null below
} catch (UnsupportedOperationException ex) {
// do nothing, just return null below
@@ -4728,6 +4779,13 @@
if (bm != null) {
bm.recycle();
}
+ if (pfd == null && out != null) {
+ // Thumbnail was not written successfully, delete the entry that refers to it.
+ // Note that this only does something if getAlbumArtOutputUri() reused an
+ // existing entry from the database. If a new entry was created, it will
+ // have been rolled back as part of backing out the transaction.
+ getContext().getContentResolver().delete(out, null, null);
+ }
}
}
return null;
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