Android之Recovery业务(二)-恢复出厂设置

2022-01-24  本文已影响0人  锄禾豆

简介

简单介绍ota升级业务和恢复出厂设置业务在流程上的差异性

框架

Settings
--> Android(MasterClearReceiver) 
--RecoverySystem--> RecoverySystemService 
--> uncrypt

代码列表

packages/apps/Settings

frameworks/base/core/res/AndroidManifest.xml
frameworks/base/core/java/android/os/RecoverySystem.java
frameworks/base/services/core/java/com/android/server/
RecoverySystemService.java
MasterClearReceiver.java

bootable/recovery
注:
7.1

分析
1.app调用系统接口:RecoverySystem.rebootWipeUserData

权限:
android.permission.REBOOT

Settings发送广播
    private void doMasterClear() {
        Intent intent = new Intent(Intent.ACTION_MASTER_CLEAR);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        intent.putExtra(Intent.EXTRA_REASON, "MasterClearConfirm");
        intent.putExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, mEraseSdCard);
        getActivity().sendBroadcast(intent);
        // Intent handling is asynchronous -- assume it will happen soon.
    }
    
Android AndroidManifest.xml定义接受
   Thread thr = new Thread("Reboot") {
            @Override
            public void run() {
                try {
                    RecoverySystem.rebootWipeUserData(context, shutdown, reason, forceWipe);
                    Log.wtf(TAG, "Still running after master clear?!");
                } catch (IOException e) {
                    Slog.e(TAG, "Can't perform master clear/factory reset", e);
                } catch (SecurityException e) {
                    Slog.e(TAG, "Can't perform master clear/factory reset", e);
                }
            }
        };

    thr.start();

RecoverySystem分析业务
rebootWipeUserData --> bootCommand
    public static void rebootWipeUserData(Context context, boolean shutdown, String reason,
            boolean force) throws IOException {
        ···
        String shutdownArg = null;
        if (shutdown) {
            shutdownArg = "--shutdown_after";
        }

        String reasonArg = null;
        if (!TextUtils.isEmpty(reason)) {
            reasonArg = "--reason=" + sanitizeArg(reason);
        }

        final String localeArg = "--locale=" + Locale.getDefault().toString();
        
        bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);
    }

    private static void bootCommand(Context context, String... args) throws IOException {
        LOG_FILE.delete();

        StringBuilder command = new StringBuilder();
        for (String arg : args) {
            if (!TextUtils.isEmpty(arg)) {
                command.append(arg);
                command.append("\n");
            }
        }

        // Write the command into BCB (bootloader control block) and boot from
        // there. Will not return unless failed.
        RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
        rs.rebootRecoveryWithCommand(command.toString());//写入RecoverySystemService

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

2.RecoverySystemService接收数据进行处理

RecoverySystemService.BinderService
        public void rebootRecoveryWithCommand(String command) {
            synchronized (sRequestLock) {
                if (!setupOrClearBcb(true, command)) {
                    return;
                }

                // Having set up the BCB, go ahead and reboot.
                PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
                pm.reboot(PowerManager.REBOOT_RECOVERY);
            }
        }
注意:
1.ota升级也是调入setupOrClearBcb,差异仅仅在于command
2.ota升级实在RecoverySystem中操作reboot,参数为REBOOT_RECOVERY_UPDATE
  这是是在RecoverySystemService中操作reboot,参数为REBOOT_RECOVERY
这样的目的就是谁控制权限问题

3.uncrypt通过socket与RecoverySystemService建立联系,将数据写入misc分区

省略--此过程可参考:Android之Recovery业务(一)-升级过程

4.PowerManager.reboot差异

Powermanager.reboot(PowerManager.REBOOT_RECOVERY)
-->> PowerManagerService.reboot 
-->> ShutdownThread.reboot --> shutdownInner --> beginShutdownSequence --> run

    public void run() {
        ···
        if (mRebootHasProgressBar) {//此时mRebootHasProgressBar=false
            sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);

            // If it's to reboot to install an update and uncrypt hasn't been
            // done yet, trigger it now.
            uncrypt();
        }
        ···
        rebootOrShutdown(mContext, mReboot, mReason);
    }

5.recovery部分日志及文件分析

1)快速进入recovery的方式
adb reboot recovery

2)日志分析
日记重定向   //可控制日志是否保存文件还是串口输出
bootable/recovery/Android.mk
REDIRECT_LOG_TO := SDCARD

日志所在位置
/cache/recovery

涉及文件:
last_install   //升级成功与否的标记
---------------------------------------------
例如
@/cache/recovery/block.map //update.zip的存储位置
1    //这个代表成功
time_total: 51
retry: 0
bytes_written_system: 790208512
bytes_stashed_system: 0
uncrypt_time: 0
---------------------------------------------------

last_log**   //日志
-----------------------
last_log   //最新的
last_log*  //代表过去的
特别注意:
recovery系统中有对应的build.prop等信息 如果更新过 则last_log可查
升级过程可以看到需要升级的目标版本信息,例如:
[    5.866441] Target: Android/rk3288/rk3288:7.1.2/NHG47K/iotuse08190934:userdebug/test-keys
-------------------------

last_kmsg** //kernel日志
last_locale  //语言区域

uncrypt_file  //system中,update.zip所在的路径
-------------------------------------
/data/Firmware/update.zip  //system中,update.zip所在的路径
------------------------------------

其他

PowerManagerService
    SystemProperties.set("sys.powerctl", "reboot,recovery");

system/core/rootdir/init.rc
    on property:sys.powerctl=*
    powerctl ${sys.powerctl}

system/core/init/builtins.cpp
    {"powerctl",                {1,     1,    do_powerctl}},
    
    android_reboot_with_callback

system/core/libcutils/android_reboot.c
int android_reboot_with_callback(
    int cmd, int flags __unused, const char *arg,
    void (*cb_on_remount)(const struct mntent*))
{
    int ret;
    remount_ro(cb_on_remount);
    switch (cmd) {
        case ANDROID_RB_RESTART:
            ret = reboot(RB_AUTOBOOT);
            break;

        case ANDROID_RB_POWEROFF:
            ret = reboot(RB_POWER_OFF);
            break;

        case ANDROID_RB_RESTART2:
            ret = syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                           LINUX_REBOOT_CMD_RESTART2, arg);
            break;

        default:
            ret = -1;
    }

    return ret;
}

//syscall 这是native和kernel打交道的接口

kernel/kernel/reboot.c
    case LINUX_REBOOT_CMD_RESTART2:
        ret = strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1);
        if (ret < 0) {
            ret = -EFAULT;
            break;
        }
        buffer[sizeof(buffer) - 1] = '\0';

        kernel_restart(buffer);
        break;
        

void kernel_restart(char *cmd)
{
    kernel_restart_prepare(cmd);
    migrate_to_reboot_cpu();
    syscore_shutdown();
    if (!cmd)
        pr_emerg("Restarting system\n");
    else
        pr_emerg("Restarting system with command '%s'\n", cmd);
    kmsg_dump(KMSG_DUMP_RESTART);
    machine_restart(cmd);
}
上一篇下一篇

猜你喜欢

热点阅读