系统应用使用FileProvider的坑( 二 )


查询的时候就只需要从strat里面找到文件路径最匹配的name即可 。

系统应用使用FileProvider的坑

文章插图
打开文件
有了这个uri之后我们就能通过将它传给其他应用,并配置.或者.为其他应用设置读写权限:
val uri = FileProvider.getUriForFile(this, "me.linjw.demo.fileprovider.provider", file)val intent = Intent()intent.data = http://www.kingceram.com/post/uriintent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)intent.setClassName("me.linjw.demo.fileprovider.recv", "me.linjw.demo.fileprovider.recv.MainActivity")startActivity(intent)
其他应用拿到这个uri就可以通过.打开文件流:
val inputStream = intent.data?.let { contentResolver.openInputStream(it) }
或者有时候我们希望通过String传递uri的时候可以提前使用Context.grantUriPermission为指定的包名申请权限,然后接收端Uri.parse去解析出Uri来操作文件:
// 发送端val uri = FileProvider.getUriForFile(this, "me.linjw.demo.fileprovider.provider", file)grantUriPermission("me.linjw.demo.fileprovider.recv", uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)val intent = Intent()intent.putExtra("uri", uri.toString())intent.setClassName("me.linjw.demo.fileprovider.recv", "me.linjw.demo.fileprovider.recv.MainActivity")startActivity(intent)// 接收端val uri = Uri.parse(intent.getStringExtra("uri"))val inputStream = contentResolver.openInputStream(uri)
Uri操作文件的原理实际上就是通过请求我们之前声明的me.linjw.demo..这个,让它给我们去打开文件:
// FileProvider.javapublic ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode)throws FileNotFoundException {// ContentProvider has already checked granted permissionsfinal File file = mStrategy.getFileForUri(uri);final int fileMode = modeToMode(mode);return ParcelFileDescriptor.open(file, fileMode);}
也就是说文件权限的校验实际上只发生在打开的阶段.其他应用虽然没有权限打开我们的文件,但是我们可以在里面帮它打开然后返回文件描述符,给其他应用去读写 。
系统应用使用的坑
项目中有个系统应用需要向其他应用传的文件,于是把加上,然后发现其他应用还是没有权限 。从日志里面看是说这个并没有从UID 1000里暴露出来:
02-13 06:52:28.92142924292 E AndroidRuntime: Caused by: java.lang.SecurityException: Permission Denial: opening provider androidx.core.content.FileProvider from ProcessRecord{806d30d 4292:me.linjw.demo.fileprovider.recv/u0a53} (pid=4292, uid=10053) that is not exported from UID 1000
由于这个UID 1000太显眼,所以尝试将系统签名去掉发现权限就正常了,实锤是系统签名的原因 。
系统应用使用FileProvider的坑

文章插图
查看出现异常的时候的日志,发现了下面的打印:
02-13 06:52:28.4868631393 W UriGrantsManagerService: For security reasons, the system cannot issue a Uri permission grant to content://me.linjw.demo.fileprovider.provider/root/data/user/0/me.linjw.demo.fileprovider/files/test.txt [user 0]; use startActivityAsCaller() instead
在代码里面搜索关键字,发现系统应用需要在源码里面配置FileProvider的authorities:
// https://cs.android.com/android/platform/superproject/+/android-13.0.0_r29:frameworks/base/services/core/java/com/android/server/uri/UriGrantsManagerService.java// Bail early if system is trying to hand out permissions directly; it// must always grant permissions on behalf of someone explicit.final int callingAppId = UserHandle.getAppId(callingUid);if ((callingAppId == SYSTEM_UID) || (callingAppId == ROOT_UID)) {if ("com.android.settings.files".equals(grantUri.uri.getAuthority())|| "com.android.settings.module_licenses".equals(grantUri.uri.getAuthority())) {// Exempted authority for// 1. cropping user photos and sharing a generated license html//file in Settings app// 2. sharing a generated license html file in TvSettings app// 3. Sharing module license files from Settings app} else {Slog.w(TAG, "For security reasons, the system cannot issue a Uri permission"+ " grant to " + grantUri + "; use startActivityAsCaller() instead");return -1;}}