App1とapp2の2つのアプリがあります。
App2には次のものがあります。
<provider
Android:name="Android.support.v4.content.FileProvider"
Android:authorities="com.Android.provider.ImageSharing"
Android:exported="false"
Android:grantUriPermissions="true" >
<meta-data
Android:name="Android.support.FILE_PROVIDER_PATHS"
Android:resource="@xml/paths" />
</provider>
paths.xml:
<paths>
<files-path name="my_images" path="images/"/>
</paths>
App2は、そのアクティビティでApp1から画像のURIを取得する要求を受け取ります。 URIが決定されると、App2アクティビティは次のことを行います。
Intent intent = new Intent();
intent.setDataAndType(contentUri, getContentResolver().getType(contentUri));
int uid = Binder.getCallingUid();
String callingPackage = getPackageManager().getNameForUid(uid);
getApplicationContext().grantUriPermission(callingPackage, contentUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
setResult(Activity.RESULT_OK, intent);
finish();
App2から結果を受信すると、App1は次のことを行います。
Uri imageUri = data.getData();
if(imageUri != null) {
ImageView iv = (ImageView) layoutView.findViewById(R.id.imageReceived);
iv.setImageURI(imageUri);
}
App1でApp2から戻ると、次の例外が発生します。
Java.lang.SecurityException:許可拒否:からエクスポートされないProcessRecord {52a99eb0 3493:com.Android.App1.app/u0a57}(pid = 3493、uid = 10057)からプロバイダーAndroid.support.v4.content.FileProviderを開くuid 10058
私は何を間違えていますか?
これを解決する唯一の方法は、次のように、それを必要とする可能性のあるすべてのパッケージに許可を与えることです。
List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
まず、grantUriPermission()
から切り替えて、Intent
自体に_FLAG_GRANT_READ_URI_PERMISSION
_を置くだけです via addFlags()
またはsetFlag()
。
何らかの理由で機能しない場合は、getCallingUid()
ロジックをどこにでもある代わりにonCreate()
に移動して、実際の「呼び出し元」を見つけられるかどうかを確認できます。 。
Lorenzo Quiroliによる すばらしい記事 があります。これは、古いAndroidバージョン。
彼は、次のように、IntentのClipDataを手動で設定し、アクセス許可を設定する必要があることを発見しました。
if ( Build.VERSION.SDK_INT <= Build.VERSION_CODES.Lollipop ) {
takePictureIntent.setClipData( ClipData.newRawUri( "", photoURI ) );
takePictureIntent.addFlags( Intent.FLAG_GRANT_WRITE_URI_PERMISSION|Intent.FLAG_GRANT_READ_URI_PERMISSION );
}
これをAPI 17でテストしたところ、うまく機能しました。機能するソリューションがどこにも見つかりませんでした。
setData(contentUri);
を追加し、要件に基づいてaddFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
またはaddFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
を追加します
これにより、Java.lang.SecurityException:Permission Denialが解決されます
確認済み。
これは https://developer.Android.com/reference/Android/support/v4/content/FileProvider.html#Permissions に従って行われます
ファイルプロバイダーについて読むには、このリンクに従ってください ファイルプロバイダー
、キットカットとマシュマロは次の手順に従います。まず、MainfestFileのアプリケーションタグの下にある広告タグプロバイダー。
<provider
Android:name="Android.support.v4.content.FileProvider"
Android:authorities="${applicationId}.provider"
Android:exported="false"
Android:grantUriPermissions="true">
<meta-data
Android:name="Android.support.FILE_PROVIDER_PATHS"
Android:resource="@xml/provider_paths"/>
</provider>
resフォルダーの下に(provider_paths.xml)でファイル名を作成します
<paths xmlns:Android="http://schemas.Android.com/apk/res/Android">
<external-path name="external_files" path="."/>
キットKitKatバージョンに含まれるこの問題を解決しました
private void takePicture() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
Uri photoURI = null;
try {
File photoFile = createImageFileWith();
path = photoFile.getAbsolutePath();
photoURI = FileProvider.getUriForFile(MainActivity.this,
getString(R.string.file_provider_authority),
photoFile);
} catch (IOException ex) {
Log.e("TakePicture", ex.getMessage());
}
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Lollipop) {
takePictureIntent.setClipData(ClipData.newRawUri("", photoURI));
takePictureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION|Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
startActivityForResult(takePictureIntent, PHOTO_REQUEST_CODE);
}
}
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.ENGLISH).format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DCIM), "Camera");
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = "file:" + image.getAbsolutePath();
return image;
}
そのようにして問題を解決しました:
Intent sIntent = new Intent("com.appname.ACTION_RETURN_FILE").setData(uri);
List<ResolveInfo> resInfoList = activity.getPackageManager().queryIntentActivities(sIntent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
activity.grantUriPermission(FILE_PROVIDER_ID, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
sIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
activity.setResult(RESULT_OK, sIntent);
このアドバイスをありがとう、@ CommonsWare。
私の問題は、呼び出し元のパッケージにありました。何らかの理由で、Binder.callingUid()
およびgetPackageManager().getNameForUid(uid)
はApp2
の代わりに App1
。
App2のonCreate
とonResume
で呼び出してみましたが、喜びはありませんでした。
私はそれを解決するために次を使用しました:
getApplicationContext().grantUriPermission(getCallingPackage(),
contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
結局のところ、アクティビティにはこれ専用のAPIがあります。 here を参照してください。
特定のパッケージ名の許可を設定する必要があります。その後、それにアクセスできるようになります。
context.grantUriPermission("com.Android.App1.app", fileUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);