summaryrefslogtreecommitdiffstats
path: root/packages/ExternalStorageProvider/src
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2013-10-25 17:12:49 -0700
committerJeff Sharkey <jsharkey@android.com>2013-10-25 17:51:04 -0700
commitdb5ef125007644daa94aeaf1bd8637f4e0095e94 (patch)
tree7b8121682fb4c74cc2794b844c593eaf2173a71c /packages/ExternalStorageProvider/src
parent96c620595bd0585f934b0971b4552c57845e9a78 (diff)
downloadframeworks_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.java93
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);
+ }
+ }
}