Примечание: Я отвечал на другие вопросы о подобных исключениях. Это не дубликат - ни один из них не касается выбора 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 и предоставлении разрешений; Я пробовал некоторые из них - они не работали. И не совсем понял некоторых - Вот этот был одним из них.
Спасибо.
Возможный дубликат Как использовать поддержку FileProvider для обмена контентом с другими приложениями?
@Cheesebaron - Согласно документации, вам действительно не нужен метод GrantUriPermission. Ознакомьтесь с комментариями по адресу: Замечания по GrantUriPermission
@Cheesebaron - Но чтобы убедиться и все равно попробовать, где именно я бы назвал метод?
Исключение очевидно. Вам необходимо установить 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
чтобы предоставить выборочный доступ к файлам, вы можете сделать это с помощью FLAG_GRANT_READ_URI_PERMISSION и / или FLAG_GRANT_WRITE_URI_PERMISSION в намерении, которое вы используете для передачи одного из значений Uri вашего провайдера в стороннее приложение (например, через намерение ACTION_VIEW, используемое с startActivity()).
см. здесь stackoverflow.com/questions/24090667/… для более подробной информации
Я уже добавляю эти флаги с целью выбора файлов. Пожалуйста, обратитесь к коду в вопросе.
возможно, вы используете его неправильно .. проверьте эту ссылку medium.com/@quiro91/… для более подробной информации ...
Я нигде не вижу, чтобы вы вызывали
GrantUriPermissionза файлом, которым хотите поделиться.