ionic-team / ionic-team/capacitor-camera
[Bug]: Crash SecurityException: No persistable permission grants found in IONCAMROpenPhotoPickerActivity
- Dominant language
- TypeScript
- Stars
- 4
- Forks
- 1
- Avg merge
- 2d 1h
- Merged PRs (30d)
- 2
Description
### Current Behavior
When using the photo picker, the application crashes with a SecurityException during the activity result lifecycle. This happens because the library attempts to take persistable URI permissions (takePersistableUriPermission) on a content URI provided by the system picker, but the intent data doesn't carry the required persistable flag permissions from the OS or the hosting application.
## Log Crash
```
Caused by java.lang.SecurityException: No persistable permission grants found for UID 10184 and Uri content://media/external/file/40736
at android.os.Parcel.createExceptionOrNull(Parcel.java:2456)
at android.os.Parcel.createException(Parcel.java:2440)
at android.os.Parcel.readException(Parcel.java:2423)
at android.os.Parcel.readException(Parcel.java:2365)
at android.app.IUriGrantsManager$Stub$Proxy.takePersistableUriPermission(IUriGrantsManager.java:315)
at android.content.ContentResolver.takePersistableUriPermission(ContentResolver.java:2896)
at io.ionic.libs.ioncameralib.view.IONCAMROpenPhotoPickerActivity.makeUriPermissionPersistable(IONCAMROpenPhotoPickerActivity.kt:117)
at io.ionic.libs.ioncameralib.view.IONCAMROpenPhotoPickerActivity.launchSinglePicker$lambda$1(IONCAMROpenPhotoPickerActivity.kt:34)
at io.ionic.libs.ioncameralib.view.IONCAMROpenPhotoPickerActivity.$r8$lambda$u6b4keYGOB1tfGwipliJ3-5MoFw()
at io.ionic.libs.ioncameralib.view.IONCAMROpenPhotoPickerActivity$$ExternalSyntheticLambda0.onActivityResult(D8$$SyntheticClass)
at androidx.activity.result.ActivityResultRegistry.doDispatch(ActivityResultRegistry.kt:350)
at androidx.activity.result.ActivityResultRegistry.dispatchResult(ActivityResultRegistry.kt:311)
at androidx.activity.ComponentActivity.onActivityResult(ComponentActivity.kt:756)
at android.app.Activity.dispatchActivityResult(Activity.java:8381)
at android.app.ActivityThread.deliverResults(ActivityThread.java:5334)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:5380)
at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:54)
at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2247)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7886)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:568)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1045)
```
### Expected Behavior
The library should either gracefully catch the SecurityException when persisting permissions fails, or check if the URI flags allow persistence before calling the method, allowing the user to select the photo without crashing the app.
### Reproduction
https://github.com/ionic-team/capacitor-camera/tree/main/example-app
### Screenshots / Media
_No response_
### Environment Details
```Shell
OS: Android 12
Installed Dependencies:
@capacitor/android: 8.4.0
@capacitor/cli: 8.4.0
@capacitor/core: 8.4.0
@capacitor/ios: 8.4.0
@capacitor/camera: 8.2.0
```
### Versions Affected
8.2.0
### Platforms Affected
- [ ] iOS
- [x] Android
- [ ] Web
### Notes / Comments
The issue happens in `IONCAMROpenPhotoPickerActivity.kt` on the underlying `ion-android-camera` library inside the `makeUriPermissionPersistable` method. The Android Photo Picker doesn't always grant or require long-term persistence, so calling `takePersistableUriPermission` unconditionally causes the crash.
As a quick fix to prevent the application from crashing, we can wrap that native call in a try-catch block. Please note that while this prevents the crash, the proper long-term solution for the plugin should involve copying the selected files into the application's internal cache directory (`cacheDir`) to guarantee persistent access to the media.
Here is the quick fix to avoid the crash:
```kotlin
private fun makeUriPermissionPersistable(uri: Uri) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
try {
applicationContext.contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
} catch (e: SecurityException) {
Log.w("PhotoPicker", "Failed to take persistable URI permission (skipping to avoid crash): ${e.message}")
}
}
}
```
Contributor guide
Assessment
This issue has not been assessed yet.