CVE-2024-0032 AI-generated analysis
platform/frameworks/base (com.android.internal.content.FileSystemProvider, packages/ExternalStorageProvider) and platform/packages/providers/DownloadProvider (DownloadStorageProvider), storage access framework / DocumentsProvider
Replaces FileSystemProvider's exact-match regex for /Android/{data,obb,sandbox} with an overridable shouldHideDocument(documentId) hook. ExternalStorageProvider implements the hook to hide those directories and their entire subtrees (on non-USB storage) from queryChildDocuments, querySearchDocuments and the open-tree flag. The fix also consolidates the queryChildDocuments variants around a single includeHidden parameter.
Root cause
FileSystemProvider.shouldHide(File) used PATTERN_HIDDEN_PATH, "(?i)^/storage/[^/]+/(?:[0-9]+/)?Android/(?:data|obb|sandbox)$", which matches only the restricted directory itself. queryChildDocuments(parentDocumentId, ...) applied this filter to the queried parent and to each child. A document ID whose path pointed inside one of those directories was therefore not recognised as hidden: the parent check passed and the contents were listed. The check also depended on the absolute path starting with /storage/, not on the canonicalised root-relative path of the document ID. ExternalStorageProvider.shouldBlockFromTree relied on the same shouldHide(dir). FLAG_DIR_BLOCKS_OPEN_DOCUMENT_TREE was only computed for writable directories.
How the fix works
- frameworks/base core/java/com/android/internal/content/FileSystemProvider.java: removes PATTERN_HIDDEN_PATH, shouldHide(File), shouldShow(File), queryChildDocumentsShowAll and the Predicate-based private queryChildDocuments. Adds protected shouldHideDocument(String documentId) (default false) and a final File overload that converts the file via getDocIdForFile(document). Renames shouldBlockFromTree to shouldBlockDirectoryFromTree (default false, may throw FileNotFoundException).
- FileSystemProvider.queryChildDocuments(documentId, projection, sortOrder) now delegates to a new protected queryChildDocuments(documentId, projection, sortOrder, includeHidden=false). queryChildDocumentsForManage is made final and delegates with includeHidden=true. The 4-arg method returns an empty cursor if the parent is not a directory. It also returns an empty cursor if !includeHidden && shouldHideDocument(documentId) holds for the parent, and it skips every child for which shouldHideDocument(file) is true.
- FileSystemProvider.querySearchDocuments: the BFS is rewritten on ArrayDeque with MAX_RESULTS_NUMBER=23. Each dequeued file is skipped (and not expanded) when shouldHideDocument(file) is true. The exclusion set and matchSearchQueryArguments are applied after that.
- FileSystemProvider.includeFile: FLAG_DIR_BLOCKS_OPEN_DOCUMENT_TREE is now set for any directory where shouldBlockDirectoryFromTree(docId) is true, independent of file.canWrite(). Delete, rename and move flags are set for writable files and directories alike.
- packages/ExternalStorageProvider ExternalStorageProvider.java: adds PATTERN_RESTRICTED_ANDROID_SUBTREES = "^Android/(?:data|obb|sandbox)(?:/.+)?" (CASE_INSENSITIVE) and overrides shouldHideDocument(documentId). The override returns false if isOnRemovableUsbStorage(documentId). Otherwise it matches getPathFromDocId(documentId) (canonicalised, no leading or trailing slash) against the pattern, so the directories and all their descendants are hidden. The file also removes its queryChildDocumentsForManage override, which the final base method now handles.
- ExternalStorageProvider.shouldBlockDirectoryFromTree(documentId) keeps the same checks: non-directory returns false, USB returns false, root returns true, Download and Android return true. It now ends with a shouldHideDocument(documentId) check and propagates FileNotFoundException instead of wrapping IOException in IllegalArgumentException. A new helper, isOnRemovableUsbStorage(documentId), checks Root.FLAG_REMOVABLE_USB on getRootFromDocId. getPathFromDocId no longer throws: on IOException during canonicalisation it logs and returns "". The visible parameter is dropped from buildFile and resolveDocId.
- packages/providers/DownloadProvider DownloadStorageProvider.java: removes its own queryChildDocuments and queryChildDocumentsForManage overrides (the latter is now final in the base class). It overrides the protected 4-arg queryChildDocuments(documentId, projection, sortOrder, includeHidden) instead. Raw document IDs delegate to super.queryChildDocuments(..., includeHidden). The former manage flag becomes includeHidden for includeDownloadsFromMediaStore and the DownloadManager query.
Completeness
- Covers listing (queryChildDocuments), recursive search (querySearchDocuments) and tree-selection blocking for Android/data, Android/obb and Android/sandbox and all their subdirectories and files on the integrated shared storage. Matching is case-insensitive and uses the canonicalised root-relative path from the document ID.
- Hiding is explicitly skipped for roots with Root.FLAG_REMOVABLE_USB (isOnRemovableUsbStorage). The old base-class regex applied in queryChildDocuments to any /storage/<vol>/ path, so listing on USB volumes is now less restricted than before.
- The diff adds shouldHideDocument calls only in queryChildDocuments, querySearchDocuments and shouldBlockDirectoryFromTree (used by includeFile's tree flag). It adds none to other document-ID entry points. The index shows FileSystemProvider.openDocument resolving getFileForDocId(documentId) directly. The other entry points' bodies are not shown.
- If getPathFromDocId fails to canonicalise the path, it returns "". shouldHideDocument treats that as not hidden, while shouldBlockDirectoryFromTree treats it as root and blocks it.
- The default shouldHideDocument in FileSystemProvider returns false, so other FileSystemProvider subclasses hide nothing unless they override it. The previous base-class regex did apply to them (for example, DownloadStorageProvider raw documents through the 3-arg super call).
Behavior changes
- A child-listing query (queryChildDocuments) on ExternalStorageProvider for a directory inside Android/data, Android/obb or Android/sandbox (any depth, any case) now returns an empty cursor and logs 'Queried directory ... is hidden'. Such entries are also omitted from parent listings and search results. queryChildDocumentsForManage (includeHidden=true) still returns them.
- FLAG_DIR_BLOCKS_OPEN_DOCUMENT_TREE is now set on blocked directories even when they are not writable. The FLAG_SUPPORTS_DELETE/RENAME/MOVE computation is restructured without changing which flags writable entries receive.
- Hidden-path filtering no longer applies to removable USB roots in ExternalStorageProvider.
- queryChildDocumentsForManage is final in FileSystemProvider, so subclasses must override the protected 4-arg queryChildDocuments. shouldHide and shouldBlockFromTree are replaced by shouldHideDocument and shouldBlockDirectoryFromTree.
- ExternalStorageProvider.shouldBlockDirectoryFromTree now throws FileNotFoundException instead of IllegalArgumentException on resolution failure. getPathFromDocId no longer throws IOException.
- DownloadStorageProvider: raw-document manage queries now pass includeHidden=true to the base class. Previously they called the 3-arg base method, which applied hiding.
Per major
- Android 14: Same fix: frameworks/base 4af5db76f253 and DownloadProvider 5acd646e0cf6 both landed in android-security-14.0.0_r5 (cherry-picks of internal changes). At android-security-14.0.0_r29 the per-major facts report only 2/8 lines of the patched region present. The reason is not shown in the material, so the current state of that region is unverified. The FileSystemProvider imports (ArrayDeque, Queue) and MAX_RESULTS_NUMBER are present at r29.
- Android 15: Not affected: the bulletin does not list Android 15. 8/8 lines of the patched region are present at android-security-15.0.0_r17.
- Android 16: Not affected: the bulletin does not list Android 16. 8/8 lines of the patched region are present at android-security-16.0.0_r8.
- Android 17: Not affected: the bulletin does not list Android 17. 8/8 lines of the patched region are present at android-security-17.0.0_r1.
Unknowns
- The exact trigger and which path form (non-/storage/ mount, path casing, or a subdirectory document ID) was used in the reported issue. The bulletin gives only High/EoP.
- Why android-security-14.0.0_r29 has only 2/8 lines of the patched region present, and which file's region this refers to. The surrounding code at r29 is not shown.
- Contents of the two internal cherry-picked changes (7f5667bf..., cf28630a...) beyond what is in the AOSP commit.
- Whether the Android 12/12L/13 backports differ from this Android 14 change.
- Whether the bulletin's '15-next' entry corresponds to any Android 15 release branch. The per-major facts treat Android 15 as not listed.