Android 第三方 APK 包的静默安装

(原创,转载请注明出处! http://www.cnblogs.com/Martinium/articles/mirage_install.html )

    有一天,客户提出了这样一个需求。提供一定数目的 APK 包,要求预先安装到系统里,并且可以卸载。很显然,这些 APK 只能 install 到 /data/app/ 目录下,push 到 /system/app/ 是不能卸载的。

    大家心里肯定在想,如果能把这些 APKs 直接烧录到镜像文件里去就好了,最好是 data.img,烧录完成后一启动,那些APK就安静地躺在 /data/app/ 那里了。可惜这样行不通。源码编译出来的APK都是放在  /system/app/ 目录下的,这样用户那边又卸载不了。

    如果手头有 adb 工具,一切也好办,一条命令解决。

find <dir> -name "*\.apk" -exec adb install {} \;

<dir> 替换成自己的那些 APK 所在的目录。adb 需要环境变量 PATH 能搜索到,或者用 adb 的全路径。

    可是现在工厂批量生产,没有提供 adb 工具,或者某些客户先前禁止了 adb 打开的功能选项。于是选择从 SD 卡安装。待用户手动点击,一个一个地点击安装,确认,安装完成,这效率多低下啊!如果另外自己写一个 APK,头一次开机绑定一个 service,在 AndroidManifest.xml 里添加 permission,

<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
<uses-permission android:name="android.permission.CLEAR_APP_CACHE" />
<uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />

在第一次启动的时候安装,也就完事了。

可客户又说了,不希望看到多余的 APK 喔,于是打算修改下 category ,将
<category android:name="android.intent.category.LAUNCHER" />
改成
<category android:name="android.intent.category.DEFAULT" />
客户说不行,在 Settings->Apps->ALL 下面仍然看得到。而且卸载不了。我知道自己写的这个APK必须有system权限才能悄悄的安装的,于是卸载不了。我想把这个 service 插入到某个系统 APK(如packages/app/PackageInstaller)来堵住他的口,心里更想着怎么揍他一顿。

好吧,冷静,冷静,让我再想想其他办法。

在 PC 上,是用adb (Android Debug Bridge,源码路径 system/core/adb/)命令安装;
在 Android 设备上,可以用 pm (PackageManger ,源码路径 frameworks/base/cmds/pm/)命令呀!
最终,我写了一个简单的脚本,取名 mirage_install.sh

#!/system/bin/sh

MARK=/data/local/FACTORY_RESET
PKGS=/mnt/external_sd/mirage_package

if [ ! -e $MARK ]; then
echo "booting the first time, so pre-install some APKs."

busybox find $PKGS -name "*\.apk" -exec sh /system/bin/pm install {} \;

# NO NEED to delete these APKs since we keep a mark under data partition.
# And the mark will be wiped out after doing factory reset, so you can install
# these APKs again if files are still there. 
# busybox rm -rf $PKGS

touch $MARK
echo "OK, installation complete."
fi

    /mnt/external_sd/mirage_package 对应 SD 卡那些 APK 的文件夹,Android 分存储设备为 Internal Storage 和 External Storage,后者对应 SD。其中设置文件 /data/local/FACTORY_RESET 为标志位“是否是首次启动”。首次启动会检测路径是否存在并静默安装,以后启动的话无影响。除非通过恢复出厂设置(factory reset)来擦除这个脏位,清零后没有第三方应用了,于是又可以再次安装了,所以不用删除 SD 卡的那些包。
    这里用到了 busybox 工具,可以自己下载 busybox 源码编译生成,或者修改 frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java 文件,大概思路是:判断 路径 /mnt/external_sd/mirage_package 存在与否,然后调用文件里面的 install 函数安装。不过最好不要改动源码。

    接下来,我尝试在 init.rc 文件里添加这个 service 成功。

service mirage_install /system/bin/sh /system/bin/mirage_install.sh
    user root
    group root
    disabled
    oneshot

on property:init.svc.bootanim=stopped
    start mirage_install

这个服务的名字叫 mirage_install,执行 sh /system/bin/mirage_install.sh 这个脚本。
有关 Android Init Language 的讲解在源码 system/core/init/readme.txt 中,可以自己查看意思。
这里 oneshot 表示 Do not restart the service when it exits.
而且在ANDROID开机动画停止后(init.svc.bootanim=stopped)启动该项服务。尝试过其他条件,比如 dev.bootcomplete=1,也就是在 kernel 启动完成后,这样是不行的,有关的系统服务还没有起来,关于其他的 property,可以在控制台下用 getprop 查看, setprop 修改.

注意给 mirage_install.sh 文件加执行权限 chmod a+x mirage_install.sh
然后在 device/<company>/<product>/device.mk 文件里添加下面一行

PRODUCT_COPY_FILES += \
$(LOCAL_PATH)/system/bin/mirage_install.sh:system/bin/mirage_install.sh

冒号左边对应 mirage_install.sh 文件的相对路径,根据自己放置的路径而定,这里的 LOCAL_PATH 就是 device.mk 所在的文件夹,右边固定为 system/bin/mirage_install.sh,表示将复制到 Android 设备上的 /system/bin/mirage_install.sh 路径下。

    重新编译 Android 工程,将 APK 包放到 SD 卡的 mirage_package 路径下,然后烧录生成的 *.img文件,看到了 APK 的静默安装,OK,大工告成!

原文地址:https://www.cnblogs.com/Martinium/p/mirage_install.html