In modern Android security architecture and privacy engineering, protecting user data stored across local applications and shared storage directories is governed by the principle of least privilege. In older Android versions, applications frequently requested broad, global file system permissions (such as READ_EXTERNAL_STORAGE) to access user media or documents. Starting with Android 10 and formalized in modern releases through Scoped Storage and the Storage Access Framework (SAF), Android shifted away from broad file-path access in favor of granular Content URI Permission Grants. When an application shares a photo, PDF document, or database file with another application, the operating system assigns temporary, secure URI capability tokens governed by the UriGrantsManagerService. For Android developers and mobile security auditors, inspecting which applications hold active URI tokens is essential. Using the Android Debug Bridge (ADB), engineers can audit granted URI tokens, access modes, and expiration states via dumpsys uri_grants.
Architecture of Android Content URI Permission Grants
When an application shares private files using a FileProvider or delegates access via the system document picker (ACTION_OPEN_DOCUMENT or ACTION_CREATE_DOCUMENT), it transmits a content:// URI accompanied by explicit intent permission flags:
- FLAG_GRANT_READ_URI_PERMISSION: Grants the recipient application temporary permission to read data exposed by the content URI.
- FLAG_GRANT_WRITE_URI_PERMISSION: Grants temporary permission to write or modify data exposed by the URI.
- FLAG_GRANT_PERSISTABLE_URI_PERMISSION: Allows the receiving application to preserve access across device reboots by invoking
ContentResolver.takePersistableUriPermission(). - FLAG_GRANT_PREFIX_URI_PERMISSION: Grants recursive access to all child URIs matching the specified URI path prefix (frequently used for directory tree access via
ACTION_OPEN_DOCUMENT_TREE).
The UriGrantsManagerService maintains an active in-memory table of every granted URI token, binding the source package UID, the recipient package UID, the authority, and granted permission modes. The dumpsys uri_grants command dumps this complete authorization ledger.
Connecting via ADB and Verifying Privileges
Ensure that your target Android device has Developer Options and USB Debugging enabled. Connect the device via USB or wireless ADB, and confirm that the ADB daemon recognizes the device:
adb devices
Confirm that your device serial is displayed with the device state before issuing shell diagnostic commands.
Querying the Complete URI Grants Diagnostics Dump
To inspect the comprehensive ledger of all active URI permission grants across the entire operating system, execute the following command:
adb shell dumpsys uri_grants
The diagnostic dump outputs all active and persistable grants grouped by user profile and content provider authority:
URI GRANTS (dumpsys uri_grants)
-------------------------------------------------------------------------------
User 0:
Granted Uri Permissions:
[com.example.camera.fileprovider]
UriPermission{a1b2c3d: content://com.example.camera.fileprovider/photos/img_104.jpg}
sourcePkg=com.example.camera (uid=10142)
targetPkg=com.example.photoeditor (uid=10185)
mode=READ
persisted=false
[com.android.providers.media.documents]
UriPermission{e4f5a6b: content://com.android.providers.media.documents/document/image%3A1024}
sourcePkg=com.android.providers.media.module (uid=10005)
targetPkg=com.example.chat (uid=10210)
mode=READ|WRITE
persisted=true
persistedTime=2026-11-08 09:14:22
Filtering Grants by Target Application Package
Because system components and media providers maintain numerous grants, isolate permissions granted to a specific application using grep:
adb shell dumpsys uri_grants | grep -A 6 "targetPkg=com.example.myapp"
Key parameters reported in the URI permission token include:
- content URI: The complete authority and path of the granted resource (e.g.,
content://com.example.myapp.fileprovider/cache/report.pdf). - sourcePkg: The owning package providing the file data.
- targetPkg: The consuming package permitted to access the content.
- mode: The granted capability flags (
READ,WRITE, orREAD|WRITE). - persisted: Indicates whether the permission survives application and device restarts.
Auditing Persisted URI Permissions and Memory Leaks
When an application takes persistable URI permissions using takePersistableUriPermission(), Android enforces a hard quota (typically 128 or 512 persisted URIs per application). If an application continuously persists URIs without releasing old ones via releasePersistableUriPermission(), it hits the platform limit, causing subsequent document opens to crash with a SecurityException.
To audit all persisted grants across the system:
adb shell dumpsys uri_grants | grep -B 4 "persisted=true"
Count how many persisted URIs your target application currently holds to ensure it stays well beneath platform quota limits:
adb shell dumpsys uri_grants | grep "targetPkg=com.example.myapp" | grep -c "persisted=true"
Testing Dynamic Permission Revocation
To evaluate how your application handles revoked file access, you can simulate permission revocation. When a user deletes a file or uninstalls the source application, Android revokes active URI grants.
To revoke a specific URI permission programmatically from the terminal using the package manager command interface:
adb shell cmd activity revoke-uri-permission com.example.camera "content://com.example.camera.fileprovider/photos/img_104.jpg"
Observe your application’s error handling to verify that it catches SecurityException cleanly and informs the user rather than crashing ungracefully.
Auditing Storage Access Framework Directory Trees
When an application requests broad folder access via ACTION_OPEN_DOCUMENT_TREE, the granted URI typically contains a /tree/ authority segment with PREFIX mode flags:
adb shell dumpsys uri_grants | grep -A 5 "content://.*tree"
Ensure that third-party applications do not hold unauthorized persistent prefix grants to critical shared directories (such as Download or Documents), validating that Scoped Storage isolation boundaries remain strictly enforced.