Прикрепите PDF-файл - Java.Lang.SecurityException: Permission Denial

Примечание: Я отвечал на другие вопросы о подобных исключениях. Это не дубликат - ни один из них не касается выбора PDF или этой конкретной ошибки.

Пристегните.

Мы работаем над проектом Xamarin.Android. Есть активность 'прикрепить документ'.

Когда пользователь пытается выбрать PDF-файл (из каталога загрузок), возникает следующее исключение:

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

Назначение средства выбора файлов:

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 и предоставлении разрешений; Я пробовал некоторые из них - они не работали. И не совсем понял некоторых - Вот этот был одним из них.

Спасибо.

Я нигде не вижу, чтобы вы вызывали GrantUriPermission за файлом, которым хотите поделиться.

Cheesebaron 02.10.2018 08:36

@Cheesebaron - Согласно документации, вам действительно не нужен метод GrantUriPermission. Ознакомьтесь с комментариями по адресу: Замечания по GrantUriPermission

Ronak Vachhani 02.10.2018 09:05

@Cheesebaron - Но чтобы убедиться и все равно попробовать, где именно я бы назвал метод?

Ronak Vachhani 02.10.2018 09:14
0
4
999
1

Ответы 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>

Выдает следующее исключение: Java.Lang.RuntimeException: Unable to get provider android.support.v4.content.FileProvider: java.lang.SecurityException: Provider must not be exported

Ronak Vachhani 02.10.2018 09:01

чтобы предоставить выборочный доступ к файлам, вы можете сделать это с помощью FLAG_GRANT_READ_URI_PERMISSION и / или FLAG_GRANT_WRITE_URI_PERMISSION в намерении, которое вы используете для передачи одного из значений Uri вашего провайдера в стороннее приложение (например, через намерение ACTION_VIEW, используемое с startActivity()).

amit 02.10.2018 11:47

см. здесь stackoverflow.com/questions/24090667/… для более подробной информации

amit 02.10.2018 11:48

Я уже добавляю эти флаги с целью выбора файлов. Пожалуйста, обратитесь к коду в вопросе.

Ronak Vachhani 02.10.2018 13:32

возможно, вы используете его неправильно .. проверьте эту ссылку medium.com/@quiro91/… для более подробной информации ...

amit 03.10.2018 12:47

Другие вопросы по теме