本文通过对源码进行追踪,并且调试各种方式,得出android通过adb push apk放置目录/sdcard/Download/下无法安装的原因,并从两个修改点触发,提出如何能修复此问题的建议。
1. 现象
把apk通过adb push的方式放在/sdcard/Download文件夹下,
(1)直接打开File(DocumentUI),直接进入Download文件夹, 点击应用无法安装
(2)点击左上角,选择Device -> Download,进入之后Download文件夹, 点击应用可以安装。
2. 分析
trace代码,判定这是android正常的设计,目的是确保下载APK的来源是经过用户同意后才能允许安装。
(1)当APK下载保存到设备后,MediaProvider数据库会保存apk的来源(owner_package_name),
比如 通过adb push的方式owner_package_name=com.android.shell;通过chrome浏览器下载的方式owner_package_name=com.android.chrome
(2)进入安装流程,区别在于发起源不同:
a. Files -> P3000 -> Download 是通过package:com.android.documentsui直接启动com.android.packageinstaller
b. Files -> Download 是package:com.android.documentsui转给package:com.android.providers.downloads.ui再启动com.android.packageinstaller
而在b的情况下,
系统会将apk对应的路径会从 下载未安装:raw:/storage/emulated/0/Download/XXX.apk 变为 下载已安装:msf:1000000044(数字为系统分配),这样做是因为android文件系统为了优化访问性能
在这个优化过程中,改变之前可以正常安装,改变之后安装需要验证来源的权限:
比如:使用chrome下载
如果是通过shell放入至设备,com.android.shell并没有申请Manifest.permission.REQUEST_INSTALL_PACKAGES的方式,所以安装就被拦截了;
同样,本地验证通过Settings -> Connected devices -> USB -> File Transfer的方式将APK放入Internal shared storage -> Download 安装同样也会被拦截;
这是因为有些进程没有UI申请Manifest.permission.REQUEST_INSTALL_PACKAGES的方式。
3. 结论
(1)确保Download文件夹中APK的来源是通过下载的方式
(2)其他方式存入设备的APK不要存放在Download文件夹
4. 修复方案
(1)不拦截
未知来源的APK,在两种情况下会拦截安装
a. originatingUid所对应的apk(也就是APK的来源)的targetSdkVersion < 0
b. originatingUid所对应的apk(也就是APK的来源)未申请权限Manifest.permission.REQUEST_INSTALL_PACKAGES
Index: frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
============================