Platform: RK3568
OS: Android 12.0
Kernel: 4.19
Rockchip默认提供了机制来预置第三方APK, 方法很简单:
1. 在device/rockchip/rk3568创建preinstall目录(如果要可卸载,那就创建preinstall_del目录)
2. 将你要预安装的APK放进此目录即可
preinstall 不可卸载
preinstall_del 可卸载,回复出厂可恢复
preinstall_del_forever 可卸载 恢复出厂不可恢复
下面看下实现原理过程:
device/rockchip/common/device.mk中有:
# Prebuild apps
$(call inherit-product, device/rockchip/common/modules/preinstall.mk)
device\rockchip\common\modules\preinstall.mk
# Include this makefile to support prebuild apps
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
auto_generator.py是个python脚本,用于生成Android.mk和preinstall.mk文件,
def main(argv):
preinstall_dir = os.path.join(argv[1] + '/' + argv[2])
if os.path.exists(preinstall_dir):
#Use to define modules for install
makefile_path = preinstall_dir + '/Android.mk'
#Use to include modules
include_path = preinstall_dir + '/preinstall.mk'
if os.path.exists(makefile_path):
os.remove(makefile_path)
if os.path.exists(include_path):
os.remove(include_path)
makefile = file(makefile_path, 'w')
includefile = file(include_path, 'w')
makefile.write("LOCAL_PATH := $(my-dir)\n\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:
makefile.write(templet %(found.group(), argv[2]))
includefile.write('PRODUCT_PACKAGES += %s\n' %found.group())
makefile.close()
includefile.close()
Android.mk用于制定编译规则,如我在preinstall目录下放了个AVSourceTester.apk,那么生成的文件内容是
LOCAL_PATH := $(my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := AVSourceTester
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_PATH := $(TARGET_OUT)/preinstall
LOCAL_SRC_FILES := $(LOCAL_MODULE)$(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_DEX_PREOPT := false
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
include $(BUILD_PREBUILT)
preinstall.mk内容如下:
PRODUCT_PACKAGES += AVSourceTester
编译系统之后,生成路径是
out/target/product/rk3568/system/preinstall/AVSourceTester/AVSourceTester.apk
系统开机之后会调用
frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java
preinstallThirdPartyAPK(packageParser,executorService,scanFlags);
private 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(BUNDLED_PERSIST_DIR),
mDefParseFlags | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR
| ParsingPackageUtils.PARSE_IS_PREINSTALL,
scanFlags | SCAN_AS_PREINSTALL
| SCAN_AS_SYSTEM,
0,packageParser, executorService);
}
private void preinstallPrebundledUninstallBack(PackageParser2 packageParser, ExecutorService executorService,int scanFlags){
scanDirTracedLI(Environment.getPrebundledUninstallBackDirectory(),
mDefParseFlags | ParsingPackageUtils.PARSE_IS_PREBUNDLED_DIR,
scanFlags | SCAN_AS_PREBUNDLED_DIR,
0,packageParser, executorService);
}
private void preinstallPrebundledUninstallGone(PackageParser2 packageParser, ExecutorService executorService,int scanFlags){
scanDirTracedLI(Environment.getPrebundledUninstallGoneDirectory(),
mDefParseFlags | ParsingPackageUtils.PARSE_IS_PREBUNDLED_DIR,
scanFlags | SCAN_AS_PREBUNDLED_DIR,
0,packageParser, executorService);
}
private static final String BUNDLED_PERSIST_DIR = "/odm/bundled_persist-app";
private static final String BUNDLED_UNINSTALL_GONE_DIR = "/odm/bundled_uninstall_gone-app";
private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags,
long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
try {
scanDirLI(scanDir, parseFlags, scanFlags, currentTime, packageParser, executorService);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime,
PackageParser2 packageParser, ExecutorService executorService) {
final File[] files = scanDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
Log.d(TAG, "No files in app dir " + scanDir);
return;
}
if (DEBUG_PACKAGE_SCANNING) {
Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags
+ " flags=0x" + Integer.toHexString(parseFlags));
}
ArrayList<String> list = new ArrayList<String>();
boolean isPrebundled = (parseFlags & ParsingPackageUtils.PARSE_IS_PREBUNDLED_DIR) != 0;
if (isPrebundled) {
synchronized (mPackages) {
mSettings.readPrebundledPackagesLPr();
}
}
if (scanDir.getAbsolutePath().contains(BUNDLED_UNINSTALL_GONE_DIR)) {
if (!readDeleteFile(list)) {
Log.e(TAG, "read data failed");
return;
}
}
ParallelPackageParser parallelPackageParser =
new ParallelPackageParser(packageParser, executorService);
// Submit files for parsing in parallel
int fileCount = 0;
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
// Ignore entries which are not packages
continue;
}
if (file.getAbsolutePath().contains(BUNDLED_UNINSTALL_GONE_DIR)) {
if (list != null && list.size() > 0) {
final boolean isdeleteApk = isDeleteApk(file,parseFlags,list);
if (isdeleteApk) {
// Ignore deleted bundled apps
continue;
}
}
}
parallelPackageParser.submit(file, parseFlags);
fileCount++;
}
// Process results one by one
for (; fileCount > 0; fileCount--) {
ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
Throwable throwable = parseResult.throwable;
int errorCode = PackageManager.INSTALL_SUCCEEDED;
String errorMsg = null;
if (throwable == null) {
// TODO(toddke): move lower in the scan chain
// Static shared libraries have synthetic package names
if (parseResult.parsedPackage.isStaticSharedLibrary()) {
renameStaticSharedLibraryPackage(parseResult.parsedPackage);
}
try {
addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
currentTime, null);
if (isPrebundled) {
final PackageParser.Package pkg;
try {
pkg = new PackageParser().parsePackage(parseResult.scanFile, parseFlags);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
}
synchronized (mPackages) {
mSettings.markPrebundledPackageInstalledLPr(pkg.packageName);
}
}
} catch (PackageManagerException e) {
errorCode = e.error;
errorMsg = "Failed to scan " + parseResult.scanFile + ": " + e.getMessage();
Slog.w(TAG, errorMsg);
}
} else if (throwable instanceof PackageParserException) {
PackageParserException e = (PackageParserException)
throwable;
errorCode = e.error;
errorMsg = "Failed to parse " + parseResult.scanFile + ": " + e.getMessage();
Slog.w(TAG, errorMsg);
} else {
throw new IllegalStateException("Unexpected exception occurred while parsing "
+ parseResult.scanFile, throwable);
}
if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0 && errorCode != INSTALL_SUCCEEDED) {
mApexManager.reportErrorWithApkInApex(scanDir.getAbsolutePath(), errorMsg);
}
// Delete invalid userdata apps
if ((scanFlags & SCAN_AS_SYSTEM) == 0
&& errorCode != PackageManager.INSTALL_SUCCEEDED) {
logCriticalInfo(Log.WARN,
"Deleting invalid package at " + parseResult.scanFile);
removeCodePathLI(parseResult.scanFile);
}
}
if (isPrebundled) {
synchronized (mPackages) {
mSettings.writePrebundledPackagesLPr();
}
}
}
关键函数是copyPackagesToAppInstallDir(),它会把preinstall目录下的安装文件copy到安装目录。
这样安装就成功了。
安装preinstall和preinstall_del的区别在于后者在安装完之后会删除系统目录下的apk,因此要是做了恢复出厂设置或者卸载动作,那就不能恢复了。
删除函数是deletePreinstallDir(),通过init中的ctl命令实现。
private void deletePreinstallDir(File dir) {
String[] files = dir.list();
if (files != null) {
Slog.d(TAG, "Ready to cleanup preinstall");
SystemProperties.set("ctl.start", "preinst_clr");
}
}
不过在source code中并没有找到preinst_clr这个service,可以在init.rc中自己添加下,
参考的是 Nu3001/device_rockchip_rksdk
service preinst_clr /system/bin/preinstall_cleanup.sh
disabled
oneshot
preinstall_cleanup.sh这个文件默认是有的,本质是直接删除apk。
#!/system/bin/sh
log -t PackageManager "Start to clean up /system/preinstall_del/"
mount -o rw,remount -t ext4 /system
rm system/preinstall_del/*.*
mount -o ro,remount -t ext4 /system