[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 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
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/144860.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

wind版本elasticdump执行报错 unexpected token ‘ in json at

输入的格式不对&#xff1a; 之前&#xff0c;json格式不对&#xff1a; elasticdump --inputhttp://***:9200/d_*_news, --output/home/zyyt/es_data_bak/0714.json --searchBody{"query":{"bool":{"must":[{"term":{"languag…

【算法练习Day48】回文子串最长回文子序列

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 回文子串最长回文子序列总结…

SparkSQL自定义UDF函数

需求&#xff1a;员工id正常为8位&#xff0c;对于不满8位的员工id左侧用0补齐 import org.apache.spark.sql.{DataFrame, SparkSession}object DataSetCreate {def main(args: Array[String]): Unit {val spark SparkSession.builder().appName("test").master(&…

excel用RAND函数、或者RAND.NV函数生成随机数、这两个函数的区别

用RAND函数生成大于0小于1的随机数 插入-》函数&#xff1a; 选择RAND函数&#xff1a; 点击“继续”&#xff1a; 点击“确定”&#xff0c;就生成随机数了&#xff1a; 用RAND.NV函数生成一个大于0小于1的随机数 步骤跟RAND函数相同&#xff0c;只不过选择的是RAN…

Eclipse使用配置tomcat服务:Server配置

目标&#xff1a; 在Eclipse中&#xff0c;默认会把Web项目放到Eclipse的工作空间下 的.metadata\.plugins\org.eclipse.wst.server.core\tmp0(或者是tmp1)\wtpwebapps\下 &#xff0c;如果现在Eclipse中有名为imsmanagere的项目&#xff0c;将它按以前的方式部署到服务器上&am…

2023.11.14使用bootstrap制作一个简洁的前端注册登录页

2023.11.14使用bootstrap制作一个简洁的前端注册登录页 比较简洁的登录页&#xff0c;主要是为自己开发的一些平台页面做测试用&#xff0c;前端具备功能如下&#xff1a; &#xff08;1&#xff09;输入用户名、密码&#xff0c;需补充后端验证代码。 &#xff08;2&#xff…

下载文件时的文件名中文乱码问题,文件名丢失

涉及到的java代码如下&#xff0c;下载的时候文件名为中文 package com.example.springboot.service.impl;import com.alibaba.excel.EasyExcel; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.StringU…

【nlp】2.4 GRU模型

GRU模型 1 GRU介绍2 GRU的内部结构图2.1 GRU结构分析2.2 Bi-GRU介绍2.3 使用Pytorch构建GRU模型2.4 GRU优缺点3 RNN及其变体1 GRU介绍 GRU(Gated Recurrent Unit)也称门控循环单元结构, 它也是传统RNN的变体, 同LSTM一样能够有效捕捉长序列之间的语义关联, 缓解梯度消失或爆…

上海国际集团党委副书记、总裁刘信义一行莅临ZStack调研指导

11月10日&#xff0c;上海国际集团有限公司党委副书记、总裁刘信义率上海国际集团、上海国资经营及国鑫创投领导莅临上海云轴信息科技有限公司&#xff08;简称“云轴科技ZStack”&#xff09;调研指导&#xff0c;云轴科技ZStack创始人、董事长张鑫&#xff0c;携公司管理团队…

最新宝塔反代openai官方API开发接口详细搭建教程,解决502 Bad Gateway问题

一、前言 宝塔反代openai官方API接口详细教程&#xff0c;实现国内使用ChatGPT502 Bad Gateway问题解决&#xff0c; 此方法最简单快捷&#xff0c;没有复杂步骤&#xff0c;不容易出错&#xff0c;即最简单&#xff0c;零代码、零部署的方法。 二、实现前提 一台海外服务器…

鸿蒙系统扫盲(一):鸿蒙OS和开源鸿蒙什么关系?

我们经常提到鸿蒙&#xff0c;但是大家都分不清鸿蒙OS&#xff08;Harmony OS&#xff09;和 开源鸿蒙&#xff08;Open Harmony&#xff09;的区别&#xff1f; 1.开源鸿蒙&#xff08;Open Harmony&#xff09; 鸿蒙系统愿来的设计初衷&#xff0c;就是让所有设备都可以运行…

【C++】join ()和detach ()函数详解和示例

简单的来说&#xff0c;join ()方法建立的线程具有阻碍作用&#xff0c;该线程不结束&#xff0c;另一些函数就无法运行。detach ()方法建立的线程&#xff0c;可以和另一些函数同时进行。下面以示例进行详细说明&#xff0c;以帮助大家理解和使用。 目录 join ()detach () jo…

asp.net core mvc之 局部视图 和视图组件

一、局部视图是什么&#xff1f; 1、局部视图也是 .cshtml文件 2、将重复使用的内容&#xff08;如&#xff1a;右侧栏&#xff09;&#xff0c;做出局部视图&#xff0c;供其他视图调用 3、局部视图不会执行 _ViewStart.cshtml 二、局部视图示例 1、在 /Views/Shared目录右…

python合并多个CSV文件成一个

之前写的是合并Excel&#xff0c;这次是操作CSV文件&#xff0c;可以看到文件非常多 每一个文件里面前两行是表头&#xff0c;无关信息&#xff0c;因此合并的时候需要略过&#xff1a; python代码如下&#xff1a; import os import csv# 输入文件夹路径和输出文件路径 inp…

印刷设备丝杆选择研磨杆还是冷轧杆好?

在印刷设备中&#xff0c;选择研磨杆还是冷轧杆取决于具体的使用需求和设备要求。以下是关于两种丝杆选择的要点&#xff1a; 1、精度要求&#xff1a;研磨杆通常具有更高的制造精度&#xff0c;能够有效保证印刷设备的精度和稳定性。研磨杆经过精细研磨和校准&#xff0c;具有…

Python语言:文件的操作与使用

Python语言可以对电脑中的文件进行一系列操作&#xff0c;包括文件的打开与关闭&#xff0c;文件内容的读取和追加等。 打开文件 语法&#xff1a;使用open函数 使用python语言的内置open函数打开一个文件&#xff0c;里面有三个参数可以指定文件的路径&#xff0c;操作方式&a…

python实现双臂老虎机k-armed-bandit

老虎机&#xff0c;投入钱币会随机返还钱币&#xff08;reward&#xff09; 这里设置两台老虎机&#xff0c;一台均值500&#xff0c;标准差5&#xff0c;一台均值550&#xff0c;标准差10 初始值均为998&#xff0c;更新规则为reward之和/轮数 最后结果会在均值附近收敛 impo…

山东省技能兴鲁网络安全大赛 web方向

文章目录 购买FLAG日志里的FLAG一只小蜜蜂 购买FLAG 随便登录admin进去&#xff0c;发现有充值和购买功能 但是试试充值发现不行 购买页面如下 bp抓包看看&#xff0c;发现value值可控 我们试试将其改为正数&#xff0c;发现成功 购买得到flag 日志里的FLAG <?phphi…

创新的Sui项目在CoinDCX的Unfold 2023黑客松中获奖

近日&#xff0c;在印度班加罗尔&#xff08;Bengaluru&#xff09;&#xff0c;超过2500人参加了CoinDCX组织的综合性Web3活动Unfold 2023。作为讨论、聚会和活动的一部分&#xff0c;进行了一次多链黑客松&#xff0c;Sui联合赞助了该活动。所有团队&#xff0c;无论他们构建…

企业如何利用好用户画像对客户进行精准营销?提高营销转化?

随着市场竞争的加剧&#xff0c;企业对于客户的需求和行为越来越关注&#xff0c;如何利用好用户画像对客户进行精准营销&#xff0c;提高营销转化&#xff0c;成为企业关注的焦点。 一、了解用户需求和行为 首先&#xff0c;企业需要了解客户的需求和行为&#xff0c;包括客户…