当前位置: 首页 > 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
 

相关文章:

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

Platform: RK3568 OS: Android 12.0 Kernel: 4.19 Rockchip默认提供了机制来预置第三方APK, 方法很简单&#xff1a; 1. 在device/rockchip/rk3568创建preinstall目录(如果要可卸载&#xff0c;那就创建preinstall_del目录) 2. 将你要预安装的APK放进此目录即可 preinstall 不…...

数据分析场景下,企业如何做好大模型选型和落地?

在数据驱动的数字化时代&#xff0c;有效的数据分析已成为企业成功的关键因素。而随着大模型带来能力突破&#xff0c;让AI与数据分析相互结合&#xff0c;使分析结果更好支撑业务&#xff0c;促进企业内部数据价值释放&#xff0c;成为了当下企业用户尤为关注的话题。 如何按照…...

使用VScode编译betaflight固件--基于windows平台

使用VScode编译betaflight固件--基于windows平台 1、使用git克隆betaflight的开源代码2、betaflight的代码框架分析&#xff1a;3、配置编译环境&#xff1a;4、VScode上编译 betaflight不仅可以在LInux上进行编译也可以在Windows上编译&#xff0c;本文主要介绍在windows平台上…...

OkHttp网络请求读写超时

查看OkHttp的源码&#xff1a; OkHttpClient 的 Builder() public Builder() {...callTimeout 0;connectTimeout 10_000;readTimeout 10_000;writeTimeout 10_000;... } callTimeout&#xff1a;整个请求的超时时间&#xff0c;如果设置了这个值&#xff0c;则总超时时间…...

@postmapping 定义formdata传参方式

背景&#xff1a;feign声明接口&#xff0c;传对象&#xff0c; 但是对象那边没有用requestBody接收&#xff1b; 前端调它也是走的formdata&#xff0c;所以不改变源代码&#xff0c;以及补新接口的情况下&#xff0c;我也需要formdata传参&#xff1b; 不然数据传不过去会为空…...

Windows客户端开发框架WPF简介

一、WPF简介 WPF的全称是Windows Presentation Foundation&#xff0c;WPF是 Microsoft 提供的一种用于构建桌面应用程序的 UI 框架。它包含在 .NET Framework 中&#xff0c;从 .NET 3.0 版本开始就被引入。 以下是一些关于 WPF 的关键特性&#xff1a; 1. XAML&#xff1a…...

2023NOIP A层联测32 sakuya

题目大意 有一棵有 n n n个节点的树&#xff0c;每条边有一个边权 w w w。有 m m m个特殊点&#xff0c;将这些点记为集合 A A A。 将 A A A中的元素随机打乱得到序列 a a a&#xff0c;求 ∑ i 2 m d ( a i − 1 , a i ) \sum\limits_{i2}^md(a_{i-1},a_i) i2∑m​d(ai−1​…...

竞赛选题 深度学习的视频多目标跟踪实现

文章目录 1 前言2 先上成果3 多目标跟踪的两种方法3.1 方法13.2 方法2 4 Tracking By Detecting的跟踪过程4.1 存在的问题4.2 基于轨迹预测的跟踪方式 5 训练代码6 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的视频多目标跟踪实现 …...

金蝶云星空表单插件获取控件值

文章目录 金蝶云星空表单插件获取控件值获取主键获取文本获取日期获取数值获取基础资料 金蝶云星空表单插件获取控件值 获取主键 正确&#xff1a; this.View.Model.GetPKValue();错误&#xff1a; 获取文本 this.View.Model.GetValue("FBILLNO")获取日期 thi…...

docker自启与容器自启

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…...

一、认识微服务

目录 一、单体架构 二、分布式架构 三、微服务 1、微服务架构特征&#xff1a; 1.单一职责&#xff1a; 2.面向服务&#xff1a; 3.自治&#xff1a; 4.隔离性强&#xff1a; 2、微服务结构&#xff1a; 3、微服务技术对比&#xff1a; 一、单体架构 二、分布式架构 三…...

Windows server 2012 R2系统服务器远程桌面服务激活服务器RD授权分享

Windows server 2012 R2系统服务器远程桌面服务激活服务器RD授权 二、激活服务器&#xff0c;获取许可证服务器ID和许可证密钥包ID三、激活终端服务器四、配置远程桌面会话主机授权服务器 上期我分享了Windows server 2012 R2系统服务器远程桌面服务的安装教程&#xff0c;若是…...

Vue的计算属性:让你的代码更简洁高效

Vue.js是一种流行的JavaScript框架&#xff0c;它提供了许多功能来帮助开发人员构建交互式Web应用程序。其中一个非常有用的功能是计算属性。在本文中&#xff0c;我们将讨论什么是Vue的计算属性以及如何使用它们来编写更简洁高效的代码。 什么是Vue的计算属性&#xff1f; Vu…...

mysql主从复制-使用心得

文章目录 前言环境配置主库从库 STATEMENTbinloggtidlog-errorDistSQL总结 前言 mysql 主从复制使用感受&#xff0c;遇到一些问题的整理&#xff0c;也总结了一些排查问题技巧。 环境 mysql5.7 配置 附&#xff1a;千万级数据快速插入配置可以参考&#xff1a;mysql千万数…...

今年副业比主业赚得多...

我是从20年开始接触副业的&#xff0c;主要是在程序员外包平台上接单。从一开始的月入0到几百&#xff0c;到现在每个月稳定有小一万的收入。这个月接了一个比较大的项目&#xff0c;结款之后发现今年的副业已经比主业赚得多了&#xff0c;简直美滋滋~ 今年主业收入8w&#xff…...

debian12安装fail2ban

趁着阿里云活动&#xff0c;买了一台一年99的VPS&#xff0c;装了debian12 rootdebian:~# neofetch _,met$$$$$gg. …...

openpnp - 74路西门子飞达控制板(主控板STM32_NUCLEO-144) - 验证

文章目录 openpnp - 74路西门子飞达控制板(主控板STM32_NUCLEO-144) - 验证概述笔记重复数字IO的问题想法手工实现程序实现确定要摘掉的数字重合线自动化测试的问题测试程序的场景测试程序的运行效果测试程序实现备注END openpnp - 74路西门子飞达控制板(主控板STM32_NUCLEO-14…...

从房地产先后跨界通信、文旅演艺领域,万通发展未来路在何方?

近年来&#xff0c;房地产市场可谓负重前行&#xff0c;各大房企纷纷谋求新出路。 作为中国最早的房企之一&#xff0c;万通发展再次处在转型变革的十字路口。自去年以来&#xff0c;万通发展在转型升级之路上动作频频&#xff0c;可谓忙得不亦乐乎。 大幕落下之时&#xff0c;…...

LLM 中的参数单位

M (Mega) 相比于 Million&#xff1a; 1M (Mega) 在计算机科学中等于 ( 2^{20} )&#xff08;即 1,048,576&#xff09;字节。1 Million 等于 ( 10^6 )&#xff08;即 1,000,000&#xff09;。因此&#xff0c;1M (Mega) 在数字上略小于 1 Million。 G (Giga) 相比于 Billion&…...

【探索Linux】—— 强大的命令行工具 P.15(进程间通信 —— system V共享内存)

阅读导航 引言一、system V的概念二、共享内存(1) 概念(2) 共享内存示意图(3) 共享内存数据结构 三、共享内存的使用1. 共享内存的使用步骤&#xff08;1&#xff09;包含头文件&#xff08;2&#xff09;获取键值&#xff08;ftok函数&#xff09;&#xff08;3&#xff09;创…...

OrangepiZERO3驱动USB摄像头的记录

关于orangepiZERO3的官方文档&#xff1a; http://www.orangepi.cn/orangepiwiki/index.php/Orange_Pi_Zero_3 按照里面有关的步骤进行操作&#xff0c;但是可能会有一点小问题&#xff0c;特此记录一下 第一步和第二步一致&#xff0c;不多说。 第三步&#xff1a; 我的命令…...

从一次时序违例修复说起:实战中set_multicycle_path与时钟使能(CE)的配合使用指南

从一次时序违例修复说起&#xff1a;实战中set_multicycle_path与时钟使能(CE)的配合使用指南 在FPGA设计流程中&#xff0c;时序收敛往往是最后阶段最令人头疼的问题之一。特别是当设计中使用时钟使能(Clock Enable, CE)信号进行功耗优化时&#xff0c;默认的单周期时序约束可…...

三步轻松获取国家中小学智慧教育平台电子课本PDF:高效教学资源下载指南

三步轻松获取国家中小学智慧教育平台电子课本PDF&#xff1a;高效教学资源下载指南 【免费下载链接】tchMaterial-parser 国家中小学智慧教育平台 电子课本下载工具&#xff0c;帮助您从智慧教育平台中获取电子课本的 PDF 文件网址并进行下载&#xff0c;让您更方便地获取课本内…...

像素剧本圣殿详细步骤:Qwen2.5-14B-Instruct模型服务健康检查与自动扩缩容配置

像素剧本圣殿详细步骤&#xff1a;Qwen2.5-14B-Instruct模型服务健康检查与自动扩缩容配置 1. 项目概述 像素剧本圣殿&#xff08;Pixel Script Temple&#xff09;是基于Qwen2.5-14B-Instruct大模型深度微调的专业剧本创作工具。该系统采用复古未来像素风格UI设计&#xff0…...

从SRCNN到WDSR:图像超分辨率核心演进路径与关键技术剖析

1. 图像超分辨率技术的基础认知 当你用手机拍下一张照片却发现放大后模糊不清时&#xff0c;图像超分辨率技术就能派上用场。这项技术就像给图像装上"显微镜"&#xff0c;能将低分辨率图片转化为清晰的高分辨率版本。不同于简单的插值放大&#xff0c;它通过深度学习…...

实战指南:利用快马ai为django项目生成开箱即用的vscode python开发环境

作为一个长期使用Python开发Django项目的程序员&#xff0c;我深知配置开发环境是个既基础又容易踩坑的环节。最近尝试用InsCode(快马)平台生成配置方案&#xff0c;发现能省去大量重复劳动。下面分享我的实战经验&#xff1a; 项目结构规范化 平台生成的Django项目骨架严格遵循…...

如何高效突破Cursor试用限制:全功能AI编程助手解锁指南

如何高效突破Cursor试用限制&#xff1a;全功能AI编程助手解锁指南 【免费下载链接】cursor-free-vip [Support 0.45]&#xff08;Multi Language 多语言&#xff09;自动注册 Cursor Ai &#xff0c;自动重置机器ID &#xff0c; 免费升级使用Pro 功能: Youve reached your tr…...

等保.三级要求下Redis 安全测评应该怎么做?

1. 引入 在现代 AI 工程中&#xff0c;Hugging Face 的 tokenizers 库已成为分词器的事实标准。不过 Hugging Face 的 tokenizers 是用 Rust 来实现的&#xff0c;官方只提供了 python 和 node 的绑定实现。要实现与 Hugging Face tokenizers 相同的行为&#xff0c;最好的办法…...

C语言回调函数在TCP客户端中的应用与实践

1. 回调函数基础概念解析回调函数是C语言中一种强大的编程机制&#xff0c;它允许我们将函数作为参数传递给其他函数。这种设计模式在现代编程中极为常见&#xff0c;特别是在事件驱动编程、异步操作和模块化设计中。1.1 回调函数的本质回调函数本质上是一个通过函数指针调用的…...

成为技术专家的捷径?不,只有长期主义的坚持

在软件测试领域&#xff0c;我们常常被一种“速成”的幻象所包围。铺天盖地的培训广告承诺“三个月精通自动化测试”、“六周成为性能测试专家”&#xff0c;各种“一招鲜”的测试工具和“万能”的测试框架被包装成通往成功的捷径。对于身处其中、渴望突破职业瓶颈的测试工程师…...