Android12 Ota支持下次重启升级

这篇博客介绍了如何在Android系统中实现下次重启升级功能,避免使用SystemRecovery.installPackage接口导致的立即重启。通过自定义的CtRecoveryTools类,实现了验证包签名、解密包和设置BCB指令的步骤,并提供了进度监听接口。在写BCB指令后,系统会在下次启动时执行升级,同时提供了是否立即重启的选项。
摘要由CSDN通过智能技术生成
博客目标:

Android进行系统升级一般是调用SystemRecovery.installPackage接口进行升级,该接口一旦调用系统会立即重启进入recovery升级,无法满足下次重启升级需求。该博客目的是参考SysteRecovery接口实现下次重启升级功能。

开发环境:

Android version: Android 12

实现代码:
package com.centerm.recoverytools;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.os.Handler;
import android.os.IRecoverySystem;
import android.os.PowerManager;
import android.os.RecoverySystem;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;

import java.io.File;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Locale;


/**
 * Create by ZXC on 2022/10/20
 */
public class CtRecoveryTools {

    private static final String TAG = "CtRecoveryTools";
    private static final Object sRequestLock = new Object();
    static IRecoverySystem mRecoveryServer;
    private static final File RECOVERY_DIR = new File("/cache/recovery");
    private static final File LOG_FILE = new File(RECOVERY_DIR, "log");
    public static final File UNCRYPT_PACKAGE_FILE = new File(RECOVERY_DIR, "uncrypt_file");
    public static final File BLOCK_MAP_FILE = new File(RECOVERY_DIR, "block.map");

    public static final int STAGE_VERIFY = 0;
    public static final int STAGE_PROCESS_PACKAGE = 1;
    public static final int STAGE_SET_BCB = 3;
    /**
     * stage:升级所处阶段.0:包验签阶段,1:包解密阶段,3:写bcb阶段
     * progress: 所处阶段进度值:0-100
     */
    public static interface ProgressListener {
        void onProgress(final int stage, final int progress);
    }

    /**
     * ota包验签. 注意:验过一次的包下次再验会失败!
     *
     * @param ota
     * @param listener
     * @param handler
     * @throws GeneralSecurityException
     * @throws IOException
     */
    public static void verifyPackage(@NonNull File ota, @Nullable final ProgressListener listener,
                                     @Nullable final Handler handler)
            throws GeneralSecurityException, IOException {
        RecoverySystem.ProgressListener listenerInternal = null;
        if (listener != null) {
            listenerInternal = new RecoverySystem.ProgressListener() {
                @Override
                public void onProgress(final int progress) {
                    if (handler != null) {
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                listener.onProgress(STAGE_VERIFY, progress);
                            }
                        });
                    } else {
                        listener.onProgress(STAGE_VERIFY, progress);
                    }
                }
            };
        }
        RecoverySystem.verifyPackage(ota, listenerInternal, null);
    }

    /**
     * ota包解密
     *
     * @param context
     * @param ota
     * @param listener
     * @param handler
     * @throws IOException
     */
    public static void processPackage(@NonNull Context context, @NonNull File ota,
                                      @Nullable final ProgressListener listener, @Nullable Handler handler)
            throws IOException {
        RecoverySystem.ProgressListener listenerInternal = null;
        if (listener != null) {
            listenerInternal = new RecoverySystem.ProgressListener() {
                @Override
                public void onProgress(int progress) {
                    listener.onProgress(STAGE_PROCESS_PACKAGE, progress);
                }
            };
        }
        RecoverySystem.processPackage(context, ota, listenerInternal, handler);
    }

    /**
     * 写bcb指令
     *
     * @param context
     * @param packageFile
     * @param listener :接口回调,为了统一接口加的,可以为空
     * @param rebootImmediately :是否立即重启,true:立即重启,false:不立即重启
     * @throws IOException
     * @throws RemoteException
     */
    public static void sendCommand(@NonNull Context context, @NonNull File packageFile,
                                   @Nullable ProgressListener listener, boolean rebootImmediately)
            throws IOException, RemoteException {
        synchronized (sRequestLock) {
            if (listener != null) {
                listener.onProgress(STAGE_SET_BCB, 0);
            }
            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 (filename.startsWith("/data/")) {
                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");
                }

                // 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;
            }

            Log.d(TAG, "setupBcb: " + command);
            mRecoveryServer = IRecoverySystem.Stub.asInterface(ServiceManager.getService(Context.RECOVERY_SERVICE));
            if (mRecoveryServer == null) {
                throw new IOException("Can not get RecoveryService!!!");
            }
            if (!mRecoveryServer.setupBcb(command)) {
                throw new IOException("Setup BCB failed");
            }

            if (listener != null) {
                listener.onProgress(STAGE_SET_BCB, 100);
            }

            if (rebootImmediately) {
                Log.d(TAG, "reboot now ...");
                // 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;
                pm.reboot(reason);

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

    /**
     * 整合完整的升级流程
     *
     * @param context
     * @param packageFile
     * @param listener
     * @param handler
     * @param rebootImmediately
     * @throws GeneralSecurityException
     * @throws IOException
     * @throws RemoteException
     */
    public static void installPackage(@NonNull Context context, @NonNull File packageFile,
                                      @Nullable ProgressListener listener, @Nullable Handler handler,
                                      boolean rebootImmediately)
            throws GeneralSecurityException, IOException, RemoteException {
        // 验证包的签名信息
        verifyPackage(packageFile, listener, handler);
        // 包解密
        processPackage(context, packageFile, listener, handler);
        // 写bcb指令
        sendCommand(context, packageFile, listener, rebootImmediately);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值