背景
最近项目开发过程中用到安装apk的功能。把从服务器下载下来的apk安装到机器中。安装过程中遇到的问题记录一下。
问题
安装过程中提示解析软件包时出现问题。
说明:本文是针对运行的apk在Manifest中声明了systemuid。如果你的应用声明了"android.uid.system",而且也遇到了这个问题,恭喜你找到了本文。
1 | android:sharedUserId="android.uid.system" |
没有声明的小伙伴可以去找别的博客了。
安装apk
首先回顾一下安装的方法,android O中大家一般这样写的。
1 2 3 4 5 6 7 8 9 | private void installApk(String path){ Intent intent = new Intent(Intent.ACTION_VIEW); Uri uri = FileProvider.getUriForFile(context,"com.****.****.fileprovider",new File(path)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); intent.setDataAndType(uri,"application/vnd.android.package-archive"); context.startActivity(intent); } |
然后再Manifest中声明provider
1 2 3 4 5 6 7 8 9 | <provider android:authorities="com.honeywell.depponservice.fileprovider" android:name="android.support.v4.content.FileProvider" android:grantUriPermissions="true" android:exported="false"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths" /> </provider> |
在xml中建一个filepaths.xml
1 2 3 4 | <?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path path="." name="cmexternal" /> </paths> |
这种方式是没问题的,但是关机问题出在了我的app声明了SYSTEM_UID。为什么这么说呢,下面继续讲。
原因
我就是按照上面的方法去安装apk,然而总是提示解析软件包时出现问题。第一反应是从服务器下载的安装包有问题,打开文件管理器,点击下载后的安装包。发现可以安装,那就说明不是安装包的问题。没办法看log吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 03-27 18:15:16.005 1378 2744 W ActivityManager: Permission Denial: opening provider android.support.v4.content.FileProvider from ProcessRecord{f1ea483 11298:com.android.packageinstaller/u0a19} (pid=11298, uid=10019) that is not exported from UID 1000 03-27 18:15:16.006 11298 12569 W InstallStaging: Error staging apk from content URI 03-27 18:15:16.006 11298 12569 W InstallStaging: java.lang.SecurityException: Permission Denial: opening provider android.support.v4.content.FileProvider from ProcessRecord{f1ea483 11298:com.android.packageinstaller/u0a19} (pid=11298, uid=10019) that is not exported from UID 1000 03-27 18:15:16.006 11298 12569 W InstallStaging: at android.os.Parcel.readException(Parcel.java:2013) 03-27 18:15:16.006 11298 12569 W InstallStaging: at android.os.Parcel.readException(Parcel.java:1959) 03-27 18:15:16.006 11298 12569 W InstallStaging: at android.app.IActivityManager$Stub$Proxy.getContentProvider(IActivityManager.java:4758) 03-27 18:15:16.006 11298 12569 W InstallStaging: at android.app.ActivityThread.acquireProvider(ActivityThread.java:5860) 03-27 18:15:16.006 11298 12569 W InstallStaging: at android.app.ContextImpl$ApplicationContentResolver.acquireUnstableProvider(ContextImpl.java:2530) 03-27 18:15:16.006 11298 12569 W InstallStaging: at android.content.ContentResolver.acquireUnstableProvider(ContentResolver.java:1783) 03-27 18:15:16.006 11298 12569 W InstallStaging: at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1396) 03-27 18:15:16.006 11298 12569 W InstallStaging: at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1249) 03-27 18:15:16.006 11298 12569 W InstallStaging: at android.content.ContentResolver.openInputStream(ContentResolver.java:969) 03-27 18:15:16.006 11298 12569 W InstallStaging: at com.android.packageinstaller.InstallStaging$StagingAsyncTask.doInBackground(InstallStaging.java:180) 03-27 18:15:16.006 11298 12569 W InstallStaging: at com.android.packageinstaller.InstallStaging$StagingAsyncTask.doInBackground(InstallStaging.java:174) 03-27 18:15:16.006 11298 12569 W InstallStaging: at android.os.AsyncTask$2.call(AsyncTask.java:333) 03-27 18:15:16.006 11298 12569 W InstallStaging: at java.util.concurrent.FutureTask.run(FutureTask.java:266) 03-27 18:15:16.006 11298 12569 W InstallStaging: at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245) 03-27 18:15:16.006 11298 12569 W InstallStaging: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162) 03-27 18:15:16.006 11298 12569 W InstallStaging: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636) 03-27 18:15:16.006 11298 12569 W InstallStaging: at java.lang.Thread.run(Thread.java:764) |
第一眼看到log时感觉是权限问题,因为有Permission Denial:字样。检查了一遍读写外部存储权限都开了啊,安装未知应用权限也打开了。可还是不行,最后索性把selinux都给关了依旧不行。无限抓狂,总之无论怎么做总会打出上面的log。在各种百度谷歌后还没有解决。一脸无奈的继续看log,看还有什么有用信息没。诶,突然发现了一行log:
1 | ActivityManager: For security reasons, the system cannot issue a Uri permission grant to content://com.***.***.fileprovider/cmexternal/PDADownload/pda_client(4).apk [user 0]; use startActivityAsCaller() instead |
最终在确认代码没问题的情况下,放大招,去源码中找答案。搜一下“For security reasons, the system cannot issue a Uri permission grant to”这段log是哪里打出来的。进到framework/base下grep.
1 | ./services/core/java/com/android/server/am/ActivityManagerService.java:9078: Slog.w(TAG, "For security reasons, the system cannot issue a Uri permission" |
十几秒后还真给搜到了。打开AMS第9078行看看。
这段log是在int checkGrantUriPermissionLocked方法中打印的,看下代码
1 2 3 4 5 6 7 8 9 10 11 12 | // 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())) { // Exempted authority for cropping user photos in Settings app } else { Slog.w(TAG, "For security reasons, the system cannot issue a Uri permission" + " grant to " + grantUri + "; use startActivityAsCaller() instead"); return -1; } } |
如果你也被这个问题折磨的很痛苦,想必看到这段代码就明白原因了吧。
这尼玛被return -1了。这段代码的意思就是如果你的应用是SYSTEM_UID或者ROOT_UID就不能用content://加fileprovider的Uri。只有settings可以用。
我们可以去看下settings里面到底有没有com.android.settings.files,这样的provider。打开settings 的AndroidManifest文件。
1 2 3 4 5 6 7 8 9 | <provider android:name="android.support.v4.content.FileProvider" android:authorities="com.android.settings.files" android:grantUriPermissions="true" android:exported="false"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> |
你会发现settings真的有。这也太坑了吧,凭什么只让settings用,所以应该怎么办呢。
- 去掉声明“android.uid.system”。
- 在AMS判断settings的地方加上你自己的uri。类似这样:
1 | if ("com.android.settings.files".equals(grantUri.uri.getAuthority()) || "com.***.***.fileprovider".equals(grantUri.uri.getAuthority())) { |
- 如果有人告诉你又不能去掉systemuid,又不能改framwork代码。告诉那个人:来来来,你来,你nb你来搞!
哈哈,第三条开个玩笑。这个问题当你知道答案后,感觉很简单,但是当你无论怎么调试,怎么百度谷歌都找不到原因时候,气的都要拔头发时候,不要放弃,当实在是没有办法时候就去源码里找答案,我相信总会找到答案的。希望本文可以帮到你。