1 原理
在Android源码中添加一个可卸载APP的普通应用(该应用是 需要一个用于集成的apk文件、至于Android.mk文件可以有但非必要),和系统级应用集成不同的是LOCAL_CERTIFICATE变量值的不同,系统级应用对应的是platform,而普通可卸载应用对应的是testkey(注意:应用输出的路径要改变),但仅仅如此还不够。因为普通应用不是直接拷贝到 /data/app 目录下就可以的,还是需要安装这一步骤的。
这里使用一个新思路,将普通应用安装包放到system分区下,开机后启动一个脚本程序preload_app.sh,在系统启动后(注意:如果开机就执行pm的 安装命令是启动不了的,因为还未彻底初试化完毕)执行如下逻辑:
- 如果是第一次开机或 将恢复出厂设置/升级,则安装该应用。
- 如果不是第一次开机,则不安装该用。
这样就确保即使恢复了出厂设置应用还在,同时保证安装应用后可以被自由卸载/升级。
2 修改方案(Android O P Q)
2.1 添加第三方应用apk文件
在${AOSP}/package/apps/路径下 添加一个普通可卸载应用Taobao.,添加对应的Android.mk文件,如下所示:
LOCAL_PATH:=$(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := Taobao.apk
LOCAL_MODULE_CLASS := APPS
#可以为user、eng、tests、optional,optional代表在任何版本下都编译
LOCAL_MODULE_TAGS := optional
#编译模块的名称
LOCAL_MODULE := Taobao
#可以为testkey、platform、shared、media、PRESIGNED(使用原签名),platform代表为系统应用
LOCAL_CERTIFICATE := testkey
#应用输出路径,此处为data/app
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
#module的后缀,可不设置
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_DEX_PREOPT := false #不进行预先优化,一般第三方应用会避免优化,而导致APP各种异常
include $(BUILD_PREBUILT)
接下来在该目录下 添加对应的 package/apps/Taobao/Taobao.apk文件。这样该目录就完成了编辑。
然后我们再修改下系统的配置文件(根据项目的实际需求,这里仅采用一种通用的方式供参考)同时修改文件
build/make/target/product/sdk_base.mk
build/make/target/product/generic_no_telephony.mk
给PRODUCT_PACKAGES 环境变量 添加新任务,如下所示:
@@ -49,7 +49,8 @@ PRODUCT_PACKAGES := \
EasterEgg \
WallpaperPicker \
WidgetPreview \
+ Taobao \
2.2 在framework层添加preload_app.sh
添加文件为{AOSP}/frameworks/base/data/cmds/preload_app/目录,在该目录下添加文件
preload_app.sh,内容为:
#!/system/bin/sh
if [ ! -f /data/app/did ]; then
PRELOAD_SOURCE=/system/preload-app
files=$(ls ${PRELOAD_SOURCE}/)
for i in ${files};do
#pm install -g ${PRELOAD_SOURCE}/${i}
pm install ${PRELOAD_SOURCE}/${i}
#chmod 755 ${PRELOAD_DEST}/${i}
done
echo 1 > /data/app/did
fi
同时添加Android.mk文件,内容如下:
# Copyright 2013 The Android Open Source Project
#
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := preload_app.sh
LOCAL_SRC_FILES := preload_app.sh
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_MODULE_TAGS := optional
include $(BUILD_PREBUILT)
同时配置关键环境变量如下(这里仅给出参考的关键变量,一般是在aosp/device/目录下添加):
PRODUCT_PACKAGES += \
preload_app.sh
PRODUCT_COPY_FILES += \
frameworks/base/cmds/preload_app/preload_app.sh:$(TARGET_COPY_OUT_SYSTEM)/bin/preload_app.sh \
#针对要拷贝的apk,需要添加
PRODUCT_COPY_FILES += \
packages/apps/X1/X1.apk:$(TARGET_COPY_OUT_SYSTEM)/preload-app/X1.apk \
packages/apps/X2/X2.apk:$(TARGET_COPY_OUT_SYSTEM)/preload-app/X2.apk \
//...
packages/apps/XN/XN.apk:$(TARGET_COPY_OUT_SYSTEM)/preload-app/XN.apk \
注意:这里在配置后编译时会遇到这种问题:
build/make/core/Makefile:61: error: Prebuilt apk found in PRODUCT_COPY_FILES: packages/apps/XXX/XXX.apk:system/preload-app/XXX.apk, use BUILD_PREBUILT instead!.
此时需要修改build/core/Makefile文件,找到如下位置,注释掉apk校验的部分,如下所示:
define check-product-copy-files
$(if $(filter-out $(TARGET_COPY_OUT_SYSTEM_OTHER)/%,$(2)), \
# $(if $(filter %.apk, $(2)),$(error \
# Prebuilt apk found in PRODUCT_COPY_FILES: $(1), use BUILD_PREBUILT instead!)))
...
修正后该问题解决。
2.3 配置SELinux
修改文件 device/qcom/sepolicy/generic/private/file_contexts,内容添加如下:
/system/bin/preload_app.sh u:object_r:preload_app_exec:s0
添加文件 device/qcom/sepolicy/generic/private/preload_app.te,内容添加如下:
#preload_app_copy_exec
type preload_app, domain;
type preload_app_exec, system_file_type, exec_type, file_type;
#init_domain(preload_app)
allow preload_app app_data_file:file{ read write getattr };
2.4 开机启动服务相关修改
修改对应的init.rc文件,同时在系统启动完成后添加如下内容:
#property start
on property:sys.boot_completed=1
start preload_app
#add bin of shell script
service preload_app /system/bin/preload_app.sh
class main
user root
group root
seclabel u:r:preload_app:s0
disabled
oneshot
主要是确保启动后可以根据需要来设置属性,进而启动该脚本。