附加PDF-Java.Lang.SecurityException:权限被拒绝

时间:2018-10-02 06:10:36

标签: android xamarin xamarin.android android-permissions

注意:我经历了有关类似异常的其他问题。这不是重复的-它们都不是关于选择PDF或此特定错误。

插上。

我们正在研究Xamarin.Android项目。有一个“附加文档” 活动。

当用户尝试从下载目录中选择PDF时,它将引发以下异常:

  

Java.Lang.SecurityException:权限被拒绝:正在读取   com.android.providers.downloads.DownloadStorageProvider uri   内容://com.android.providers.downloads.documents/226来自   pid = 13877,uid = 10282要求您使用   ACTION_OPEN_DOCUMENT或相关API

文件选择意图:

ChooseFile.Click += (sender, e) =>
{
    Intent intent = new Intent();
    intent.SetType("*/*");
    intent.SetAction(Intent.ActionOpenDocument);
    intent.AddCategory(Intent.CategoryOpenable);

    if (global::Android.OS.Build.VERSION.SdkInt >= 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();
    }

};

OnActivityResult方法:

protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
{
    base.OnActivityResult(requestCode, resultCode, data);

    if ((requestCode == FILE_SELECT_CODE) && (resultCode == Result.Ok) && (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 >= 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);
    }
}

检查文件目录URI:

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);
}

GetDataColumn方法:

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 && cursor.MoveToFirst())
        {
            int index = cursor.GetColumnIndexOrThrow(column);
            return cursor.GetString(index);
        }
    }
    finally
    {
        if (cursor != null)
            cursor.Close();
    }
    return null;
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.jnbmedical.portal"
          android:installLocation="auto"
          android:versionCode="33"
          android:versionName="3.3">

    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="28" />

    <application android:label="JandB" android:icon="@drawable/icon">
        <provider android:name="android.support.v4.content.FileProvider"
              android:authorities="com.jnbmedical.portal.fileprovider"
              android:exported="false"
              android:grantUriPermissions="true">
            <meta-data android:name="android.support.FILE_PROVIDER_PATHS"
                 android:resource="@xml/file_provider_paths" />
        </provider>
    </application>

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.CALL_PHONE" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
    <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
    <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <uses-permission android:name="com.jandbmedical.portal.testapp.permission.C2D_MESSAGE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

</manifest>

file_provider_paths.xml

<?xml version="1.0" encoding="utf-8" ?>
<paths>
  <external-path
      name="external_files" path="." />
</paths>

我们最近(在启动屏幕活动中)添加了运行时权限,以处理Android 23(即6.0-棉花糖)及更高版本。

权限字段和数组:

const int REQUEST = 0;

readonly string[] PERMISSIONS =
{
    Manifest.Permission.AccessCoarseLocation,
    Manifest.Permission.AccessFineLocation,
    Manifest.Permission.ReadExternalStorage,
    Manifest.Permission.WriteExternalStorage,
    Manifest.Permission.ReadPhoneState,
};

CheckPermissions方法:

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();
    }
}

OnRequestPermissionsResult方法:

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;
    }
}

VerifyPermissions方法:

public static bool VerifyPermissions(Permission[] grantResults)
{
    // At least one result must be checked.
    if (grantResults.Length < 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;
}

我希望有一个TL; DR:版本,但我不知道问题出在哪里,因为我们的代码几乎与该操作的其他示例代码相似。我读了一些有关持久性URI和授予权限的内容。我尝试了其中一些-他们没有用。并且不完全理解其中的一些-this one是其中之一。

谢谢。

1 个答案:

答案 0 :(得分:-1)

例外情况很明显。您需要在声明此活动的android:exported="true"文件中设置AndroidManifest.xml。目前设置为false。

尝试以下

<application android:label="JandB" android:icon="@drawable/icon">
    <provider android:name="android.support.v4.content.FileProvider"
          android:authorities="com.jnbmedical.portal.fileprovider"
          android:exported="true"  <!-- change here -->
          android:grantUriPermissions="true">
        <meta-data android:name="android.support.FILE_PROVIDER_PATHS"
             android:resource="@xml/file_provider_paths" />
    </provider>
</application>