title: Attach PDF - Java.Lang.SecurityException: Permission Denial
body: <p><strong><em>Note:</em> I have gone through other questions about similar exceptions. This is not a duplicate - none of them are about picking PDF or this particular error.</strong></p>
  
  <p>Buckle in.</p>
  
  <p>We are working on a Xamarin.Android project. There is an <em>'attach document'</em> activity. </p>
  
  <p>When the user tries to select a PDF (from the downloads directory), it throws the following exception:</p>
  
  <blockquote>
    <p>Java.Lang.SecurityException: Permission Denial: reading
    com.android.providers.downloads.DownloadStorageProvider uri
    content://com.android.providers.downloads.documents/226 from
    pid=13877, uid=10282 requires that you obtain access using
    ACTION_OPEN_DOCUMENT or related APIs</p>
  </blockquote>
  
  <p><strong>File-picker intent:</strong></p>
  
  <pre><code>ChooseFile.Click += (sender, e) =&gt;
  {
      Intent intent = new Intent();
      intent.SetType("*/*");
      intent.SetAction(Intent.ActionOpenDocument);
      intent.AddCategory(Intent.CategoryOpenable);
  
      if (global::Android.OS.Build.VERSION.SdkInt &gt;= global::Android.OS.BuildVersionCodes.N)
      {
          intent.AddFlags(ActivityFlags.GrantReadUriPermission);
          intent.AddFlags(ActivityFlags.GrantWriteUriPermission);
          intent.AddFlags(ActivityFlags.GrantPersistableUriPermission);
          intent.PutExtra(Intent.ExtraLocalOnly, true);
          intent.AddFlags(ActivityFlags.NoHistory);
      }
  
      try
      {
          StartActivityForResult(Intent.CreateChooser(intent, "Select File"), FILE_SELECT_CODE);
      }
      catch (Exception ex)
      {
          Toast.MakeText(this, "Please install a File Manager.", ToastLength.Short).Show();
      }
  
  };
  </code></pre>
  
  <p><strong>OnActivityResult method:</strong></p>
  
  <pre><code>protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
  {
      base.OnActivityResult(requestCode, resultCode, data);
  
      if ((requestCode == FILE_SELECT_CODE) &amp;&amp; (resultCode == Result.Ok) &amp;&amp; (data != null))
      {
          // Get the Uri of the selected file 
          uri = data.Data;
          string path = "";
  
          bool isdoc = DocumentsContract.IsDocumentUri(this, uri);
          if (isdoc)
          {
              if (IsDownloadsDocument(uri))
              {
                  string id = DocumentsContract.GetDocumentId(uri);
  
                  global::Android.Net.Uri contentUri = null;
  
                  if (global::Android.OS.Build.VERSION.SdkInt &gt;= global::Android.OS.BuildVersionCodes.N)
                  {
                      contentUri = ContentUris.WithAppendedId(global::Android.Net.Uri.Parse("content://com.android.providers.downloads.documents"), Convert.ToInt64(id));
                  }
                  else
                  {
                      contentUri = ContentUris.WithAppendedId(global::Android.Net.Uri.Parse("content://downloads/public_downloads"), Convert.ToInt64(id));
                  }
  
                  path = GetDataColumn(this, contentUri, null, null);
  
              }
              else if (IsMediaDocument(uri))
              {
                  string docId = DocumentsContract.GetDocumentId(uri);
                  string[] split = docId.Split(':');
  
                  string type = split[0];
  
                  global::Android.Net.Uri contentUri = null;
                  if ("image".Equals(type))
                  {
                      contentUri = MediaStore.Images.Media.ExternalContentUri;
                  }
                  else if ("video".Equals(type))
                  {
                      contentUri = MediaStore.Video.Media.ExternalContentUri;
                  }
                  else if ("audio".Equals(type))
                  {
                      contentUri = MediaStore.Audio.Media.ExternalContentUri;
                  }
  
                  string selection = "_id=?";
                  string[] selectionArgs = new String[]
                  {
                      split[1]
                  };
  
                  path = GetDataColumn(this, contentUri, selection, selectionArgs);
              }
          }
          else
          {
              path = GetRealPathFromURI(global::Android.Net.Uri.Parse(getImageUrlWithAuthority(this, uri)));
          }
  
          txtFileName.Text = System.IO.Path.GetFileName(path);
          fileStream = File.ReadAllBytes(path);
      }
  }
  </code></pre>
  
  <p><strong>Checking file directory URI:</strong></p>
  
  <pre><code>private bool IsExternalStorageDocument(global::Android.Net.Uri uri)
  {
      return "com.android.externalstorage.documents".Equals(uri.Authority);
  }
  
  private bool IsDownloadsDocument(global::Android.Net.Uri uri)
  {
      return "com.android.providers.downloads.documents".Equals(uri.Authority);
  }
  
  private bool IsMediaDocument(global::Android.Net.Uri uri)
  {
      return "com.android.providers.media.documents".Equals(uri.Authority);
  }
  
  private bool IsGooglePhotosUri(global::Android.Net.Uri uri)
  {
      return "com.google.android.apps.photos.content".Equals(uri.Authority);
  }
  </code></pre>
  
  <p><strong>GetDataColumn method:</strong></p>
  
  <pre><code>private string GetDataColumn(Context context, global::Android.Net.Uri uri, String selection, string[] selectionArgs)
  {
      ICursor cursor = null;
      string column = "_data";
      string[] projection =
      {
          column
      };
  
      try
      {
          cursor = context.ContentResolver.Query(uri, projection, selection, selectionArgs, null);
          if (cursor != null &amp;&amp; cursor.MoveToFirst())
          {
              int index = cursor.GetColumnIndexOrThrow(column);
              return cursor.GetString(index);
          }
      }
      finally
      {
          if (cursor != null)
              cursor.Close();
      }
      return null;
  }
  </code></pre>
  
  <p><strong>AndroidManifest.xml</strong></p>
  
  <pre><code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
  &lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
            package="com.jnbmedical.portal"
            android:installLocation="auto"
            android:versionCode="33"
            android:versionName="3.3"&gt;
  
      &lt;uses-sdk android:minSdkVersion="19" android:targetSdkVersion="28" /&gt;
  
      &lt;application android:label="JandB" android:icon="@drawable/icon"&gt;
          &lt;provider android:name="android.support.v4.content.FileProvider"
                android:authorities="com.jnbmedical.portal.fileprovider"
                android:exported="false"
                android:grantUriPermissions="true"&gt;
              &lt;meta-data android:name="android.support.FILE_PROVIDER_PATHS"
                   android:resource="@xml/file_provider_paths" /&gt;
          &lt;/provider&gt;
      &lt;/application&gt;
  
      &lt;uses-permission android:name="android.permission.INTERNET" /&gt;
      &lt;uses-permission android:name="android.permission.CALL_PHONE" /&gt;
      &lt;uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /&gt;
      &lt;uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /&gt;
      &lt;uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /&gt;
      &lt;uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /&gt;
      &lt;uses-permission android:name="android.permission.MANAGE_DOCUMENTS" /&gt;
      &lt;uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" /&gt;
      &lt;uses-permission android:name="android.permission.INSTALL_PACKAGES" /&gt;
      &lt;uses-permission android:name="android.permission.WAKE_LOCK" /&gt;
      &lt;uses-permission android:name="android.permission.GET_ACCOUNTS" /&gt;
      &lt;uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" /&gt;
      &lt;uses-permission android:name="com.jandbmedical.portal.testapp.permission.C2D_MESSAGE" /&gt;
      &lt;uses-permission android:name="android.permission.READ_PHONE_STATE" /&gt;
  
  &lt;/manifest&gt;
  </code></pre>
  
  <p><strong>file_provider_paths.xml</strong></p>
  
  <pre><code>&lt;?xml version="1.0" encoding="utf-8" ?&gt;
  &lt;paths&gt;
    &lt;external-path
        name="external_files" path="." /&gt;
  &lt;/paths&gt;
  </code></pre>
  
  <hr>
  
  <p>We recently added run-time permissions (in the splash-screen activity) to handle Android 23 (i.e. 6.0 - marshmallow) and higher.</p>
  
  <p><strong>Permission fields &amp; array:</strong></p>
  
  <pre><code>const int REQUEST = 0;
  
  readonly string[] PERMISSIONS =
  {
      Manifest.Permission.AccessCoarseLocation,
      Manifest.Permission.AccessFineLocation,
      Manifest.Permission.ReadExternalStorage,
      Manifest.Permission.WriteExternalStorage,
      Manifest.Permission.ReadPhoneState,
  };
  </code></pre>
  
  <p><strong>CheckPermissions method:</strong></p>
  
  <pre><code>private void CheckPermissions()
  {
      if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadPhoneState) != Permission.Granted
          || ContextCompat.CheckSelfPermission(this, Manifest.Permission.AccessFineLocation) != Permission.Granted
          || ContextCompat.CheckSelfPermission(this, Manifest.Permission.AccessCoarseLocation) != Permission.Granted
          || ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadExternalStorage) != Permission.Granted
          || ContextCompat.CheckSelfPermission(this, Manifest.Permission.WriteExternalStorage) != Permission.Granted)
      {
          //Permissions have not been granted
          ActivityCompat.RequestPermissions(this, PERMISSIONS, REQUEST);
      }
      else
      {
          //Permissions have been granted
          TelephonyManager tm = (TelephonyManager)GetSystemService(TelephonyService);
          Util.UUID = tm.DeviceId;
  
          StartActivity(typeof(MainActivity));
          Finish();
      }
  }
  </code></pre>
  
  <p><strong>OnRequestPermissionsResult method:</strong></p>
  
  <pre><code>public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
  {
      switch (requestCode)
      {
          case REQUEST:
              {
                  var permissionGranted = VerifyPermissions(grantResults);
                  if (permissionGranted)
                  {
                      TelephonyManager tm = (TelephonyManager)GetSystemService(TelephonyService);
                      Util.UUID = tm.DeviceId;
  
                      StartActivity(typeof(MainActivity));
                      Finish();
                  }
                  else
                  {
                      this.ShowInformationWithClick(Resources.GetText(Resource.String.Alert), Resources.GetText(Resource.String.OK), "App requires storage, location, and phone access to function properly. Please grant necessary permissions. App will not work if permissions are not granted.", HandleClicked);
                  }
              }
              break;
      }
  }
  </code></pre>
  
  <p><strong>VerifyPermissions method:</strong></p>
  
  <pre><code>public static bool VerifyPermissions(Permission[] grantResults)
  {
      // At least one result must be checked.
      if (grantResults.Length &lt; 1)
          return false;
  
      // Verify that each required permission has been granted, otherwise return false.
      foreach (Permission result in grantResults)
      {
          if (result != Permission.Granted)
          {
              return false;
          }
      }
      return true;
  }
  </code></pre>
  
  <p>I wish there was a TL;DR: version of this but I do not know what the problem is because our code is almost similar to other sample codes out there for this operation. I read something about persistable URIs and granting permissions; I tried some of those - they didn't work. And didn't fully understand some - <a href="https://stackoverflow.com/questions/18249007/how-to-use-support-fileprovider-for-sharing-content-to-other-apps">this one</a> being one of them.</p>
  
  <p>Thanks.</p>