17

I'm using the intent action ACTION_GET_CONTENT.

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(i, 3);

I need to use the URI in onActivityResult for copying the image that a user chooses and compress the copied image.

But I'm getting this error even after taking uri permission using takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION) in onActivityResult. This works well when I use the Intent action ACTION_OPEN_DOCUMENT.

Here is the stack trace:

01-08 01:22:52.581 3838-4425/com.example.wallpaper E/AndroidRuntime: FATAL EXCEPTION: 
AsyncTask Process: com.example.wallpaper, PID: 3838                                                                         
java.lang.RuntimeException: An error occured while executing doInBackground()                                                                              
at android.os.AsyncTask$3.done(AsyncTask.java:304)                                                                              
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)                                                                             
at java.util.concurrent.FutureTask.setException(FutureTask.java:222)                                                                             
at java.util.concurrent.FutureTask.run(FutureTask.java:242)                                                                              
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)                                                                              
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)                                                                              
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)                                                                              
at java.lang.Thread.run(Thread.java:818)                                                                           
Caused by: java.lang.SecurityException: No persistable permission grants     
found for UID 10151 and Uri 0 @ content://media/external/images/media/29                                                                              
at android.os.Parcel.readException(Parcel.java:1546)                                                                              
at android.os.Parcel.readException(Parcel.java:1499)                                                                              
at android.app.ActivityManagerProxy.takePersistableUriPermission(ActivityManagerNative.java:3977)                                                                              
at android.content.ContentResolver.takePersistableUriPermission(ContentResolver.java:1658)                                                                         
at com.example.wallpaper.ImageChooser.getBitmap(ImageChooser.java:249)                                                                              
at com.example.wallpaper.ImageChooser.access$000(ImageChooser.java:110)                                                                              
at com.example.wallpaper.ImageChooser$2.doInBackground(ImageChooser.java:298)                                                                              
at com.example.wallpaper.ImageChooser$2.doInBackground(ImageChooser.java:282)                                                                              
at android.os.AsyncTask$2.call(AsyncTask.java:292)                                                                              
at java.util.concurrent.FutureTask.run(FutureTask.java:237)                                                                              
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)                                                                               
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)                                                                             
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)                                                                           
at java.lang.Thread.run(Thread.java:818) 
Elydasian
  • 2,016
  • 5
  • 23
  • 41
Piyush
  • 1,744
  • 1
  • 15
  • 28

1 Answers1

48

But I'm getting this error even after taking uri permission using takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION) in onActivityResult.

takePersistableUriPermission() is for ACTION_OPEN_DOCUMENT and other actions that are part of the Storage Access Framework, not for ACTION_GET_CONTENT.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • i save my last URI and do this thing can i do this ? – Bhanu Sharma Aug 29 '16 at 08:07
  • @BhanuSharma: I am sorry, but I do not understand your question. – CommonsWare Aug 29 '16 at 10:55
  • http://stackoverflow.com/questions/39199123/getting-only-once-permission-for-sdcard-window-on-lollipop-and-above-version – Bhanu Sharma Aug 29 '16 at 11:26
  • 1
    @BhaveshMoradiya: If you want to use `takePersistableUriPermission()`, to have long-term access to the content identified by a `Uri`, you must get that `Uri` in response to `ACTION_OPEN_DOCUMENT`. `ACTION_OPEN_DOCUMENT` is covered in [the documentation](https://developer.android.com/guide/topics/providers/document-provider). If you have additional concerns, I suggest that you ask a fresh Stack Overflow question, where you can explain in detail what you do not understand and what you need to know. – CommonsWare Dec 01 '18 at 11:55
  • Thanks for the document (useful flag).. I am new in android and I post question in SO but at that time I don't know what exactly I need that's why no one answered but after 1 day spend I found this.I need More help in this like 1). can I use this if I have only my video file path in string ? 2). (https://stackoverflow.com/questions/53513306/external-to-internal-memory-and-vice-versa-file-transfer-and-remove-file-from-so) This is the problem I am facing exactly. I refereed some basic app to clear some confusion. Thanks – Bhavesh Moradiya Dec 01 '18 at 12:20
  • So what happens if I use Intent.ACTION_GET_CONTENT or Intent.ACTION_PICK ? For how long can I access the Uri ? Or is it completely up the the app that was opened from those, and I should use the content right away and then forget I ever had access to it ? And does ACTION_OPEN_DOCUMENT promise me I can access the Uri for as long as I wish (with takePersistableUriPermission) , even after a restart of the device ? – android developer Jun 18 '19 at 12:21
  • 1
    @androiddeveloper: "For how long can I access the Uri ?" -- at best, you will have access for your running process. Consume the content ASAP. "...even after a restart of the device ?" -- persistable permissions should survive device reboots. – CommonsWare Jun 18 '19 at 12:26
  • @CommonsWare I see. So the framework is responsible for the Intent.ACTION_GET_CONTENT or Intent.ACTION_PICK time that I have to use their result, right? Not the app ? Or both? – android developer Jun 18 '19 at 12:33
  • 1
    @androiddeveloper: Well, to an extent, both. Primarily, I worry about the length of time we have rights via things like `FLAG_GRANT_READ_URI_PERMISSION`. See [this blog post](https://commonsware.com/blog/2016/08/10/uri-access-lifetime-shorter-than-you-might-think.html) for more. The app could elect to decline to serve content for that `Uri` anymore, but that can affect `ACTION_OPEN_DOCUMENT` too (e.g., user deletes the content, so there is nothing more to serve). – CommonsWare Jun 18 '19 at 12:37
  • @CommonsWare You mean about ACTION_OPEN_DOCUMENT ? If so, this is actually very important. What if I wanted to get access to the root of some storage volume, and it got revoked for some reason? Can such a thing occur, or just theoretically, or maybe only on special cases (such as SD card which got ejected)? Is there a way to check if I have access to a storage volume root or not? And if I even have a Uri to the storage volume root path (in onActivityResult) ? At the very least, to those built-in of the device? – android developer Jun 18 '19 at 12:42
  • @androiddeveloper: "What if I wanted to get access to the root of some storage volume, and it got revoked for some reason? " -- that would be `ACTION_OPEN_DOCUMENT_TREE`. If an *app* supplied the tree, the app could elect to stop providing the tree. In practice, I would expect this to be "on special cases", such as that tree no longer existing. This is not significantly different than the user deleting the filesystem directory; you need to deal with that (e.g., `DocumentFile` and `exists()`, catch the `IOException`). – CommonsWare Jun 18 '19 at 12:47
  • 1
    @androiddeveloper: "Is there a way to check if I have access to a storage volume root or not?" -- `DocumentFile` and `exists()` should tell you if the content exists. To see if you have a permission grant, use `ContentResolver` and `getPersistedUriPermissions()`. – CommonsWare Jun 18 '19 at 12:48
  • @CommonsWare But `exists()` isn't about whether I have access or not. For example, if I want to show the user a list of all storage volumes, and whether I have access to each of them or not, I don't see it being possible using `getPersistedUriPermissions()` . I can get a list of all of the storage volumes (via `StorageManager.html#getStorageVolumes()`) , but there is no Uri for each of what I get in the result, so I can't compare them to `getPersistedUriPermissions()`... And because of this, I can't know if entering a DocumentFile, if it's one of them or not (and which). – android developer Jun 18 '19 at 13:16
  • 1
    @androiddeveloper: "For example, if I want to show the user a list of all storage volumes, and whether I have access to each of them or not, I don't see it being possible using getPersistedUriPermissions()" -- storage volumes sub-divide into those that you requested previously and those that you did not. You know you don't have access to the latter. For the former, you have the `Uri` to see if you have permission. `StorageVolume` has a UUID, so all you need to do is track the UUID with the `Uri` for those you requested. I may be missing something, but this seems like it should cover you. – CommonsWare Jun 18 '19 at 13:24
  • @CommonsWare How "storage volumes sub-divide into those that you requested previously" ? It's just a list. None of the items of StorageVolume class instance. None of them tell you that you have access to it. Each has UUID, that's true, but there is no mapping between the UUID you have there, to the UriPermission instances you get from getPersistedUriPermissions. The StorageVolume doesn't have Uri, and the UriPermission doesnt' have UUID .That's why I say I don't see it being possible. Maybe I should write a new question here and you could show me how it's possible? – android developer Jun 18 '19 at 13:43
  • 1
    @androiddeveloper: "It's just a list" -- you already keep track of the `Uri` values that you obtained. Track the UUIDs along with them. Any unrecognized UUID is for a volume for which you do not have access. "None of the items of StorageVolume class instance" -- you said you were using `StorageManager.html#getStorageVolumes()`, which returns `StorageVolume` objects. "The StorageVolume doesn't have Uri" -- it does, for the ones that you requested and got access, because that's what you use to access them. You already save that `Uri` somewhere; save the UUID along with it. – CommonsWare Jun 18 '19 at 13:47
  • @CommonsWare I don't see anything related to Uri in here: https://developer.android.com/reference/android/os/storage/StorageVolume . Only UUID, and it's not guaranteed the user will grant access to it at all, when you use startActivityForResult. The user might choose a different storage volume or even a folder within the one you wanted (or not wanted). BTW, even the UUID might be null, according to the docs. I don't understand on which functions you look at that tell you which storageVolume you have access to and which you don't. – android developer Jun 18 '19 at 14:02
  • @CommonsWare Look, maybe I should create a new question. I'm sure other people would like it anyway. And it got too long here... – android developer Jun 18 '19 at 14:03
  • @androiddeveloper: "I don't see anything related to Uri in here" -- I assumed that you were using `createStorageAccessIntent()` and getting a `Uri` as a result, in order to access the storage volume. – CommonsWare Jun 18 '19 at 14:04
  • @CommonsWare There is no function "createStorageAccessIntent" on StorageVolume . There is `createAccessIntent` instead, and it's deprecated on Android Q, and passing null to it returns null (at least for the primary storage volume). There is `createOpenDocumentTreeIntent` function, but its Uri is inside the extras (in "android.provider.extra.INITIAL_URI" key), and it's almost the same as the Uri you get from `getStorageVolumes` . May I ask a question on StackOverflow now ? I think it's best this way... – android developer Jun 18 '19 at 14:31
  • @CommonsWare OK here is the new question: https://stackoverflow.com/q/56657639/878126 . I hope someone will know the answer to this. – android developer Jun 18 '19 at 22:47