当前位置: 首页 > news >正文

[RK3568][Android12.0]--- 系统自带预置第三方APK方法

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 parallelint fileCount = 0;for (File file : files) {final boolean isPackage = (isApkFile(file) || file.isDirectory())&& !PackageInstallerService.isStageName(file.getName());if (!isPackage) {// Ignore entries which are not packagescontinue;}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 appscontinue;}}}parallelPackageParser.submit(file, parseFlags);fileCount++;}// Process results one by onefor (; 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 namesif (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 appsif ((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
 

相关文章:

  • 使用jedis连接虚拟机redis报错 Failed to connect to any host resolved for DNS name
  • 冰点还原精灵Deep Freeze for mac版
  • 线程池ThreadPoolTaskExecutor实战
  • find和grep命令的简单使用
  • 【Vue原理解析】之插件系统
  • 深度学习名词总结
  • 【算法】最短路径——迪杰斯特拉 (Dijkstra) 算法
  • 解决java发邮件错误javax.net.ssl.SSLHandshakeException: No appropriate protocol
  • 亚马逊上架儿童睡衣,婴儿沐浴座椅,不锈钢儿童水杯被召回的原因解析
  • 终于有人把VMware虚拟机三种网络模式讲清楚了!
  • 【力扣刷题篇】栈与队列相关OJ题及题解
  • 中睿天下Coremail | 2023年Q3企业邮箱安全态势观察报告
  • rocketmq-exporter配置为系统服务-自启动
  • SQL对数据进行去重
  • Java自学第8课:电商项目(3) - 重新搭建环境
  • Angular4 模板式表单用法以及验证
  • css系列之关于字体的事
  • Java Agent 学习笔记
  • javascript 总结(常用工具类的封装)
  • LeetCode541. Reverse String II -- 按步长反转字符串
  • MySQL Access denied for user 'root'@'localhost' 解决方法
  • Python学习之路13-记分
  • Stream流与Lambda表达式(三) 静态工厂类Collectors
  • TypeScript实现数据结构(一)栈,队列,链表
  • use Google search engine
  • Vue2.x学习三:事件处理生命周期钩子
  • webpack入门学习手记(二)
  • 经典排序算法及其 Java 实现
  • 聚类分析——Kmeans
  • 看图轻松理解数据结构与算法系列(基于数组的栈)
  • 设计模式 开闭原则
  • 详解移动APP与web APP的区别
  • 项目管理碎碎念系列之一:干系人管理
  • 一些css基础学习笔记
  • 用Canvas画一棵二叉树
  • “十年磨一剑”--有赞的HBase平台实践和应用之路 ...
  • gunicorn工作原理
  • mysql面试题分组并合并列
  • 长三角G60科创走廊智能驾驶产业联盟揭牌成立,近80家企业助力智能驾驶行业发展 ...
  • #includecmath
  • #stm32整理(一)flash读写
  • (13):Silverlight 2 数据与通信之WebRequest
  • (WSI分类)WSI分类文献小综述 2024
  • (汇总)os模块以及shutil模块对文件的操作
  • (十八)三元表达式和列表解析
  • (一)spring cloud微服务分布式云架构 - Spring Cloud简介
  • ./configure、make、make install 命令
  • .apk 成为历史!
  • .cfg\.dat\.mak(持续补充)
  • .NET 2.0中新增的一些TryGet,TryParse等方法
  • .NET Core6.0 MVC+layui+SqlSugar 简单增删改查
  • .NET 中使用 Mutex 进行跨越进程边界的同步
  • //解决validator验证插件多个name相同只验证第一的问题
  • @EventListener注解使用说明
  • @FeignClient 调用另一个服务的test环境,实际上却调用了另一个环境testone的接口,这其中牵扯到k8s容器外容器内的问题,注册到eureka上的是容器外的旧版本...