Android: java.lang.SecurityException: does not have permission to uri 0 @ content://com.android.chrome.FileProvider/BlockedFile

10,840

Solution 1

I was getting this (not specifically due to chrome, however) because I copy/pasted intent-building code from another application.

Specifically, I had

uriToSend = FileProvider.getUriForFile(ctx, "com.mycompany.myfirstapp.fileprovider", f);

Copied into "com.mycompany.mysecondapp". So when mysecondapp tried to talk to the file provider at "com.mycompany.myfirstapp.fileprovider", it was trying to talk to myfirstapp's fileprovider, which it didn't have access to.

Checklist if you're sending to your own fileprovider:

  1. You've added the fileprovider to your android manifest
  2. Your code refers to the URI of the fileprovider from your app

Solution 2

URI based permissions, such as those controlling access to the screenshot are only active through the lifecycle of the receiving component (i.e., the receiving Activity) as per the FileProvider documentation:

A content URI allows you to grant read and write access using temporary access permissions. When you create an Intent containing a content URI, in order to send the content URI to a client app, you can also call Intent.setFlags() to add permissions. These permissions are available to the client app for as long as the stack for a receiving Activity is active. For an Intent going to a Service, the permissions are available as long as the Service is running.

You can extend the life of the permission grant by passing the Intent through to your Service - the permissions with 'chain' through different components if passed through startService/startActivity.

Share:
10,840
Daniel F
Author by

Daniel F

Updated on July 31, 2022

Comments

  • Daniel F
    Daniel F almost 2 years

    I'm developing an App which contains an Activity that can be invoked via the android.intent.action.CHOOSER and android.intent.action.SEND actions.

    When I share a website via Chrome to this activity, I can retrieve the screenshot of the website via Uri uriScreenshot = bExtras.getParcelable("share_screenshot_as_stream"); and then store that stream into a Bitmap. It's not really relevant that I'm storing it, but what's important is that this stream exists in the Intent that invoked the my Activity.

    Sometimes I want to "reshare" that Intent, have Android show the user the system's share dialog. In order to do this I modify the Intent, pointing the Intent directly to Android's internal Chooser:

    iCurrentIntent.setClassName("android", "com.android.internal.app.ChooserActivity");
    MyApplication.getAppContext().startActivity(iCurrentIntent);
    

    My problem is that I get the following exception:

    E/AndroidRuntime: FATAL EXCEPTION: main
      Process: process.name.abc.xyz, PID: 29696
      java.lang.SecurityException: Uid 10107 does not have permission to uri 0 @ content://com.android.chrome.FileProvider/BlockedFile_101125595074498
         at android.os.Parcel.readException(Parcel.java:1684)
         at android.os.Parcel.readException(Parcel.java:1637)
         at android.app.ActivityManagerProxy.startActivity(ActivityManagerNative.java:3101)
         at android.app.Instrumentation.execStartActivity(Instrumentation.java:1518)
         at android.app.ContextImpl.startActivity(ContextImpl.java:791)
         at android.app.ContextImpl.startActivity(ContextImpl.java:768)
         at android.content.ContextWrapper.startActivity(ContextWrapper.java:356)
         at process.name.abc.xyz.managers.utils.MyReshareManager.reshare(MyReshareManager.java:155)
         at process.name.abc.xyz.services.HUDService$HUDManager$11.onClick(HUDService.java:695)
         at android.view.View.performClick(View.java:5637)
         at android.view.View$PerformClick.run(View.java:22429)
         at android.os.Handler.handleCallback(Handler.java:751)
         at android.os.Handler.dispatchMessage(Handler.java:95)
         at android.os.Looper.loop(Looper.java:154)
         at android.app.ActivityThread.main(ActivityThread.java:6121)
         at java.lang.reflect.Method.invoke(Native Method)
         at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889)
         at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)
    

    The issue is not that I am modifying the Intent, but, I believe, the fact that I am getting assigned some security permissions on the Bitmap stream when Android invokes my Activity, and that when I forward the Intent the recieving Activity (com.android.internal.app.ChooserActivity) doesn't have permission to read that stream, since it was granted to my app and not to com.android.internal.app.ChooserActivity.

    I have tried adding iCurrentIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); to the intent, but that doesn't solve the issue.

    It is clearly related to the fact that the intent contains a stream which the ChooserActivity is trying to access. If such a stream is not present, that problem does not occur.

    This also occurs when I try to send that Intent over to Pocket iCurrentIntent.setClassName("com.ideashower.readitlater.pro", "com.ideashower.readitlater.activity.AddActivity");; in fact, any receiving Activity (Twitter, WhatsApp).

    It's always that same UID 10107 which is trying to access the stream and causes the crash, which is a bit strange, because it makes it appear that it is not the receiving Activity which is causing the crash. I say so, because if I understood it correctly, Twitter and WhatsApp and ChooserActivity all have a different UID.

    What can I do about this?

    =========

    Update: It looks like this is related to ClipData which Chrome is sending along, and not the stream. Working on it.

  • Daniel F
    Daniel F about 7 years
    Sorry, this doesn't cover my case. I am not creating the Intent. I am modifying the one which I got passed, as I want the Activity which receives the reshared Intent to receive it in a state as close as possible as it was when the original sender created it. I tried resharing from my Activity directly, leaving the service out of the equation, and it still causes the same crash.
  • Daniel F
    Daniel F about 7 years
    Also, I noticed that my case is somewhat more complex, since it is not the receiving service which then uses the one stored in MyApplication, but another service which creates some kind of overlay with a button on which the user can click to trigger the reshare action.
  • ianhanniballake
    ianhanniballake about 7 years
    So what you're saying that if you replace your entire Activity/Application/Service construct with an Activity that simply calls setClassName and startActivity, that still fails?
  • Daniel F
    Daniel F about 7 years
    Yes. I'm still using my Activity, but there I apply setClassName and call startActivity directly and finish() and return that activity instead of storing into the Application and triggering the Service.
  • Daniel F
    Daniel F about 7 years
    I believe I could work around the issue by storing the screenshot, and replacing the original "share_screenshot_as_stream" parcelable with my stored screenshot, that this would make my app be the owner of the stream. But I don't want to do that, because I don't want to limit myself to Chrome-originated Bitmap streams. I'd prefer another solution.
  • ianhanniballake
    ianhanniballake about 7 years
    The sending app is always the owner of its own URIs. Your app only has temporary access to them through the Intent you receive. Can you add the code of your minimal Activity that does nothing but forwards the activity along?
  • Daniel F
    Daniel F about 7 years
    Ok, I think I solved it. Setting iCurrentIntent.setClipData(null); on the Intent which is to be forwarded prevents the error. So it looks like this is not really related to traditional URIs passed in extras. Will track this the next couple of hours and eventually post the code if it still causes trouble. Thank you for your help.
  • ianhanniballake
    ianhanniballake about 7 years
    Well, that removes the ability for the receiving app to read the share_screenshot_as_stream stream since it is the ClipData that holds the URI based permissions.