diff options
author | Jeff Sharkey <jsharkey@android.com> | 2013-10-25 17:12:49 -0700 |
---|---|---|
committer | Jeff Sharkey <jsharkey@android.com> | 2013-10-25 17:51:04 -0700 |
commit | db5ef125007644daa94aeaf1bd8637f4e0095e94 (patch) | |
tree | 7b8121682fb4c74cc2794b844c593eaf2173a71c /packages/ExternalStorageProvider/src | |
parent | 96c620595bd0585f934b0971b4552c57845e9a78 (diff) | |
download | frameworks_base-db5ef125007644daa94aeaf1bd8637f4e0095e94.zip frameworks_base-db5ef125007644daa94aeaf1bd8637f4e0095e94.tar.gz frameworks_base-db5ef125007644daa94aeaf1bd8637f4e0095e94.tar.bz2 |
Use inotify to update DocumentsUI.
While user is actively looking at a directory, subscribe to inotify
events and notify of content changes to trigger requeries. Reference
count the observers, since multiple cursors are regularly open during
requeries.
Fix leaking cursors on activity rotation; crazy loader ID generation
is no longer needed.
Bug: 10999396
Change-Id: Iddeb08a056fee80c93df8499874705bcd213a1e2
Diffstat (limited to 'packages/ExternalStorageProvider/src')
-rw-r--r-- | packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java | 93 |
1 files changed, 92 insertions, 1 deletions
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java index 65e3eee..559e052 100644 --- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java @@ -16,14 +16,17 @@ package com.android.externalstorage; +import android.content.ContentResolver; import android.content.Context; import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.database.MatrixCursor; import android.database.MatrixCursor.RowBuilder; import android.graphics.Point; +import android.net.Uri; import android.os.CancellationSignal; import android.os.Environment; +import android.os.FileObserver; import android.os.ParcelFileDescriptor; import android.os.storage.StorageManager; import android.os.storage.StorageVolume; @@ -49,6 +52,8 @@ import java.util.Map; public class ExternalStorageProvider extends DocumentsProvider { private static final String TAG = "ExternalStorage"; + private static final boolean LOG_INOTIFY = false; + public static final String AUTHORITY = "com.android.externalstorage.documents"; // docId format: root:path/to/file @@ -83,6 +88,9 @@ public class ExternalStorageProvider extends DocumentsProvider { @GuardedBy("mRootsLock") private HashMap<String, File> mIdToPath; + @GuardedBy("mObservers") + private Map<File, DirectoryObserver> mObservers = Maps.newHashMap(); + @Override public boolean onCreate() { mStorageManager = (StorageManager) getContext().getSystemService(Context.STORAGE_SERVICE); @@ -327,8 +335,9 @@ public class ExternalStorageProvider extends DocumentsProvider { public Cursor queryChildDocuments( String parentDocumentId, String[] projection, String sortOrder) throws FileNotFoundException { - final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection)); final File parent = getFileForDocId(parentDocumentId); + final MatrixCursor result = new DirectoryCursor( + resolveDocumentProjection(projection), parentDocumentId, parent); for (File file : parent.listFiles()) { includeFile(result, null, file); } @@ -431,4 +440,86 @@ public class ExternalStorageProvider extends DocumentsProvider { } return name; } + + private void startObserving(File file, Uri notifyUri) { + synchronized (mObservers) { + DirectoryObserver observer = mObservers.get(file); + if (observer == null) { + observer = new DirectoryObserver( + file, getContext().getContentResolver(), notifyUri); + observer.startWatching(); + mObservers.put(file, observer); + } + observer.mRefCount++; + + if (LOG_INOTIFY) Log.d(TAG, "after start: " + observer); + } + } + + private void stopObserving(File file) { + synchronized (mObservers) { + DirectoryObserver observer = mObservers.get(file); + if (observer == null) return; + + observer.mRefCount--; + if (observer.mRefCount == 0) { + mObservers.remove(file); + observer.stopWatching(); + } + + if (LOG_INOTIFY) Log.d(TAG, "after stop: " + observer); + } + } + + private static class DirectoryObserver extends FileObserver { + private static final int NOTIFY_EVENTS = ATTRIB | CLOSE_WRITE | MOVED_FROM | MOVED_TO + | CREATE | DELETE | DELETE_SELF | MOVE_SELF; + + private final File mFile; + private final ContentResolver mResolver; + private final Uri mNotifyUri; + + private int mRefCount = 0; + + public DirectoryObserver(File file, ContentResolver resolver, Uri notifyUri) { + super(file.getAbsolutePath(), NOTIFY_EVENTS); + mFile = file; + mResolver = resolver; + mNotifyUri = notifyUri; + } + + @Override + public void onEvent(int event, String path) { + if ((event & NOTIFY_EVENTS) != 0) { + if (LOG_INOTIFY) Log.d(TAG, "onEvent() " + event + " at " + path); + mResolver.notifyChange(mNotifyUri, null, false); + } + } + + @Override + public String toString() { + return "DirectoryObserver{file=" + mFile.getAbsolutePath() + ", ref=" + mRefCount + "}"; + } + } + + private class DirectoryCursor extends MatrixCursor { + private final File mFile; + + public DirectoryCursor(String[] columnNames, String docId, File file) { + super(columnNames); + + final Uri notifyUri = DocumentsContract.buildChildDocumentsUri( + AUTHORITY, docId); + setNotificationUri(getContext().getContentResolver(), notifyUri); + + mFile = file; + startObserving(mFile, notifyUri); + } + + @Override + public void close() { + super.close(); + stopObserving(mFile); + } + } } |