一直在忙于Gallery,最近遇到了这样的bug,在Setting-->storage&USB->internal storage->images->选择一张-图片> home key exit,然后在Gallery删除选中的那种照片,最后在最近列表里选择Setting,画面仍保留在那张图片,点击打印men,就会报Gallery Force Close.通过log可以看到报以下错:
04-15 11:00:33.959 8427 8427 D AndroidRuntime: Shutting down VM
04-15 11:00:33.965 8427 8427 E AndroidRuntime: FATAL EXCEPTION: main
04-15 11:00:33.965 8427 8427 E AndroidRuntime: Process: com.android.gallery3d, PID: 8427
04-15 11:00:33.965 8427 8427 E AndroidRuntime: java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaDocumentsProvider uri content://com.android.providers.media.documents/document/image%3A593 from pid=8427, uid=10065 requires android.permission.MANAGE_DOCUMENTS, or grantUriPermission()
04-15 11:00:33.965 8427 8427 E AndroidRuntime: at android.os.Parcel.readException(Parcel.java:1620)
04-15 11:00:33.965 8427 8427 E AndroidRuntime: at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183)
04-15 11:00:33.965 8427 8427 E AndroidRuntime: at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:135)
04-15 11:00:33.965 8427 8427 E AndroidRuntime: at android.content.ContentProviderProxy.query(ContentProviderNative.java:421)
04-15 11:00:33.965 8427 8427 E AndroidRuntime: at android.content.ContentResolver.query(ContentResolver.java:493)
04-15 11:00:33.965 8427 8427 E AndroidRuntime: at android.content.ContentResolver.query(ContentResolver.java:435)
04-15 11:00:33.965 8427 8427 E AndroidRuntime: at com.android.gallery3d.filtershow.cache.ImageLoader.getLocalPathFromUri(ImageLoader.java:85)
没有删除这张照片之前,选择打印是正常的,为何删除了照片就报权限问题了呢?经过代码分析,Gallery是没有权限访问MediaDocumentsProvider,MediaDocumentsProvider是可以接受 android:grantUriPermissions="true"传递权限的,而文件管理器documentsui是申请了 <uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />,而文件管理器打开图片会将read的权限通过intent的方式传递了给Gallery:Intent.FLAG_GRANT_READ_URI_PERMISSION
final Intent view = new Intent(Intent.ACTION_VIEW);
view.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
view.setData(doc.derivedUri);
try {
startActivity(view);
} catch (ActivityNotFoundException ex) {
Toast.makeText(this, R.string.toast_no_application, Toast.LENGTH_SHORT).show();
}
http://androidxref.com/6.0.0_r1/xref/frameworks/base/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
从而Gallery可以访问MediaDocumentsProvider,而删除了照片的时候,一并将该权限删除了 MediaDocumentsProvider.onMediaStoreDelete(getContext(),
4069 volumeName, FileColumns.MEDIA_TYPE_IMAGE, id);,所以再次访问就报权限问题了
if (mediaType == FileColumns.MEDIA_TYPE_IMAGE) {
deleteIfAllowed(uri, data);
MediaDocumentsProvider.onMediaStoreDelete(getContext(),
volumeName, FileColumns.MEDIA_TYPE_IMAGE, id);
idvalue[0] = String.valueOf(id);
database.mNumQueries++;
Cursor cc = db.query("thumbnails", sDataOnlyColumn,
"image_id=?", idvalue, null, null, null);
try {
while (cc.moveToNext()) {
deleteIfAllowed(uri, cc.getString(0));
}
database.mNumDeletes++;
db.delete("thumbnails", "image_id=?", idvalue);
} finally {
IoUtils.closeQuietly(cc);
}
}
http://androidxref.com/6.0.0_r1/xref/packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java
revokeUriPermission删除权限
/**
* When deleting an item, we need to revoke any outstanding Uri grants.
*/
static void onMediaStoreDelete(Context context, String volumeName, int type, long id) {
if (!"external".equals(volumeName)) return;
if (type == FileColumns.MEDIA_TYPE_IMAGE) {
final Uri uri = DocumentsContract.buildDocumentUri(
AUTHORITY, getDocIdForIdent(TYPE_IMAGE, id));
context.revokeUriPermission(uri, ~0);
} else if (type == FileColumns.MEDIA_TYPE_VIDEO) {
final Uri uri = DocumentsContract.buildDocumentUri(
AUTHORITY, getDocIdForIdent(TYPE_VIDEO, id));
context.revokeUriPermission(uri, ~0);
} else if (type == FileColumns.MEDIA_TYPE_AUDIO) {
final Uri uri = DocumentsContract.buildDocumentUri(
AUTHORITY, getDocIdForIdent(TYPE_AUDIO, id));
context.revokeUriPermission(uri, ~0);
}
}
http://androidxref.com/6.0.0_r1/xref/packages/providers/MediaProvider/src/com/android/providers/media/MediaDocumentsProvider.java