From 730b3a3a452afb9be38bbef9f7ba36bdbd392b89 Mon Sep 17 00:00:00 2001 From: Ivan Chiang Date: Wed, 21 Aug 2019 16:12:54 +0800 Subject: [PATCH] Allow providers block folders in ACTION_OPEN_DOCUMENT_TREE DocumentsContract - Add new flag FLAG_DIR_BLOCKS_TREE in Document ExternalStorageProvider - Add flag into DocumentInfo for blocking folder Change-Id: Ib557fe99d330788a3bd968bffd43b6658761514f Bug: 32370759 Test: atest DocumentsTest --- api/current.txt | 1 + .../android/provider/DocumentsContract.java | 32 +++++++++++-- .../internal/content/FileSystemProvider.java | 10 ++++ .../ExternalStorageProvider.java | 48 +++++++++++++++++++ 4 files changed, 87 insertions(+), 4 deletions(-) diff --git a/api/current.txt b/api/current.txt index 231495fa02c1d..e8c975880983f 100644 --- a/api/current.txt +++ b/api/current.txt @@ -38234,6 +38234,7 @@ package android.provider { field public static final String COLUMN_MIME_TYPE = "mime_type"; field public static final String COLUMN_SIZE = "_size"; field public static final String COLUMN_SUMMARY = "summary"; + field public static final int FLAG_DIR_BLOCKS_TREE = 32768; // 0x8000 field public static final int FLAG_DIR_PREFERS_GRID = 16; // 0x10 field public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 32; // 0x20 field public static final int FLAG_DIR_SUPPORTS_CREATE = 8; // 0x8 diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index fd81178d2cfbf..eb09930005b52 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -363,15 +363,22 @@ public final class DocumentsContract { *

* Type: INTEGER (int) * - * @see #FLAG_SUPPORTS_WRITE - * @see #FLAG_SUPPORTS_DELETE - * @see #FLAG_SUPPORTS_THUMBNAIL + * @see #FLAG_DIR_BLOCKS_TREE * @see #FLAG_DIR_PREFERS_GRID * @see #FLAG_DIR_PREFERS_LAST_MODIFIED - * @see #FLAG_VIRTUAL_DOCUMENT + * @see #FLAG_DIR_SUPPORTS_CREATE + * @see #FLAG_PARTIAL * @see #FLAG_SUPPORTS_COPY + * @see #FLAG_SUPPORTS_DELETE + * @see #FLAG_SUPPORTS_METADATA * @see #FLAG_SUPPORTS_MOVE * @see #FLAG_SUPPORTS_REMOVE + * @see #FLAG_SUPPORTS_RENAME + * @see #FLAG_SUPPORTS_SETTINGS + * @see #FLAG_SUPPORTS_THUMBNAIL + * @see #FLAG_SUPPORTS_WRITE + * @see #FLAG_VIRTUAL_DOCUMENT + * @see #FLAG_WEB_LINKABLE */ public static final String COLUMN_FLAGS = "flags"; @@ -542,6 +549,23 @@ public final class DocumentsContract { * @see DocumentsContract#getDocumentMetadata(ContentInterface, Uri) */ public static final int FLAG_SUPPORTS_METADATA = 1 << 14; + + /** + * Flag indicating that a document is a directory that wants to block itself + * from being selected when the user launches an {@link Intent#ACTION_OPEN_DOCUMENT_TREE} + * intent. Only valid when {@link #COLUMN_MIME_TYPE} is {@link #MIME_TYPE_DIR}. + *

+ * Note that this flag only applies to the single directory to which it is + * applied. It does not block the user from selecting either a parent or + * child directory during an {@link Intent#ACTION_OPEN_DOCUMENT_TREE} request. + * In particular, the only way to guarantee that a specific directory can never + * be granted via an {@link Intent#ACTION_OPEN_DOCUMENT_TREE} request is to ensure + * that both it and all of its parent directories have set this flag. + * + * @see Intent#ACTION_OPEN_DOCUMENT_TREE + * @see #COLUMN_FLAGS + */ + public static final int FLAG_DIR_BLOCKS_TREE = 1 << 15; } /** diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java index cdb79abbb7ceb..f5708a5c89afa 100644 --- a/core/java/com/android/internal/content/FileSystemProvider.java +++ b/core/java/com/android/internal/content/FileSystemProvider.java @@ -17,6 +17,7 @@ package com.android.internal.content; import android.annotation.CallSuper; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentResolver; import android.content.Intent; @@ -552,6 +553,11 @@ public abstract class FileSystemProvider extends DocumentsProvider { flags |= Document.FLAG_SUPPORTS_DELETE; flags |= Document.FLAG_SUPPORTS_RENAME; flags |= Document.FLAG_SUPPORTS_MOVE; + + if (shouldBlockFromTree(docId)) { + flags |= Document.FLAG_DIR_BLOCKS_TREE; + } + } else { flags |= Document.FLAG_SUPPORTS_WRITE; flags |= Document.FLAG_SUPPORTS_DELETE; @@ -592,6 +598,10 @@ public abstract class FileSystemProvider extends DocumentsProvider { return row; } + protected boolean shouldBlockFromTree(@NonNull String docId) { + return false; + } + protected boolean typeSupportsMetadata(String mimeType) { return MetadataReader.isSupportedMimeType(mimeType) || Document.MIME_TYPE_DIR.equals(mimeType); diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java index 1b27b52f1fa14..48d34ae7ba47f 100644 --- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java @@ -16,6 +16,7 @@ package com.android.externalstorage; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.usage.StorageStatsManager; import android.content.ContentResolver; @@ -298,6 +299,53 @@ public class ExternalStorageProvider extends FileSystemProvider { return projection != null ? projection : DEFAULT_ROOT_PROJECTION; } + /** + * Check that the directory is the root of storage or blocked file from tree. + * + * @param docId the docId of the directory to be checked + * @return true, should be blocked from tree. Otherwise, false. + */ + @Override + protected boolean shouldBlockFromTree(@NonNull String docId) { + try { + final File dir = getFileForDocId(docId, true /* visible */).getCanonicalFile(); + if (!dir.isDirectory()) { + return false; + } + + final String path = dir.getAbsolutePath(); + + // Block Download folder from tree + if (MediaStore.Downloads.isDownloadDir(path)) { + return true; + } + + final ArrayMap roots = new ArrayMap<>(); + + synchronized (mRootsLock) { + roots.putAll(mRoots); + } + + // block root of storage + for (int i = 0; i < roots.size(); i++) { + RootInfo rootInfo = roots.valueAt(i); + // skip home root + if (TextUtils.equals(rootInfo.rootId, ROOT_ID_HOME)) { + continue; + } + + // block the root of storage + if (TextUtils.equals(path, rootInfo.visiblePath.getAbsolutePath())) { + return true; + } + } + return false; + } catch (IOException e) { + throw new IllegalArgumentException( + "Failed to determine if " + docId + " should block from tree " + ": " + e); + } + } + @Override protected String getDocIdForFile(File file) throws FileNotFoundException { return getDocIdForFileMaybeCreate(file, false);