背景:基本 Android14操作系统内置了一些第三方APP应用,发现首次刷完机开机时间很久,同时内置的一些第三方App也不支持卸载,所以想优化下。
基本思想:把内置的一些apk打包到固件里,系统启动成功后通过脚本安装这些apk,不占用首次开机安装时间,同时也要支持卸载。
实现步骤:
修改第三方apk配置文件
添加安装apk脚本
修改init.rc执行文件
adb调试方法
1. 修改apk配置文件即Android.mk,随机找个Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
$(shell mkdir -p $(TARGET_OUT)/normalapp)
$(shell cp -r $(LOCAL_PATH)/Facebook.apk $(TARGET_OUT)/normalapp)
TARGET_OUT对应手机端路径是system目录
$(shell mkdir -p $(TARGET_OUT)/normalapp) 通过shell命令在system目录下创建子目录 normalapp
$(shell cp -r $(LOCAL_PATH)/Facebook.apk $(TARGET_OUT)/normalapp) 把当前目录的Facebook.apk 复制到手机端的 system/normalapp
以上配置作用就是把apk打包到固件里,而当刷机系统启动后 apk 存储对应的目录是system/normalapp,后面安装脚本就是到这个目录去找apk并安装。
2. 添加安装apk脚本 install_apks.sh,代码目录device/linaro/poplar/install_apks.sh,生成在手机目录是system/bin/
#!/system/bin/sh
FLAG_PROP="persist.sys.install_flag"
FLAG_VALUE="installed"
LOG_TAG="APK_INSTALLER"
# 函数用于记录日志到logcat
log_message() {
log -t $LOG_TAG "$1"
}
# 检查是否已经安装过
installed_status=$(getprop "$FLAG_PROP")
if [ "$installed_status" = "$FLAG_VALUE" ]; then
log_message "APK files already installed. Skipping..."
exit 0
fi
# 定义APK文件列表
APK_FILES=("Facebook.apk" "Weather.apk" "Twitter.apk" "TikTok.apk" "XClub.apk" "ShareMe.apk" "Instagram.apk")
# 记录开始时间
START_TIME=$(date +%s)
# 输出日志,显示开始安装信息
log_message "Starting APK installation process..."
# 安装每个APK文件
for APK in "${APK_FILES[@]}"; do
# 获取APK文件的完整路径
APK_PATH="/system/normalapp/$APK"
# 检查APK文件是否存在
if [ -f "$APK_PATH" ]; then
log_message "Installing $APK from: $APK_PATH"
# 安装APK文件
pm install -r "$APK_PATH"
# 检查安装结果
if [ $? -eq 0 ]; then
log_message "Successfully installed $APK"
# 删除已安装的APK文件
# rm -f "$APK_PATH"
else
log_message "Failed to install $APK"
fi
else
log_message "$APK_PATH does not exist. Skipping installation of $APK."
fi
done
# 记录结束时间
END_TIME=$(date +%s)
# 计算安装所需时间(秒)
DURATION=$((END_TIME - START_TIME))
# 输出日志,显示安装完成信息和所需时间
log_message "APK installation completed in $DURATION seconds."
# 设置 System Property 状态为已安装
setprop "$FLAG_PROP" "$FLAG_VALUE"
2.1 判断apk是否安装完成是通过persist.sys.install_flag和installed来控制的,通过执行 adb shell getprop persist.sys.install_flag来判断安装状态,如果返回installed 代表APK已经安装完成.
# 函数用于记录日志到logcat
log_message() {
log -t $LOG_TAG "$1"
}
这个函数对于调试看日志很方便,作用是把日志输出到logcat控制台,因为是基于展讯平台,我是直接抓展讯ylog日志看APK安装状态,还可以通过 adb logcat -v time > 日志保存路径.
2.2 上面脚本定义好之后,还需把该脚本复制到对应的手机目录
再定义一个配置文件device/linaro/poplar/kxd_customer.mk,内容如下:
PRODUCT_COPY_FILES += device/linaro/poplar/install_apks.sh:/system/bin/install_apks.sh
同时在build/make/target/product/base_system.mk引用kxd_customer.mk
--- a/build/make/target/product/base_system.mk
+++ b/build/make/target/product/base_system.mk
@@ -437,3 +437,5 @@ PRODUCT_COPY_FILES += $(call add-to-product-copy-files-if-exists,\
$(call inherit-product, $(SRC_TARGET_DIR)/product/runtime_libart.mk)
$(call inherit-product, vendor/sprd/open-source/res/bootani/boot_res.mk)
+$(call inherit-product, device/linaro/poplar/kxd_customer.mk)
3. 修改init.rc执行文件,配置install_apks.sh何时启动
diff --git a/system/core/rootdir/init.rc b/system/core/rootdir/init.rc
index e800a0d..acdba2d 100644
--- a/system/core/rootdir/init.rc
+++ b/system/core/rootdir/init.rc
@@ -1383,6 +1383,19 @@ on userspace-reboot-fs-remount
remount_userdata
start bootanim
+service install_apks /system/bin/sh /system/bin/install_apks.sh
+ class main
+ disabled
+ user root
+ group root
+ oneshot
+ seclabel u:r:shell:s0
+
+on property:sys.boot_completed=1
+ start install_apks
+
+
on userspace-reboot-resume
trigger userspace-reboot-fs-remount
trigger post-fs-data
在init.rc执行文件中定义了安装脚本启动时间,权限等.
配置过class main 和 class core 都可以,class core 是指核心服务,早期启动的服务,优先级高于class main,但是配置class main 足够使用.
class main 指定服务install_apks在系统启动过程中来启动服务 .
disabled 意味着服务默认是禁用的,需要触发条件才会启动,这个服务的启动条件用了on property:sys.boot_completed=1 ,系统启动完成的时候启动脚本服务.
oneshot: 表示这个服务只会运行一次,完成任务后就会停止,而不会在后台持续运行.
seclabel u:r:shell: 指定服务的 SELinux 安全标签。u:r:shell:s0
表示这个服务的进程安全上下文设置为 shell
用户的权限级别,这通常用于 shell 脚本或命令的执行。
4. adb调试节省大量时间,否则每次修改脚本或init.rc每次需要编译较麻烦.
4.1 修改install_apks.sh,先把脚本pull到电脑桌面,修改后更新及验证:
adb root
adb remount
adb push xxx/install_apks.sh /system/bin/
# 赋予脚本权限
adb shell chmod +x /system/bin/install_apks.sh
# 执行脚本
adb shell /system/bin/install_apks.sh
4.2 修改init.rc文件,手机设备端路径是/system/etc/init/hw/init.rc,修改完push进手机
adb root
adb remount
adb push xxxx/init.rc /system/etc/init/hw/
adb reboot
adb 调试完后再编译固件刷到手机看效果:由于第三方apk比较多,用这种方式处理后首次刷机开机节省约1分钟,同时apk也支持卸载比较灵活。