Rockchip平台Android应用预安装功能(基于Android13)
1. 预安装应用类型
Android上的应用预安装功能,主要是指配置产品时,根据厂商要求,将事先准备好的第三方应用预置进Android系统。预安装分为以下几种类型:
- 安装不可卸载应用
- 安装可永久卸载应用
- 安装卸载后恢复出厂设置后自动恢复的应用
2. 功能启用说明
2.1 配置和使用
在进行以下操作之前,首先需要输入命令 get_build_var TARGET_DEVICE_DIR
来找到对应的目标文件夹(比如 device/rockchip/rk3126c/
)。
在目标文件夹下有三个文件夹,分别为:
- preinstall
- preinstall_del_forever
- preinstall_del
请将需要预置的应用放入对应的文件夹,注意apk文件名尽量使用英文,避免空格。
顺利的话,执行make
之后会在$OUT/oem
目录生成对应的文件夹:
- bundled_persist-app
- bundled_uninstall_gone-app
- bundled_uninstall_back-app
仍旧对应概述中的几种类型。在烧录后,系统会自动安装这些应用到对应目录。
注意:不支持带systemuid应用的预制,请使用Android原生方式编写mk文件。可参考vendor/rockchip/common/apps/RkDeviceTest/
的集成方式。
2.2 编译结果
编译后配置文件将会输出到odm分区($OUT/oem/
),增加后请确认烧写了odm.img
来使其生效。
3. 预编译脚本
预置应用编译脚本通过下面脚本完成
ifneq ($(strip $(TARGET_PRODUCT)), )
$(shell python device/rockchip/common/auto_generator.py $(TARGET_DEVICE_DIR) preinstall bundled_persist-app $(TARGET_ARCH))
$(shell python device/rockchip/common/auto_generator.py $(TARGET_DEVICE_DIR) preinstall_del bundled_uninstall_back-app $(TARGET_ARCH))
$(shell python device/rockchip/common/auto_generator.py $(TARGET_DEVICE_DIR) preinstall_del_forever bundled_uninstall_gone-app $(TARGET_ARCH))
-include $(TARGET_DEVICE_DIR)/preinstall/preinstall.mk
-include $(TARGET_DEVICE_DIR)/preinstall_del/preinstall.mk
-include $(TARGET_DEVICE_DIR)/preinstall_del_forever/preinstall.mk
endif
可以看到预置应用的Android.mk主要通过auto_generator.py
完成
#!/usr/bin/env python
import sys
import os
import re
import zipfile
import shutil
import logging
import string
templet = """include $(CLEAR_VARS)
LOCAL_MODULE := %s
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_PATH := $(TARGET_OUT_ODM)/%s
LOCAL_SRC_FILES := $(LOCAL_MODULE)$(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_DEX_PREOPT := false
LOCAL_ENFORCE_USES_LIBRARIES := false
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_JNI_SHARED_LIBRARIES_ABI := %s
MY_LOCAL_PREBUILT_JNI_LIBS := %s
MY_APP_LIB_PATH := $(TARGET_OUT_ODM)/%s/$(LOCAL_MODULE)/lib/$(LOCAL_JNI_SHARED_LIBRARIES_ABI)
ifneq ($(LOCAL_JNI_SHARED_LIBRARIES_ABI), None)
$(warning MY_APP_LIB_PATH=$(MY_APP_LIB_PATH))
LOCAL_POST_INSTALL_CMD := \
mkdir -p $(MY_APP_LIB_PATH) \
$(foreach lib, $(MY_LOCAL_PREBUILT_JNI_LIBS), ; cp -f $(LOCAL_PATH)/$(lib) $(MY_APP_LIB_PATH)/$(notdir $(lib)))
endif
include $(BUILD_PREBUILT)
"""
copy_app_templet = """LOCAL_PATH := $(my-dir)
include $(CLEAR_VARS)
LOCAL_APK_NAME := %s
LOCAL_POST_PROCESS_COMMAND := $(shell mkdir -p $(TARGET_OUT_ODM)/%s/$(LOCAL_APK_NAME) && cp $(LOCAL_PATH)/$(LOCAL_APK_NAME).apk $(TARGET_OUT_ODM)/%s/$(LOCAL_APK_NAME)/)
"""
def main(argv):
preinstall_dir = os.path.join(argv[1],argv[2])
if os.path.exists(preinstall_dir):
#Use to include modules
isfound = 'not_found_lib'
include_path = preinstall_dir + '/preinstall.mk'
android_path = preinstall_dir + '/Android.mk'
target_arch = argv[4]
if os.path.exists(include_path):
os.remove(include_path)
if os.path.exists(android_path):
os.remove(android_path)
includefile = file(include_path, 'w')
androidfile = file(android_path, 'w')
androidfile.write("include $(call all-subdir-makefiles)\n\n")
MY_LOCAL_PREBUILT_JNI_LIBS = '\\' + '\n'
for root, dirs, files in os.walk(preinstall_dir):
for file_name in files:
p = re.compile(r'\S*(?=.apk\b)')
found = p.search(file_name)
if found:
include_apk_path = preinstall_dir + '/' + found.group()
makefile_path = include_apk_path + '/Android.mk'
apk = preinstall_dir + '/' + found.group() + '.apk'
try:
zfile = zipfile.ZipFile(apk,'r')
except:
if os.path.exists(include_apk_path):
shutil.rmtree(include_apk_path)
os.makedirs(include_apk_path)
apkpath = preinstall_dir + '/' + found.group() + '/'
shutil.move(apk,apkpath)
makefile = file(makefile_path,'w')
makefile.write("LOCAL_PATH := $(my-dir)\n\n")
makefile.write(templet % (found.group(),argv[3],'None',MY_LOCAL_PREBUILT_JNI_LIBS,argv[3]))
continue
for lib_name in zfile.namelist():
include_apklib_path = include_apk_path + '/lib' + '/arm'
if os.path.exists(include_apk_path):
shutil.rmtree(include_apk_path)
os.makedirs(include_apklib_path)
makefile = file(makefile_path,'w')
makefile.write("LOCAL_PATH := $(my-dir)\n\n")
apkpath = preinstall_dir + '/' + found.group() + '/'
if target_arch == 'arm64':
for lib_name in zfile.namelist():
lib = re.compile(r'\A(lib/arm64-v8a/)+?')
find_name = 'lib/arm64-v8a/'
if lib_name.find(find_name) == -1:
continue
libfound = lib.search(lib_name)
if libfound:
isfound = 'arm64-v8a'
data = zfile.read(lib_name)
string = lib_name.split(libfound.group())
libfile = include_apklib_path + '/' + string[1]
MY_LOCAL_PREBUILT_JNI_LIBS += '\t' + 'lib/arm64' + '/' + string[1] + '\\' + '\n'
if (os.path.isdir(libfile)):
continue
else:
includelib = file(libfile, 'w')
includelib.write(data)
try:
if cmp(isfound, 'not_found_lib'):
include_apklib_path_arm64 = include_apk_path + '/lib/arm64'
os.rename(include_apklib_path, include_apklib_path_arm64)
except Exception as e:
logging.warning('rename dir faild for:' + e)
if not cmp(isfound,'not_found_lib'):
for lib_name in zfile.namelist():
lib = re.compile(r'\A(lib/armeabi-v7a/)+?')
find_name = 'lib/armeabi-v7a/'
#if not cmp(lib_name,find_name):
# continue
if lib_name.find(find_name) == -1:
continue
libfound = lib.search(lib_name)
if libfound:
isfound = 'armeabi-v7a'
data = zfile.read(lib_name)
string = lib_name.split(libfound.group())
libfile = include_apklib_path + '/' + string[1]
MY_LOCAL_PREBUILT_JNI_LIBS += '\t' + 'lib/arm' + '/' + string[1] + '\\' + '\n'
if(os.path.isdir(libfile)):
continue
else:
includelib = file(libfile,'w')
includelib.write(data)
if not cmp(isfound,'not_found_lib'):
for lib_name in zfile.namelist():
lib = re.compile(r'\A(lib/armeabi/)+?')
find_name = 'lib/armeabi/'
#if not cmp(lib_name,find_name):
# continue
if lib_name.find(find_name) == -1:
continue
libfound = lib.search(lib_name)
if libfound:
data = zfile.read(lib_name)
string = lib_name.split(libfound.group())
libfile = include_apklib_path + '/' + string[1]
MY_LOCAL_PREBUILT_JNI_LIBS += '\t' + 'lib/arm' + '/' + string[1] + '\\' + '\n'
if(os.path.isdir(libfile)):
continue
else:
includelib = file(libfile,'w')
includelib.write(data)
tmp_jni_libs = '\\' + '\n'
if not cmp(MY_LOCAL_PREBUILT_JNI_LIBS,tmp_jni_libs):
nolibpath = preinstall_dir + '/' + found.group() + '/lib'
shutil.rmtree(nolibpath)
makefile.write(templet % (found.group(),argv[3],'None',MY_LOCAL_PREBUILT_JNI_LIBS,argv[3]))
else:
if isfound == 'arm64-v8a':
makefile.write(templet % (found.group(),argv[3], 'arm64', MY_LOCAL_PREBUILT_JNI_LIBS,argv[3]))
else:
makefile.write(templet % (found.group(),argv[3],'arm',MY_LOCAL_PREBUILT_JNI_LIBS,argv[3]))
shutil.move(apk,apkpath)
isfound = 'not_found_lib'
MY_LOCAL_PREBUILT_JNI_LIBS = '\\' + '\n'
makefile.close()
break
for root, dirs,files in os.walk(preinstall_dir):
for dir_file in dirs:
includefile.write('PRODUCT_PACKAGES += %s\n' %dir_file)
break
includefile.close()
if __name__=="__main__":
main(sys.argv)
最终生成的Android.mk脚本如下:
LOCAL_PATH := $(my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := MySysManager
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_PATH := $(TARGET_OUT_ODM)/bundled_persist-app
LOCAL_SRC_FILES := $(LOCAL_MODULE)$(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_DEX_PREOPT := false
LOCAL_ENFORCE_USES_LIBRARIES := false
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_JNI_SHARED_LIBRARIES_ABI := None
MY_LOCAL_PREBUILT_JNI_LIBS := \
MY_APP_LIB_PATH := $(TARGET_OUT_ODM)/bundled_persist-app/$(LOCAL_MODULE)/lib/$(LOCAL_JNI_SHARED_LIBRARIES_ABI)
ifneq ($(LOCAL_JNI_SHARED_LIBRARIES_ABI), None)
$(warning MY_APP_LIB_PATH=$(MY_APP_LIB_PATH))
LOCAL_POST_INSTALL_CMD := mkdir -p $(MY_APP_LIB_PATH) $(foreach lib, $(MY_LOCAL_PREBUILT_JNI_LIBS), ; cp -f $(LOCAL_PATH)/$(lib) $(MY_APP_LIB_PATH)/$(notdir $(lib)))
endif
include $(BUILD_PREBUILT)
PackageManagerService对预安装应用的处理
//PackageManagerService.java
public static final String BUNDLED_PERSIST_DIR = "/odm/bundled_persist-app";
public static final String BUNDLED_UNINSTALL_GONE_DIR = "/odm/bundled_uninstall_gone-app";
//Environment.java
private static final File DIR_PREBUNDLED_UNINSTALL_BACK_ROOT = getDirectory(
ENV_PREBUNDLED_UNINSTALL_BACK_ROOT, "/odm/bundled_uninstall_back-app");
private static final File DIR_PREBUNDLED_UNINSTALL_GONE_ROOT = getDirectory(
ENV_PREBUNDLED_UNINSTALL_GONE_ROOT, "/odm/bundled_uninstall_gone-app");
//InitAppsHelper.java
public void preinstallThirdPartyAPK(PackageParser2 packageParser, ExecutorService executorService,int scanFlags){
preinstallPrebundledpersist(packageParser,executorService,scanFlags);
preinstallPrebundledUninstallBack(packageParser,executorService,scanFlags);
preinstallPrebundledUninstallGone(packageParser,executorService,scanFlags);
}
private void preinstallPrebundledpersist(PackageParser2 packageParser, ExecutorService executorService,int scanFlags){
scanDirTracedLI(new File(mPm.BUNDLED_PERSIST_DIR),null,
mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR
| ParsingPackageUtils.PARSE_IS_PREINSTALL,
scanFlags | mPm.SCAN_AS_PREINSTALL
| SCAN_AS_SYSTEM,
packageParser, executorService);
}
private void preinstallPrebundledUninstallBack(PackageParser2 packageParser, ExecutorService executorService,int scanFlags){
scanDirTracedLI(Environment.getPrebundledUninstallBackDirectory(),null,
mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_IS_PREBUNDLED_DIR,
scanFlags | mPm.SCAN_AS_PREBUNDLED_DIR,
packageParser, executorService);
}
private void preinstallPrebundledUninstallGone(PackageParser2 packageParser, ExecutorService executorService,int scanFlags){
scanDirTracedLI(Environment.getPrebundledUninstallGoneDirectory(),null,
mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_IS_PREBUNDLED_DIR,
scanFlags | mPm.SCAN_AS_PREBUNDLED_DIR,
packageParser, executorService);
}
需要进一步了解预安装应用相关流程,请查看preinstallPrebundled
相关代码流程。