MTK Android11 APP调用OTA升级

文章目录

简介

在 MediaTek(MTK)平台的 Android 系统 中,RecoverySystem.installPackage() 是一个用于触发 OTA 升级的关键 API。它通常由系统应用(如"系统更新"App)调用,将 OTA 包路径传递给系统,最终引导设备进入 Recovery 模式并安装该包。

MTK升级代码解析

在原生SDK AOSP 中,该函数位于:

复制代码
frameworks/base/core/java/android/os/RecoverySystem.java

几个关键路径变量:

java 复制代码
private static final File RECOVERY_DIR = new File("/cache/recovery");

public static final File BLOCK_MAP_FILE = new File(RECOVERY_DIR, "block.map");

public static final File UNCRYPT_PACKAGE_FILE = new File(RECOVERY_DIR, "uncrypt_file");

public static final File UNCRYPT_STATUS_FILE = new File(RECOVERY_DIR, "uncrypt_status");

执行ota升级的代码:

java 复制代码
    public static void installPackage(Context context, File packageFile, boolean processed)
            throws IOException {
        synchronized (sRequestLock) {
            LOG_FILE.delete();
            // Must delete the file in case it was created by system server.
            UNCRYPT_PACKAGE_FILE.delete();

            String filename = packageFile.getCanonicalPath();
            Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!");

            // If the package name ends with "_s.zip", it's a security update.
            boolean securityUpdate = filename.endsWith("_s.zip");

            // If the package is on the /data partition, the package needs to
            // be processed (i.e. uncrypt'd). The caller specifies if that has
            // been done in 'processed' parameter.
            if (filename.startsWith("/data/")) {
                if (processed) {
                    if (!BLOCK_MAP_FILE.exists()) {
                        Log.e(TAG, "Package claimed to have been processed but failed to find "
                                + "the block map file.");
                        throw new IOException("Failed to find block map file");
                    }
                } else {
                    FileWriter uncryptFile = new FileWriter(UNCRYPT_PACKAGE_FILE);
                    try {
                        uncryptFile.write(filename + "\n");
                    } finally {
                        uncryptFile.close();
                    }
                    // UNCRYPT_PACKAGE_FILE needs to be readable and writable
                    // by system server.
                    if (!UNCRYPT_PACKAGE_FILE.setReadable(true, false)
                            || !UNCRYPT_PACKAGE_FILE.setWritable(true, false)) {
                        Log.e(TAG, "Error setting permission for " + UNCRYPT_PACKAGE_FILE);
                    }

                    BLOCK_MAP_FILE.delete();
                }

                // If the package is on the /data partition, use the block map
                // file as the package name instead.
                filename = "@/cache/recovery/block.map";
            }
            final String filenameArg = "--update_package=" + filename + "\n";
            final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() + "\n";
            final String securityArg = "--security\n";

            String command = filenameArg + localeArg;
            if (securityUpdate) {
                command += securityArg;
            }

            RecoverySystem rs = (RecoverySystem) context.getSystemService(
                    Context.RECOVERY_SERVICE);
            if (!rs.setupBcb(command)) {
                throw new IOException("Setup BCB failed");
            }

            // Having set up the BCB (bootloader control block), go ahead and reboot
            PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
            String reason = PowerManager.REBOOT_RECOVERY_UPDATE;

            // On TV, reboot quiescently if the screen is off
            if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
                DisplayManager dm = context.getSystemService(DisplayManager.class);
                if (dm.getDisplay(DEFAULT_DISPLAY).getState() != Display.STATE_ON) {
                    reason += ",quiescent";
                }
            }
            pm.reboot(reason);

            throw new IOException("Reboot failed (no permissions?)");
        }
    }

APP代码调用升级

java 复制代码
try {
    RecoverySystem.installPackage(MotorActivity.this, new File("/data/ota_package/update.zip"));
    // "/data/media/0/update.zip"
    // "/storage/emulated/0/update.zip"
} catch (IOException e) {
    e.printStackTrace();
}

代码需要

需要添加的权限:

xml 复制代码
  <uses-permission android:name="android.permission.DELETE_CACHE_FILES" />
  <uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
  <uses-permission android:name="android.permission.REBOOT" />

OTA升级报错

  1. 报错
    会提示一堆的selinux错误,而且把升级包放/data/下与放/storage/emulated/0/下的错误是不一样的。以下是放/storage/emulated/0/中的报错:

    升级应用访问/storage/emulated/0/update.zip失败,关键错误打印:

    E [ERROR:file_stream.cc(-1)] Domain=system, Code=EACCES, Message=Permission denied

    update_engine update_engine W type=1400 audit(0.0:532): avc: denied { dac_read_search } for capability=2 scontext=u:r:update_engine:s0 tcontext=u:r:update_engine:s0 tclass=capability permissive=0

    type=1400 audit(0.0:422): avc: denied { search } for name="0" dev="tmpfs" ino=830 scontext=u:r:update_engine:s0 tcontext=u:object_r:mnt_user_file:s0 tclass=dir permissive=0

    type=1400 audit(0.0:430): avc: denied { search } for name="/" dev="fuse" ino=263 scontext=u:r:update_engine:s0 tcontext=u:object_r:fuse:s0 tclass=dir permissive=0

    E [ERROR:file_fetcher.cc(87)] Couldn't open /storage/emulated/0/update.zip

  2. 原因分析
    Android 的 /storage/emulated/0/ 是普通 App 和用户可访问的外部存储路径,通常 不适用于系统级服务直接读取,尤其在启用了 Scoped Storage 或严格 SELinux 策略的设备上。
    update_engine 通常只能安全访问如 /data/ota_package/、/cache/ 或 /mnt/vendor/persist/ 等系统预设路径。

修复报错

  1. 一种方法是将 update.zip 放到系统可访问的目录

    推荐路径:

    /data/ota_package/update.zip

  2. 另外一种还是把升级包放/sdcard路径下,则需要修改selinux

    修改

    device/mediatek/sepolicy/basic/non_plat/update_engine.te

添加

xml 复制代码
allow update_engine media_rw_data_file:dir { search getattr };
allow update_engine media_rw_data_file:file { read open getattr };
allow update_engine self:capability dac_read_search;
allow update_engine mnt_user_file:dir { search getattr };
allow update_engine mnt_user_file:file { read open getattr };
allow update_engine fuse:dir { search getattr open read };
allow update_engine fuse:file { read open getattr };

修改

复制代码
system/sepolicy/private/domain.te
system/sepolicy/prebuilts/api/30.0/private/domain.te

修改下面的内容:

xml 复制代码
neverallow ~{
  dac_override_allowed
  iorap_inode2filename
  iorap_prefetcherd
  traced_perf
  traced_probes
  userdebug_or_eng(`heapprofd')
} self:global_capability_class_set dac_read_search;

修改后的内容:

xml 复制代码
neverallow ~{
  dac_override_allowed
  iorap_inode2filename
  iorap_prefetcherd
  traced_perf
  traced_probes
  userdebug_or_eng(`heapprofd')
  update_engine
} self:global_capability_class_set dac_read_search;

MTK文章

MTK Android设备unlock解锁
linux opensuse使用mtk烧录工具flashtool
MTK Android非常用分辨率修改充电动画
MTK Android修改开机动画与Logo
MTK Android隐藏NavigationBar
mtk安卓生成keystore
MTK Android修改selinux允许system APP可读写sys与proc
MTK Android为某个APP单独添加selinux配置文件
MTK Android串口权限配置
[MTK]安卓8 ADB执行ota升级
MTK Android11 APP调用OTA升级

作者:帅得不敢出门

相关推荐
Swift社区3 小时前
用 Task Local Values 构建 Swift 里的依赖容器:一种更轻量的依赖注入思路
开发语言·ios·swift
黑牛先生3 小时前
【GDB】调试Jsoncpp源码
开发语言·c++·算法
ibuki_fuko3 小时前
QT/C++ 程序启动时检查程序是否已经启动
开发语言·c++·qt
Kiri霧3 小时前
Go Defer语句详解
java·服务器·golang
Q_Q5110082853 小时前
基于Java的加油站销售积分管理系统的设计与实
java·开发语言
亿.63 小时前
2025鹏城杯 Web
java·安全·web·ctf·鹏城杯
2501_915909063 小时前
苹果应用加密方案的一种方法,在没有源码的前提下,如何处理 IPA 的安全问题
android·安全·ios·小程序·uni-app·iphone·webview
塔克Tark3 小时前
【Python】xxx.py文件打包为.exe可执行文件
开发语言·python
⑩-3 小时前
Java设计模式-命令模式
java·设计模式·命令模式