Issue
For over a week I try to solve weird problem. When i try to get file uri's using SAF and ACTION_OPEN_DOCUMENT and use takePersistableUriPermission() method on them I get SecurityException :
java.lang.SecurityException: UID 10384 does not have permission to content://com.android.externalstorage.documents/document/primary%3Abig%20mock%20playlist%2FB%C4%85d%C5%BA%20moj%C4%85%20kr%C3%B3low%C4%85.pdf [user 0]; you could obtain access using ACTION_OPEN_DOCUMENT or related APIs
at android.os.Parcel.createException(Parcel.java:1953)
at android.os.Parcel.readException(Parcel.java:1921)
at android.os.Parcel.readException(Parcel.java:1871)
at android.app.IActivityManager$Stub$Proxy.grantUriPermission(IActivityManager.java:4757)
at android.app.ContextImpl.grantUriPermission(ContextImpl.java:2084)
at android.content.ContextWrapper.grantUriPermission(ContextWrapper.java:781)
at android.content.ContextWrapper.grantUriPermission(ContextWrapper.java:781)
at com.example.weddkit20.util.UriHelper$Companion.grantReadPermissionToUri(UriHelper.kt:27)
at com.example.weddkit20.view.songbook.PlaylistActivity$setResultLaunchers$1.onActivityResult(PlaylistActivity.kt:123)
at com.example.weddkit20.view.songbook.PlaylistActivity$setResultLaunchers$1.onActivityResult(PlaylistActivity.kt:41)
at androidx.activity.result.ActivityResultRegistry$1.onStateChanged(ActivityResultRegistry.java:148)
at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:354)
at androidx.lifecycle.LifecycleRegistry.forwardPass(LifecycleRegistry.java:265)
at androidx.lifecycle.LifecycleRegistry.sync(LifecycleRegistry.java:307)
at androidx.lifecycle.LifecycleRegistry.moveToState(LifecycleRegistry.java:148)
at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent(LifecycleRegistry.java:134)
at androidx.lifecycle.ReportFragment.dispatch(ReportFragment.java:68)
at androidx.lifecycle.ReportFragment.dispatch(ReportFragment.java:144)
at androidx.lifecycle.ReportFragment.onStart(ReportFragment.java:109)
at android.app.Fragment.performStart(Fragment.java:2568)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1339)
at android.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1581)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1643)
at android.app.FragmentManagerImpl.dispatchMoveToState(FragmentManager.java:3054)
at android.app.FragmentManagerImpl.dispatchStart(FragmentManager.java:3011)
at android.app.FragmentController.dispatchStart(FragmentController.java:193)
at android.app.Activity.performStart(Activity.java:7487)
at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3454)
at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:180)
at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:165)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:142)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2199)
at android.os.Handler.dispatchMessage(Handler.java:112)
at android.os.Looper.loop(Looper.java:216)
at android.app.ActivityThread.main(ActivityThread.java:7625)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:524)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:987)
Caused by: android.os.RemoteException: Remote stack trace:
at com.android.server.am.ActivityManagerService.checkGrantUriPermissionLocked(ActivityManagerService.java:10930)
at com.android.server.am.ActivityManagerService.grantUriPermissionLocked(ActivityManagerService.java:11001)
at com.android.server.am.ActivityManagerService.grantUriPermission(ActivityManagerService.java:11178)
at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:691)
at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:3648)
The weird thing is I get this error only if I try to load large amount of files (100 or more). If I load smaller amount of files all permissions are granted, uri's are saved in local database and there is no problem in fetching file's content using these uri's (in another activity UI shows content of PDF files). Error clearly says than i should use ACTION_OPEN_DOCUMENT which I actually do.
Code used for loading files from file browser :
PlaylistActivity :
private fun showFileBrowser() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
type = "application/pdf"
addCategory(Intent.CATEGORY_OPENABLE)
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
fileResultLauncher.launch(intent)
}
private fun setResultLaunchers() {
fileResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
Log.d(TAG, "Success callback from file browser fired off!")
val uriList : MutableList<Uri> = arrayListOf()
val uriClipData = result.data!!.clipData
if(uriClipData != null) {
//if user picked multiple files
for(i in 0 until uriClipData.itemCount) { uriList.add(uriClipData.getItemAt(i).uri!!) }
} else {
//if user picked one file
uriList.add(result.data!!.data!!)
}
//granting permissions
uriList.forEach { uri ->
UriHelper.grantReadPermissionToUri(this, uri)
}
//business logic
val songList : List<Song> = uriList.map {
val fullFilename = UriHelper.getFileName(contentResolver, it)
val filename = StringHelper.stripExtensionFromFilename(fullFilename)
val extension = StringHelper.getExtensionFromFilename(fullFilename)
val song = Song(it, filename, extension)
song.isOnPlaylist = false
song
}
availableSongsAdapter.submitList(songList)
createPlaylistAdapter.submitList(arrayListOf())
createPlaylistViewModel.getStoredCloudSongs(this)
}
}
}
UriHelper :
@Throws(Exception::class)
fun grantReadPermissionToUri(context: Context, uri: Uri) {
context.grantUriPermission(
context.packageName,
uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
)
context.contentResolver.takePersistableUriPermission(
uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
}
Any hint what could be wrong will be appreciated. Please help me cuz this problem is driving me crazy :/
Solution
On Android 10 and lower, there is a limit of 128 persisted permission grants: https://issuetracker.google.com/issues/149315521
That limit was raised to 512 for Android 11; I do not know if Android 12 raised it further.
Answered By - CommonsWare