Android10.0 OTA升级流程分析

原文地址:https://skytoby.github.io/2019/Android%20OTA%E5%8D%87%E7%BA%A7%E6%B5%81%E7%A8%8B%E5%88%86%E6%9E%90/

一、概述

目前Android系统终端的升级主要是通过无线进行的(FOTA,Firmware Over-The-Air),主要流程是通过无线方式将升级包下载到终端,而后调用系统的升级接口进行升级。本文主要分析升级包下载后,调用系统升级接口之后的流程。

1.1 升级包结构

升级包是用make otapackage命令生成的,对于差分包,需要生成两个ota整包,再用系统的编译工具利用这两个整包生成一个差分包。下面是某终端Android9.0版本的ota升级包结构。

├─firmware-update
└─META-INF
    └─com
        ├─android
            ├─metadata
            ├─otacert
        └─google
            └─android
               ├─update-binary
               ├─updater-script
 ├─boot.img
 ├─system.new.dat.br
 ├─system.patch.dat
 ├─system.transfer.list
 ├─vendor.new.dat.br
 ├─vendor.patch.dat
 ├─vendor.transfer.list 

firmware-update:目录下主要是一些固件的升级,如mbn、dtbo、vbmeta等

boot.img:更新boot分区所需要的文件,包括kernel+ramdisk

system.new.dat.br:Android8.1镜像后新的压缩格式,为数据部分,里面是system.img数据。

system.transfer.list:数据转换的描述列表。

system.patch.dat:升级时用到,ota包中大小为0。

vendor和system类似。

update-binary:二进制文件,相当于一个脚本解释器,能够识别updater-script中描述的操作,文件的命名由bootable/recovery/install.cpp 中的UPDATE_BINARY_NAME值而定。

updater-script:升级包的升级脚本,文件的命名由bootable/recovery/updater/updater.cpp中的SCRIPT_NAME值而定。

metadata:描述设备信息及环境变量的元数据,主要包括一些编译选项、签名公钥,时间戳以及设备型号等。

otacert:ota签名。

1.2 系统启动模式

Android系统启动主要有两种:

1.组合键

用户通过按下组合键,不同的终端可以定义组合键,进入不同的工作模式,在bootable/bootloader/lk/app/aboot/aboot.c文件中判断,具体有两种:

bootloader模式,可进一步进入fastboot(快速刷机模式)。

Recovery模式,进入这种模式系统会出现一个简单的ui界面,用来提示用户的进一步操作。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RT9PaFD4-1577704943830)(/Android OTA升级流程分析/recovery.jpg)]

2.正常启动

没有按下任何组合键,正常开机,bootloader会读取启动控制信息块BCB(bootloader control block),它是一个结构体,存放着启动命令Command。根据不同的命令,系统可以进入三种不同的启动模式。下面是Bootloader Message的定义,通过注释可以了解具体结构体中成员变量的含义。

[->bootloader_message.h]

/* Bootloader Message (2-KiB)
 *
 * This structure describes the content of a block in flash
 * that is used for recovery and the bootloader to talk to
 * each other.
 *
 * The command field is updated by linux when it wants to
 * reboot into recovery or to update radio or bootloader firmware.
 * It is also updated by the bootloader when firmware update
 * is complete (to boot into recovery for any final cleanup)
 *
 * The status field was used by the bootloader after the completion
 * of an "update-radio" or "update-hboot" command, which has been
 * deprecated since Froyo.
 *
 * The recovery field is only written by linux and used
 * for the system to send a message to recovery or the
 * other way around.
 *
 * The stage field is written by packages which restart themselves
 * multiple times, so that the UI can reflect which invocation of the
 * package it is.  If the value is of the format "#/#" (eg, "1/3"),
 * the UI will add a simple indicator of that status.
 *
 * We used to have slot_suffix field for A/B boot control metadata in
 * this struct, which gets unintentionally cleared by recovery or
 * uncrypt. Move it into struct bootloader_message_ab to avoid the
 * issue.
 */
struct bootloader_message {
    char command[32];
    char status[32];
    char recovery[768];

    // The 'recovery' field used to be 1024 bytes.  It has only ever
    // been used to store the recovery command line, so 768 bytes
    // should be plenty.  We carve off the last 256 bytes to store the
    // stage string (for multistage packages) and possible future
    // expansion.
    char stage[32];

    // The 'reserved' field used to be 224 bytes when it was initially
    // carved off from the 1024-byte recovery field. Bump it up to
    // 1184-byte so that the entire bootloader_message struct rounds up
    // to 2048-byte.
    char reserved[1184];
};

二、OTA升级重启前

前面介绍完OTA升级包和系统启动模式,现在看下真正的ota升级的步骤。无论ota包是通过无线下载还是导入到SD卡,最后都会调用到RecoverySystem.installPackage方法,下面分析下这个详细的流程。

2.1 RS.installPackage

[->RecoverySystem.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 file
            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.
            //如果升级包的路径是/data/开始
            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
                        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);
            //向bootloader control block写入命名      
            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)) {
                WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
                if (wm.getDefaultDisplay().getState() != Display.STATE_ON) {
                    reason += ",quiescent";
                }
            }
            //重启
            pm.reboot(reason);
            throw new IOException("Reboot failed (no permissions?)");
        }
    }

private static final File RECOVERY_DIR = new File("/cache/recovery");

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

这里主要是对ota升级包的处理,由于进入recovery模式后,data分区将不能加载,因此需要将其通过block方式,把ota升级包生成稀疏的描述文件,保存在/cache/recovery/block.map中。升级的命令写入到BCB,重启之后bootloader会读取BCB中的command从而进入升级模式。

2.2 RS.setupBcb

[->RecoverySystem.java]

  /**
     * Talks to RecoverySystemService via Binder to set up the BCB.
     */
    private boolean setupBcb(String command) {
        try {
            //通过binder调用setupBcb
            return mService.setupBcb(command);
        } catch (RemoteException unused) {
        }
        return false;
    }

2.3 RSS.setupBcb

[->RecoverySystemService.java]

       @Override // Binder call
        public boolean setupBcb(String command) {
            if (DEBUG) Slog.d(TAG, "setupBcb: [" + command + "]");
            synchronized (sRequestLock) {
                return setupOrClearBcb(true, command);
            }
        }

2.4 RSS.setupOrClearBcb

[->RecoverySystemService.java]

  private boolean setupOrClearBcb(boolean isSetup, String command) {
            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);

            final boolean available = checkAndWaitForUncryptService();
            if (!available) {
                Slog.e(TAG, "uncrypt service is unavailable.");
                return false;
            }
            //设置ctl.start属性,启动setup-bcb服务
            if (isSetup) {
                SystemProperties.set("ctl.start", "setup-bcb");
            } else {
                SystemProperties.set("ctl.start", "clear-bcb");
            }

            // Connect to the uncrypt service socket.
            // 连接uncrypt服务
            LocalSocket socket = connectService();
            if (socket == null) {
                Slog.e(TAG, "Failed to connect to uncrypt socket");
                return false;
            }

            DataInputStream dis = null;
            DataOutputStream dos = null;
            try {
                dis = new DataInputStream(socket.getInputStream());
                dos = new DataOutputStream(socket.getOutputStream());

                // Send the BCB commands if it's to setup BCB.
                // 发送BCB commands
                if (isSetup) {
                    byte[] cmdUtf8 = command.getBytes("UTF-8");
                    dos.writeInt(cmdUtf8.length);
                    dos.write(cmdUtf8, 0, cmdUtf8.length);
                    dos.flush();
                }

                // Read the status from the socket.
                int status = dis.readInt();

                // Ack receipt of the status code. uncrypt waits for the ack so
                // the socket won't be destroyed before we receive the code.
                dos.writeInt(0);

                if (status == 100) {
                    Slog.i(TAG, "uncrypt " + (isSetup ? "setup" : "clear") +
                            " bcb successfully finished.");
                } else {
                    // Error in /system/bin/uncrypt.
                    Slog.e(TAG, "uncrypt failed with status: " + status);
                    return false;
                }
            } catch (IOException e) {
                Slog.e(TAG, "IOException when communicating with uncrypt:", e);
                return false;
            } finally {
                IoUtils.closeQuietly(dis);
                IoUtils.closeQuietly(dos);
                IoUtils.closeQuietly(socket);
            }

            return true;
        }

SystemProperties.set(“ctl.start”, “setup-bcb”),通过这种方式来启动服务(SystemProperties设置属性的原理详细见文章Android SystemProperties系统属性分析),而后连接服务,向其中发送BCB command。

2.5 PM.reboot

[->PowerManager.java]

 public void reboot(String reason) {
        try {
            mService.reboot(false, reason, true);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

2.6 PMS.reboot

[->PowerManagerService.java]

        /**
         * Reboots the device.
         *
         * @param confirm If true, shows a reboot confirmation dialog.
         * @param reason The reason for the reboot, or null if none.
         * @param wait If true, this call waits for the reboot to complete and does not return.
         */
        @Override // Binder call
        public void reboot(boolean confirm, String reason, boolean wait) {
            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
            if (PowerManager.REBOOT_RECOVERY.equals(reason)
                    || PowerManager.REBOOT_RECOVERY_UPDATE.equals(reason)) {
                mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
            }

            final long ident = Binder.clearCallingIdentity();
            try {
                shutdownOrRebootInternal(HALT_MODE_REBOOT, confirm, reason, wait);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

这里传入的参数confirm:false , reason:REBOOT_RECOVERY_UPDATE = “recovery-update”,wait:true

2.7 PMS.shutdownOrRebootInternal

[->PowerManagerService.java]

  private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm,
            final String reason, boolean wait) {
        if (mHandler == null || !mSystemReady) {
            if (RescueParty.isAttemptingFactoryReset()) {
                // If we're stuck in a really low-level reboot loop, and a
                // rescue party is trying to prompt the user for a factory data
                // reset, we must GET TO DA CHOPPA!
                PowerManagerService.lowLevelReboot(reason);
            } else {
                throw new IllegalStateException("Too early to call shutdown() or reboot()");
            }
        }
        //上面传过来的是HALT_MODE_REBOOT
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                synchronized (this) {
                    if (haltMode == HALT_MODE_REBOOT_SAFE_MODE) {
                        ShutdownThread.rebootSafeMode(getUiContext(), confirm);
                    } else if (haltMode == HALT_MODE_REBOOT) {
                        ShutdownThread.reboot(getUiContext(), reason, confirm);
                    } else {
                        ShutdownThread.shutdown(getUiContext(), reason, confirm);
                    }
                }
            }
        };

        // ShutdownThread must run on a looper capable of displaying the UI.
        Message msg = Message.obtain(UiThread.getHandler(), runnable);
        msg.setAsynchronous(true);
        UiThread.getHandler().sendMessage(msg);

        // PowerManager.reboot() is documented not to return so just wait for the inevitable.
        if (wait) {
            synchronized (runnable) {
                while (true) {
                    try {
                        runnable.wait();
                    } catch (InterruptedException e) {
                    }
                }
            }
        }
    }

2.8 ST.reboot

[->ShutdownThread.java]

public static void reboot(final Context context, String reason, boolean confirm) {
        mReboot = true;
        mRebootSafeMode = false;
        mRebootHasProgressBar = false;
        mReason = reason;
        shutdownInner(context, confirm);
    }

2.9 ST.shutdownInner

[->ShutdownThread.java]

 private static void shutdownInner(final Context context, boolean confirm) {
        // ShutdownThread is called from many places, so best to verify here that the context passed
        // in is themed.
        context.assertRuntimeOverlayThemable();

        // ensure that only one thread is trying to power down.
        // any additional calls are just returned
        synchronized (sIsStartedGuard) {
            if (sIsStarted) {
                Log.d(TAG, "Request to shutdown already running, returning.");
                return;
            }
        }

        final int longPressBehavior = context.getResources().getInteger(
                        com.android.internal.R.integer.config_longPressOnPowerBehavior);
        final int resourceId = mRebootSafeMode
                ? com.android.internal.R.string.reboot_safemode_confirm
                : (longPressBehavior == 2
                        ? com.android.internal.R.string.shutdown_confirm_question
                        : com.android.internal.R.string.shutdown_confirm);

        Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);

        if (confirm) {
            final CloseDialogReceiver closer = new CloseDialogReceiver(context);
            if (sConfirmDialog != null) {
                sConfirmDialog.dismiss();
            }
            sConfirmDialog = new AlertDialog.Builder(context)
                    .setTitle(mRebootSafeMode
                            ? com.android.internal.R.string.reboot_safemode_title
                            : com.android.internal.R.string.power_off)
                    .setMessage(resourceId)
                    .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            beginShutdownSequence(context);
                        }
                    })
                    .setNegativeButton(com.android.internal.R.string.no, null)
                    .create();
            closer.dialog = sConfirmDialog;
            sConfirmDialog.setOnDismissListener(closer);
            sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
            sConfirmDialog.show();
        } else {
            //走这里
            beginShutdownSequence(context);
        }
    }

2.10 ST.beginShutdownSequence

[->ShutdownThread.java]

 private static void beginShutdownSequence(Context context) {
        synchronized (sIsStartedGuard) {
            if (sIsStarted) {
                Log.d(TAG, "Shutdown sequence already running, returning.");
                return;
            }
            sIsStarted = true;
        }

        sInstance.mProgressDialog = showShutdownDialog(context);
        sInstance.mContext = context;
        sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);

        // make sure we never fall asleep again
        sInstance.mCpuWakeLock = null;
        try {
            sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
                    PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
            sInstance.mCpuWakeLock.setReferenceCounted(false);
            sInstance.mCpuWakeLock.acquire();
        } catch (SecurityException e) {
            Log.w(TAG, "No permission to acquire wake lock", e);
            sInstance.mCpuWakeLock = null;
        }

        // also make sure the screen stays on for better user experience
        sInstance.mScreenWakeLock = null;
        if (sInstance.mPowerManager.isScreenOn()) {
            try {
                sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
                        PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
                sInstance.mScreenWakeLock.setReferenceCounted(false);
                sInstance.mScreenWakeLock.acquire();
            } catch (SecurityException e) {
                Log.w(TAG, "No permission to acquire wake lock", e);
                sInstance.mScreenWakeLock = null;
            }
        }

        if (SecurityLog.isLoggingEnabled()) {
            SecurityLog.writeEvent(SecurityLog.TAG_OS_SHUTDOWN);
        }

        // start the thread that initiates shutdown
        sInstance.mHandler = new Handler() {
        };
        //启动线程
        sInstance.start();
    }
2.10.1 ST.showShutdownDialog

[->ShutdownThread.java]

private static ProgressDialog showShutdownDialog(Context context) {
        // Throw up a system dialog to indicate the device is rebooting / shutting down.
        ProgressDialog pd = new ProgressDialog(context);

        // Path 1: Reboot to recovery for update
        //   Condition: mReason startswith REBOOT_RECOVERY_UPDATE
        //
        //  Path 1a: uncrypt needed
        //   Condition: if /cache/recovery/uncrypt_file exists but
        //              /cache/recovery/block.map doesn't.
        //   UI: determinate progress bar (mRebootHasProgressBar == True)
        //
        // * Path 1a is expected to be removed once the GmsCore shipped on
        //   device always calls uncrypt prior to reboot.
        //
        //  Path 1b: uncrypt already done
        //   UI: spinning circle only (no progress bar)
        //
        // Path 2: Reboot to recovery for factory reset
        //   Condition: mReason == REBOOT_RECOVERY
        //   UI: spinning circle only (no progress bar)
        //
        // Path 3: Regular reboot / shutdown
        //   Condition: Otherwise
        //   UI: spinning circle only (no progress bar)

        // mReason could be "recovery-update" or "recovery-update,quiescent".
        //传入的是REBOOT_RECOVERY_UPDATE,走这里
        if (mReason != null && mReason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) {
            // We need the progress bar if uncrypt will be invoked during the
            // reboot, which might be time-consuming.
            mRebootHasProgressBar = RecoverySystem.UNCRYPT_PACKAGE_FILE.exists()
                    && !(RecoverySystem.BLOCK_MAP_FILE.exists());
            pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title));
            //正常升级模式下,mRebootHasProgressBar = true
            if (mRebootHasProgressBar) {
                pd.setMax(100);
                pd.setProgress(0);
                pd.setIndeterminate(false);
                pd.setProgressNumberFormat(null);
                pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
                pd.setMessage(context.getText(
                            com.android.internal.R.string.reboot_to_update_prepare));
            } else {
                if (showSysuiReboot()) {
                    return null;
                }
                pd.setIndeterminate(true);
                pd.setMessage(context.getText(
                            com.android.internal.R.string.reboot_to_update_reboot));
            }
        } else if (mReason != null && mReason.equals(PowerManager.REBOOT_RECOVERY)) {
            if (RescueParty.isAttemptingFactoryReset()) {
                // We're not actually doing a factory reset yet; we're rebooting
                // to ask the user if they'd like to reset, so give them a less
                // scary dialog message.
                pd.setTitle(context.getText(com.android.internal.R.string.power_off));
                pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
                pd.setIndeterminate(true);
            } else {
                // Factory reset path. Set the dialog message accordingly.
                pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title));
                pd.setMessage(context.getText(
                            com.android.internal.R.string.reboot_to_reset_message));
                pd.setIndeterminate(true);
            }
        } else {
            if (showSysuiReboot()) {
                return null;
            }
            pd.setTitle(context.getText(com.android.internal.R.string.power_off));
            pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
            pd.setIndeterminate(true);
        }
        pd.setCancelable(false);
        pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);

        pd.show();
        return pd;
    }

2.11 ST.run

[->ShutdownThread.java]

  /**
     * Makes sure we handle the shutdown gracefully.
     * Shuts off power regardless of radio state if the allotted time has passed.
     */
    public void run() {
        TimingsTraceLog shutdownTimingLog = newTimingsLog();
        shutdownTimingLog.traceBegin("SystemServerShutdown");
        metricShutdownStart();
        metricStarted(METRIC_SYSTEM_SERVER);

        BroadcastReceiver br = new BroadcastReceiver() {
            @Override public void onReceive(Context context, Intent intent) {
                // We don't allow apps to cancel this, so ignore the result.
                actionDone();
            }
        };

        /*
         * Write a system property in case the system_server reboots before we
         * get to the actual hardware restart. If that happens, we'll retry at
         * the beginning of the SystemServer startup.
         */
        {
            String reason = (mReboot ? "1" : "0") + (mReason != null ? mReason : "");
            SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
        }

        /*
         * If we are rebooting into safe mode, write a system property
         * indicating so.
         */
        if (mRebootSafeMode) {
            SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
        }

        metricStarted(METRIC_SEND_BROADCAST);
        shutdownTimingLog.traceBegin("SendShutdownBroadcast");
        Log.i(TAG, "Sending shutdown broadcast...");

        // First send the high-level shut down broadcast.
        mActionDone = false;
        Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY);
        mContext.sendOrderedBroadcastAsUser(intent,
                UserHandle.ALL, null, br, mHandler, 0, null, null);

        final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
        synchronized (mActionDoneSync) {
            while (!mActionDone) {
                long delay = endTime - SystemClock.elapsedRealtime();
                if (delay <= 0) {
                    Log.w(TAG, "Shutdown broadcast timed out");
                    break;
                } else if (mRebootHasProgressBar) {
                    int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 *
                            BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME);
                    sInstance.setRebootProgress(status, null);
                }
                try {
                    mActionDoneSync.wait(Math.min(delay, ACTION_DONE_POLL_WAIT_MS));
                } catch (InterruptedException e) {
                }
            }
        }
        if (mRebootHasProgressBar) {
            sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null);
        }
        shutdownTimingLog.traceEnd(); // SendShutdownBroadcast
        metricEnded(METRIC_SEND_BROADCAST);

        Log.i(TAG, "Shutting down activity manager...");
        shutdownTimingLog.traceBegin("ShutdownActivityManager");
        metricStarted(METRIC_AM);

        final IActivityManager am =
                IActivityManager.Stub.asInterface(ServiceManager.checkService("activity"));
        if (am != null) {
            try {
                am.shutdown(MAX_BROADCAST_TIME);
            } catch (RemoteException e) {
            }
        }
        if (mRebootHasProgressBar) {
            sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
        }
        shutdownTimingLog.traceEnd();// ShutdownActivityManager
        metricEnded(METRIC_AM);

        Log.i(TAG, "Shutting down package manager...");
        shutdownTimingLog.traceBegin("ShutdownPackageManager");
        metricStarted(METRIC_PM);

        final PackageManagerService pm = (PackageManagerService)
            ServiceManager.getService("package");
        if (pm != null) {
            pm.shutdown();
        }
        if (mRebootHasProgressBar) {
            sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
        }
        shutdownTimingLog.traceEnd(); // ShutdownPackageManager
        metricEnded(METRIC_PM);

        // Shutdown radios.
        shutdownTimingLog.traceBegin("ShutdownRadios");
        metricStarted(METRIC_RADIOS);
        shutdownRadios(MAX_RADIO_WAIT_TIME);
        if (mRebootHasProgressBar) {
            sInstance.setRebootProgress(RADIO_STOP_PERCENT, null);
        }
        shutdownTimingLog.traceEnd(); // ShutdownRadios
        metricEnded(METRIC_RADIOS);
        
        //为true,将执行uncrypt操作
        if (mRebootHasProgressBar) {
            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();
        }

        shutdownTimingLog.traceEnd(); // SystemServerShutdown
        metricEnded(METRIC_SYSTEM_SERVER);
        saveMetrics(mReboot, mReason);
        // Remaining work will be done by init, including vold shutdown
        rebootOrShutdown(mContext, mReboot, mReason);
    }

2.12 ST.uncrypt

[->ShutdownThread.java]

private void uncrypt() {
        Log.i(TAG, "Calling uncrypt and monitoring the progress...");

        final RecoverySystem.ProgressListener progressListener =
                new RecoverySystem.ProgressListener() {
            @Override
            public void onProgress(int status) {
                if (status >= 0 && status < 100) {
                    // Scale down to [MOUNT_SERVICE_STOP_PERCENT, 100).
                    status = (int)(status * (100.0 - MOUNT_SERVICE_STOP_PERCENT) / 100);
                    status += MOUNT_SERVICE_STOP_PERCENT;
                    CharSequence msg = mContext.getText(
                            com.android.internal.R.string.reboot_to_update_package);
                    sInstance.setRebootProgress(status, msg);
                } else if (status == 100) {
                    CharSequence msg = mContext.getText(
                            com.android.internal.R.string.reboot_to_update_reboot);
                    sInstance.setRebootProgress(status, msg);
                } else {
                    // Ignored
                }
            }
        };

        final boolean[] done = new boolean[1];
        done[0] = false;
        Thread t = new Thread() {
            @Override
            public void run() {
                RecoverySystem rs = (RecoverySystem) mContext.getSystemService(
                        Context.RECOVERY_SERVICE);
                String filename = null;
                try {
                    filename = FileUtils.readTextFile(RecoverySystem.UNCRYPT_PACKAGE_FILE, 0, null);
                    rs.processPackage(mContext, new File(filename), progressListener);
                } catch (IOException e) {
                    Log.e(TAG, "Error uncrypting file", e);
                }
                done[0] = true;
            }
        };
        t.start();

        try {
            t.join(MAX_UNCRYPT_WAIT_TIME);
        } catch (InterruptedException unused) {
        }
        if (!done[0]) {
            Log.w(TAG, "Timed out waiting for uncrypt.");
            final int uncryptTimeoutError = 100;
            String timeoutMessage = String.format("uncrypt_time: %d/n" + "uncrypt_error: %d/n",
                    MAX_UNCRYPT_WAIT_TIME / 1000, uncryptTimeoutError);
            try {
                FileUtils.stringToFile(RecoverySystem.UNCRYPT_STATUS_FILE, timeoutMessage);
            } catch (IOException e) {
                Log.e(TAG, "Failed to write timeout message to uncrypt status", e);
            }
        }
    }

2.13 RS.processPackage

[->RecoverySystem.java]

 public static void processPackage(Context context,
                                      File packageFile,
                                      final ProgressListener listener,
                                      final Handler handler)
            throws IOException {
        String filename = packageFile.getCanonicalPath();
        if (!filename.startsWith("/data/")) {
            return;
        }

        RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
        IRecoverySystemProgressListener progressListener = null;
        if (listener != null) {
            final Handler progressHandler;
            if (handler != null) {
                progressHandler = handler;
            } else {
                progressHandler = new Handler(context.getMainLooper());
            }
            progressListener = new IRecoverySystemProgressListener.Stub() {
                int lastProgress = 0;
                long lastPublishTime = System.currentTimeMillis();

                @Override
                public void onProgress(final int progress) {
                    final long now = System.currentTimeMillis();
                    progressHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            if (progress > lastProgress &&
                                    now - lastPublishTime > PUBLISH_PROGRESS_INTERVAL_MS) {
                                lastProgress = progress;
                                lastPublishTime = now;
                                listener.onProgress(progress);
                            }
                        }
                    });
                }
            };
        }
        //通过progressListener传递进度到界面
        if (!rs.uncrypt(filename, progressListener)) {
            throw new IOException("process package failed");
        }
    }

2.14 RS.uncrypt

[->RecoverySystem.java]

 /**
     * Talks to RecoverySystemService via Binder to trigger uncrypt.
     */
    private boolean uncrypt(String packageFile, IRecoverySystemProgressListener listener) {
        try {
            return mService.uncrypt(packageFile, listener);
        } catch (RemoteException unused) {
        }
        return false;
    }

2.15 RSS.uncrypt

[->RecoverySystemService.java]

public boolean uncrypt(String filename, IRecoverySystemProgressListener listener) {
            if (DEBUG) Slog.d(TAG, "uncrypt: " + filename);

            synchronized (sRequestLock) {
                mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);

                final boolean available = checkAndWaitForUncryptService();
                if (!available) {
                    Slog.e(TAG, "uncrypt service is unavailable.");
                    return false;
                }

                // Write the filename into UNCRYPT_PACKAGE_FILE to be read by
                // uncrypt.
                RecoverySystem.UNCRYPT_PACKAGE_FILE.delete();

                try (FileWriter uncryptFile = new FileWriter(RecoverySystem.UNCRYPT_PACKAGE_FILE)) {
                    uncryptFile.write(filename + "/n");
                } catch (IOException e) {
                    Slog.e(TAG, "IOException when writing /"" +
                            RecoverySystem.UNCRYPT_PACKAGE_FILE + "/":", e);
                    return false;
                }

                // Trigger uncrypt via init.
                //通过设置ctl.start属性,开启uncrypt服务
                SystemProperties.set("ctl.start", "uncrypt");

                // Connect to the uncrypt service socket.
                //连接服务
                LocalSocket socket = connectService();
                if (socket == null) {
                    Slog.e(TAG, "Failed to connect to uncrypt socket");
                    return false;
                }

                // Read the status from the socket.
                DataInputStream dis = null;
                DataOutputStream dos = null;
                try {
                    dis = new DataInputStream(socket.getInputStream());
                    dos = new DataOutputStream(socket.getOutputStream());
                    int lastStatus = Integer.MIN_VALUE;
                    while (true) {
                        // 读取进度
                        int status = dis.readInt();
                        // Avoid flooding the log with the same message.
                        if (status == lastStatus && lastStatus != Integer.MIN_VALUE) {
                            continue;
                        }
                        lastStatus = status;

                        if (status >= 0 && status <= 100) {
                            // Update status
                            Slog.i(TAG, "uncrypt read status: " + status);
                            if (listener != null) {
                                try {
                                    listener.onProgress(status);
                                } catch (RemoteException ignored) {
                                    Slog.w(TAG, "RemoteException when posting progress");
                                }
                            }
                            if (status == 100) {
                                Slog.i(TAG, "uncrypt successfully finished.");
                                // Ack receipt of the final status code. uncrypt
                                // waits for the ack so the socket won't be
                                // destroyed before we receive the code.
                                dos.writeInt(0);
                                break;
                            }
                        } else {
                            // Error in /system/bin/uncrypt.
                            Slog.e(TAG, "uncrypt failed with status: " + status);
                            // Ack receipt of the final status code. uncrypt waits
                            // for the ack so the socket won't be destroyed before
                            // we receive the code.
                            dos.writeInt(0);
                            return false;
                        }
                    }
                } catch (IOException e) {
                    Slog.e(TAG, "IOException when reading status: ", e);
                    return false;
                } finally {
                    IoUtils.closeQuietly(dis);
                    IoUtils.closeQuietly(dos);
                    IoUtils.closeQuietly(socket);
                }

                return true;
            }
        }

SystemProperties.set(“ctl.start”, “uncrypt”);这个操作主要是通过init(启动的Properties服务)进行,而后启动uncrypt服务,通过socket方式systemserver和uncrypt进行通信。

[->uncrypt.rc]

service uncrypt /system/bin/uncrypt
    class main
    socket uncrypt stream 600 system system
    disabled
    oneshot

service setup-bcb /system/bin/uncrypt --setup-bcb
    class main
    socket uncrypt stream 600 system system
    disabled
    oneshot

service clear-bcb /system/bin/uncrypt --clear-bcb
    class main
    socket uncrypt stream 600 system system
    disabled
    oneshot

2.16 uncrypt.main

[->uncrypt.cpp]

//
// If the filesystem is using an encrypted block device, it will also
// read the file and rewrite it to the same blocks of the underlying
// (unencrypted) block device, so the file contents can be read
// without the need for the decryption key.
//
// The output of this program is a "block map" which looks like this:
//
//     /dev/block/platform/msm_sdcc.1/by-name/userdata     # block device
//     49652 4096                        # file size in bytes, block size
//     3                                 # count of block ranges
//     1000 1008                         # block range 0
//     2100 2102                         # ... block range 1
//     30 33                             # ... block range 2
//
// Each block range represents a half-open interval; the line "30 33"
// reprents the blocks [30, 31, 32].
//
// Recovery can take this block map file and retrieve the underlying
// file data to use as an update package.

/**
 * In addition to the uncrypt work, uncrypt also takes care of setting and
 * clearing the bootloader control block (BCB) at /misc partition.
 *
 * uncrypt is triggered as init services on demand. It uses socket to
 * communicate with its caller (i.e. system_server). The socket is managed by
 * init (i.e. created prior to the service starts, and destroyed when uncrypt
 * exits).
 *
 * Below is the uncrypt protocol.
 *
 *    a. caller                 b. init                    c. uncrypt
 * ---------------            ------------               --------------
 *  a1. ctl.start:
 *    setup-bcb /
 *    clear-bcb /
 *    uncrypt
 *
 *                         b2. create socket at
 *                           /dev/socket/uncrypt
 *
 *                                                   c3. listen and accept
 *
 *  a4. send a 4-byte int
 *    (message length)
 *                                                   c5. receive message length
 *  a6. send message
 *                                                   c7. receive message
 *                                                   c8. <do the work; may send
 *                                                      the progress>
 *  a9. <may handle progress>
 *                                                   c10. <upon finishing>
 *                                                     send "100" or "-1"
 *
 *  a11. receive status code
 *  a12. send a 4-byte int to
 *    ack the receive of the
 *    final status code
 *                                                   c13. receive and exit
 *
 *                          b14. destroy the socket
 *
 * Note that a12 and c13 are necessary to ensure a11 happens before the socket
 * gets destroyed in b14.
 */

int main(int argc, char** argv) {
    enum { UNCRYPT, SETUP_BCB, CLEAR_BCB, UNCRYPT_DEBUG } action;
    const char* input_path = nullptr;
    const char* map_file = CACHE_BLOCK_MAP.c_str();
    //解析参数
    if (argc == 2 && strcmp(argv[1], "--clear-bcb") == 0) {
        action = CLEAR_BCB;
    } else if (argc == 2 && strcmp(argv[1], "--setup-bcb") == 0) {
        action = SETUP_BCB;
    } else if (argc == 1) {
        action = UNCRYPT;
    } else if (argc == 3) {
        input_path = argv[1];
        map_file = argv[2];
        action = UNCRYPT_DEBUG;
    } else {
        usage(argv[0]);
        return 2;
    }

    if ((fstab = read_fstab()) == nullptr) {
        log_uncrypt_error_code(kUncryptFstabReadError);
        return 1;
    }

    if (action == UNCRYPT_DEBUG) {
        LOG(INFO) << "uncrypt called in debug mode, skip socket communication";
        bool success = uncrypt_wrapper(input_path, map_file, -1);
        if (success) {
            LOG(INFO) << "uncrypt succeeded";
        } else{
            LOG(INFO) << "uncrypt failed";
        }
        return success ? 0 : 1;
    }

    // c3. The socket is created by init when starting the service. uncrypt
    // will use the socket to communicate with its caller.
    android::base::unique_fd service_socket(android_get_control_socket(UNCRYPT_SOCKET.c_str()));
    if (service_socket == -1) {
        PLOG(ERROR) << "failed to open socket /"" << UNCRYPT_SOCKET << "/"";
        log_uncrypt_error_code(kUncryptSocketOpenError);
        return 1;
    }
    fcntl(service_socket, F_SETFD, FD_CLOEXEC);

    if (listen(service_socket, 1) == -1) {
        PLOG(ERROR) << "failed to listen on socket " << service_socket.get();
        log_uncrypt_error_code(kUncryptSocketListenError);
        return 1;
    }

    android::base::unique_fd socket_fd(accept4(service_socket, nullptr, nullptr, SOCK_CLOEXEC));
    if (socket_fd == -1) {
        PLOG(ERROR) << "failed to accept on socket " << service_socket.get();
        log_uncrypt_error_code(kUncryptSocketAcceptError);
        return 1;
    }

    bool success = false;
    switch (action) {
        //UNCRYPT操作
        case UNCRYPT:
            success = uncrypt_wrapper(input_path, map_file, socket_fd);
            break;
        case SETUP_BCB:
            success = setup_bcb(socket_fd);
            break;
        case CLEAR_BCB:
            success = clear_bcb(socket_fd);
            break;
        default:  // Should never happen.
            LOG(ERROR) << "Invalid uncrypt action code: " << action;
            return 1;
    }

    // c13. Read a 4-byte code from the client before uncrypt exits. This is to
    // ensure the client to receive the last status code before the socket gets
    // destroyed.
    int code;
    if (android::base::ReadFully(socket_fd, &code, 4)) {
        LOG(INFO) << "  received " << code << ", exiting now";
    } else {
        PLOG(ERROR) << "failed to read the code";
    }
    return success ? 0 : 1;
}

通过文件头的注释,可以知道具体的通信方式。

2.17 uncrypt.uncrypt_wrapper

[->uncrypt.cpp]

static bool uncrypt_wrapper(const char* input_path, const char* map_file, const int socket) {
    // Initialize the uncrypt error to kUncryptErrorPlaceholder.
    log_uncrypt_error_code(kUncryptErrorPlaceholder);

    std::string package;
    if (input_path == nullptr) {
        if (!find_uncrypt_package(UNCRYPT_PATH_FILE, &package)) {
            write_status_to_socket(-1, socket);
            // Overwrite the error message.
            log_uncrypt_error_code(kUncryptPackageMissingError);
            return false;
        }
        input_path = package.c_str();
    }
    CHECK(map_file != nullptr);

    auto start = std::chrono::system_clock::now();
    //执行uncrypt操作
    int status = uncrypt(input_path, map_file, socket);
    std::chrono::duration<double> duration = std::chrono::system_clock::now() - start;
    int count = static_cast<int>(duration.count());

    std::string uncrypt_message = android::base::StringPrintf("uncrypt_time: %d/n", count);
    if (status != 0) {
        // Log the time cost and error code if uncrypt fails.
        uncrypt_message += android::base::StringPrintf("uncrypt_error: %d/n", status);
        if (!android::base::WriteStringToFile(uncrypt_message, UNCRYPT_STATUS)) {
            PLOG(WARNING) << "failed to write to " << UNCRYPT_STATUS;
        }

        write_status_to_socket(-1, socket);
        return false;
    }

    if (!android::base::WriteStringToFile(uncrypt_message, UNCRYPT_STATUS)) {
        PLOG(WARNING) << "failed to write to " << UNCRYPT_STATUS;
    }
    //通过socket方式通知进度
    write_status_to_socket(100, socket);

    return true;
}

2.18 uncrypt.uncrypt

[->uncrypt.cpp]

static int uncrypt(const char* input_path, const char* map_file, const int socket) {
    LOG(INFO) << "update package is /"" << input_path << "/"";

    // Turn the name of the file we're supposed to convert into an absolute path, so we can find
    // what filesystem it's on.
    char path[PATH_MAX+1];
    if (realpath(input_path, path) == nullptr) {
        PLOG(ERROR) << "failed to convert /"" << input_path << "/" to absolute path";
        return kUncryptRealpathFindError;
    }

    bool encryptable;
    bool encrypted;
    bool f2fs_fs;
    const char* blk_dev = find_block_device(path, &encryptable, &encrypted, &f2fs_fs);
    if (blk_dev == nullptr) {
        LOG(ERROR) << "failed to find block device for " << path;
        return kUncryptBlockDeviceFindError;
    }

    // If the filesystem it's on isn't encrypted, we only produce the
    // block map, we don't rewrite the file contents (it would be
    // pointless to do so).
    LOG(INFO) << "encryptable: " << (encryptable ? "yes" : "no");
    LOG(INFO) << "  encrypted: " << (encrypted ? "yes" : "no");

    // Recovery supports installing packages from 3 paths: /cache,
    // /data, and /sdcard.  (On a particular device, other locations
    // may work, but those are three we actually expect.)
    //
    // On /data we want to convert the file to a block map so that we
    // can read the package without mounting the partition.  On /cache
    // and /sdcard we leave the file alone.
    if (strncmp(path, "/data/", 6) == 0) {
        LOG(INFO) << "writing block map " << map_file;
        //生成blockmap
        return produce_block_map(path, map_file, blk_dev, encrypted, f2fs_fs, socket);
    }

    return 0;
}

2.19 uncrypt.produce_block_map

[->uncrypt.cpp]

  static int produce_block_map(const char* path, const char* map_file, const char* blk_dev,
                             bool encrypted, bool f2fs_fs, int socket) {
    std::string err;
    if (!android::base::RemoveFileIfExists(map_file, &err)) {
        LOG(ERROR) << "failed to remove the existing map file " << map_file << ": " << err;
        return kUncryptFileRemoveError;
    }
    std::string tmp_map_file = std::string(map_file) + ".tmp";
    android::base::unique_fd mapfd(open(tmp_map_file.c_str(),
                                        O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR));
    if (mapfd == -1) {
        PLOG(ERROR) << "failed to open " << tmp_map_file;
        return kUncryptFileOpenError;
    }

    // Make sure we can write to the socket.
    if (!write_status_to_socket(0, socket)) {
        LOG(ERROR) << "failed to write to socket " << socket;
        return kUncryptSocketWriteError;
    }

    struct stat sb;
    if (stat(path, &sb) != 0) {
        LOG(ERROR) << "failed to stat " << path;
        return kUncryptFileStatError;
    }

    LOG(INFO) << " block size: " << sb.st_blksize << " bytes";

    int blocks = ((sb.st_size-1) / sb.st_blksize) + 1;
    LOG(INFO) << "  file size: " << sb.st_size << " bytes, " << blocks << " blocks";

    std::vector<int> ranges;

    std::string s = android::base::StringPrintf("%s/n%" PRId64 " %" PRId64 "/n",
                       blk_dev, static_cast<int64_t>(sb.st_size),
                       static_cast<int64_t>(sb.st_blksize));
    if (!android::base::WriteStringToFd(s, mapfd)) {
        PLOG(ERROR) << "failed to write " << tmp_map_file;
        return kUncryptWriteError;
    }

    std::vector<std::vector<unsigned char>> buffers;
    /*
     * buffers大小为5,static constexpr int WINDOW_SIZE = 5;
     */
    if (encrypted) {
        buffers.resize(WINDOW_SIZE, std::vector<unsigned char>(sb.st_blksize));
    }
    int head_block = 0;
    int head = 0, tail = 0;

    android::base::unique_fd fd(open(path, O_RDONLY));
    if (fd == -1) {
        PLOG(ERROR) << "failed to open " << path << " for reading";
        return kUncryptFileOpenError;
    }

    android::base::unique_fd wfd;
    if (encrypted) {
        wfd.reset(open(blk_dev, O_WRONLY));
        if (wfd == -1) {
            PLOG(ERROR) << "failed to open " << blk_dev << " for writing";
            return kUncryptBlockOpenError;
        }
    }

#ifndef F2FS_IOC_SET_DONTMOVE
#ifndef F2FS_IOCTL_MAGIC
#define F2FS_IOCTL_MAGIC		0xf5
#endif
#define F2FS_IOC_SET_DONTMOVE		_IO(F2FS_IOCTL_MAGIC, 13)
#endif
    if (f2fs_fs && ioctl(fd, F2FS_IOC_SET_DONTMOVE) < 0) {
        PLOG(ERROR) << "Failed to set non-movable file for f2fs: " << path << " on " << blk_dev;
        return kUncryptIoctlError;
    }

    off64_t pos = 0;
    int last_progress = 0;
    while (pos < sb.st_size) {
        // Update the status file, progress must be between [0, 99].
        int progress = static_cast<int>(100 * (double(pos) / double(sb.st_size)));
        if (progress > last_progress) {
            last_progress = progress;
            write_status_to_socket(progress, socket);
        }

        if ((tail+1) % WINDOW_SIZE == head) {
            // write out head buffer
            int block = head_block;
            if (ioctl(fd, FIBMAP, &block) != 0) {
                PLOG(ERROR) << "failed to find block " << head_block;
                return kUncryptIoctlError;
            }

            if (block == 0) {
                LOG(ERROR) << "failed to find block " << head_block << ", retrying";
                int error = retry_fibmap(fd, path, &block, head_block);
                if (error != kUncryptNoError) {
                    return error;
                }
            }

            add_block_to_ranges(ranges, block);
             //data分区是否加密
            if (encrypted) {
                if (write_at_offset(buffers[head].data(), sb.st_blksize, wfd,
                                    static_cast<off64_t>(sb.st_blksize) * block) != 0) {
                    return kUncryptWriteError;
                }
            }
            head = (head + 1) % WINDOW_SIZE;
            ++head_block;
        }

        // read next block to tail
        // data分区加密
        if (encrypted) {
            size_t to_read = static_cast<size_t>(
                    std::min(static_cast<off64_t>(sb.st_blksize), sb.st_size - pos));
            if (!android::base::ReadFully(fd, buffers[tail].data(), to_read)) {
                PLOG(ERROR) << "failed to read " << path;
                return kUncryptReadError;
            }
            pos += to_read;
        } else {
            // If we're not encrypting; we don't need to actually read
            // anything, just skip pos forward as if we'd read a
            // block.
            pos += sb.st_blksize;
        }
        tail = (tail+1) % WINDOW_SIZE;
    }

    while (head != tail) {
        // write out head buffer
        int block = head_block;
        if (ioctl(fd, FIBMAP, &block) != 0) {
            PLOG(ERROR) << "failed to find block " << head_block;
            return kUncryptIoctlError;
        }

        if (block == 0) {
            LOG(ERROR) << "failed to find block " << head_block << ", retrying";
            int error = retry_fibmap(fd, path, &block, head_block);
            if (error != kUncryptNoError) {
                return error;
            }
        }

        add_block_to_ranges(ranges, block);
        //data分区是否加密
        if (encrypted) {
            if (write_at_offset(buffers[head].data(), sb.st_blksize, wfd,
                                static_cast<off64_t>(sb.st_blksize) * block) != 0) {
                return kUncryptWriteError;
            }
        }
        head = (head + 1) % WINDOW_SIZE;
        ++head_block;
    }

    if (!android::base::WriteStringToFd(
            android::base::StringPrintf("%zu/n", ranges.size() / 2), mapfd)) {
        PLOG(ERROR) << "failed to write " << tmp_map_file;
        return kUncryptWriteError;
    }
    for (size_t i = 0; i < ranges.size(); i += 2) {
        if (!android::base::WriteStringToFd(
                android::base::StringPrintf("%d %d/n", ranges[i], ranges[i+1]), mapfd)) {
            PLOG(ERROR) << "failed to write " << tmp_map_file;
            return kUncryptWriteError;
        }
    }

    if (fsync(mapfd) == -1) {
        PLOG(ERROR) << "failed to fsync /"" << tmp_map_file << "/"";
        return kUncryptFileSyncError;
    }
    if (close(mapfd.release()) == -1) {
        PLOG(ERROR) << "failed to close " << tmp_map_file;
        return kUncryptFileCloseError;
    }

    if (encrypted) {
        if (fsync(wfd) == -1) {
            PLOG(ERROR) << "failed to fsync /"" << blk_dev << "/"";
            return kUncryptFileSyncError;
        }
        if (close(wfd.release()) == -1) {
            PLOG(ERROR) << "failed to close " << blk_dev;
            return kUncryptFileCloseError;
        }
    }

    if (rename(tmp_map_file.c_str(), map_file) == -1) {
        PLOG(ERROR) << "failed to rename " << tmp_map_file << " to " << map_file;
        return kUncryptFileRenameError;
    }
    // Sync dir to make rename() result written to disk.
    std::string file_name = map_file;
    std::string dir_name = dirname(&file_name[0]);
    android::base::unique_fd dfd(open(dir_name.c_str(), O_RDONLY | O_DIRECTORY));
    if (dfd == -1) {
        PLOG(ERROR) << "failed to open dir " << dir_name;
        return kUncryptFileOpenError;
    }
    if (fsync(dfd) == -1) {
        PLOG(ERROR) << "failed to fsync " << dir_name;
        return kUncryptFileSyncError;
    }
    if (close(dfd.release()) == -1) {
        PLOG(ERROR) << "failed to close " << dir_name;
        return kUncryptFileCloseError;
    }
    return 0;
}

这里将ota升级包生成block.map,如果升级包在的分区data是加密,那么每次获得每个block实际索引时,读取解密后的block数据到buffer,每当有5个block数据时,然后把buffer数据写入到实际的对应索引block中。

执行完成uncrypt操作,接着2.11节,执行重启的操作。

如果要测试uncrypt的功能,可以在adb shell环境下测试,将ota包推到data目录下,直接执行uncrypt命名。

# uncrypt /data/ota.zip /cache/recovery/block.map
# cat /cache/recovery/block.map
/dev/block/bootdevice/by-name/userdata    //block device
1189005639 4096                           //文件大小,block块大小
2                                         //block块的个数
440320 524288                             //第一个block块的范围区间
561152 767469                             //第二个block块的范围区间

2.20 ST.rebootOrShutdown

接着2.11中run方法继续。

[->ShutdownThread.java]

 public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
        // 传过来reboot为true
        if (reboot) {
            Log.i(TAG, "Rebooting, reason: " + reason);
            PowerManagerService.lowLevelReboot(reason);
            Log.e(TAG, "Reboot failed, will attempt shutdown instead");
            reason = null;
        } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) {
            // vibrate before shutting down
            Vibrator vibrator = new SystemVibrator(context);
            try {
                vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
            } catch (Exception e) {
                // Failure to vibrate shouldn't interrupt shutdown.  Just log it.
                Log.w(TAG, "Failed to vibrate during shutdown.", e);
            }

            // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
            try {
                Thread.sleep(SHUTDOWN_VIBRATE_MS);
            } catch (InterruptedException unused) {
            }
        }
        // Shutdown power
        Log.i(TAG, "Performing low-level shutdown...");
        PowerManagerService.lowLevelShutdown(reason);
    }

2.20.1 PWS.lowLevelReboot

[->PowerManagerService.java]

 /**
     * Low-level function to reboot the device. On success, this
     * function doesn't return. If more than 20 seconds passes from
     * the time a reboot is requested, this method returns.
     *
     * @param reason code to pass to the kernel (e.g. "recovery"), or null.
     */
    public static void lowLevelReboot(String reason) {
        if (reason == null) {
            reason = "";
        }

        // If the reason is "quiescent", it means that the boot process should proceed
        // without turning on the screen/lights.
        // The "quiescent" property is sticky, meaning that any number
        // of subsequent reboots should honor the property until it is reset.
        if (reason.equals(PowerManager.REBOOT_QUIESCENT)) {
            sQuiescent = true;
            reason = "";
        } else if (reason.endsWith("," + PowerManager.REBOOT_QUIESCENT)) {
            sQuiescent = true;
            reason = reason.substring(0,
                    reason.length() - PowerManager.REBOOT_QUIESCENT.length() - 1);
        }

        if (reason.equals(PowerManager.REBOOT_RECOVERY)
                || reason.equals(PowerManager.REBOOT_RECOVERY_UPDATE)) {
            reason = "recovery";
        }

        if (sQuiescent) {
            // Pass the optional "quiescent" argument to the bootloader to let it know
            // that it should not turn the screen/lights on.
            reason = reason + ",quiescent";
        }
        //重启操作
        SystemProperties.set("sys.powerctl", "reboot," + reason);
        try {
            Thread.sleep(20 * 1000L);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        Slog.wtf(TAG, "Unexpected return from lowLevelReboot!");
    }
2.20.2 PWS.lowLevelReboot
  public static void lowLevelShutdown(String reason) {
        if (reason == null) {
            reason = "";
        }
        //关机
        SystemProperties.set("sys.powerctl", "shutdown," + reason);
    }

2.21 小结

OTA升级重启前,主要的操作是对升级包进行处理的过程,对升级包提前处理的原因是因为在进入recovery模式进行升级时无法加载升级包所在的分区。在重启前的主要操作如下:

1.通过SystemProperties设置属性(setup-bcb),向BCB中设置升级的Command;

2.通过SystemProperties设置属性(uncrypt),开启uncrypt服务,将升级包生成一系列的block块,recovery可以读取block.map文件并获取这个文件的数据作为升级包;

3.uncrypt完成后,重启。

三、OTA升级重启后

终端重启后,加载bootloader过程中,由于之前写入的recovery的command,则将进入recovery模式.

3.1 aboot.aboot_init

[->aboot.c]

void aboot_init(const struct app_descriptor *app)
{
	unsigned reboot_mode = 0;
	int boot_err_type = 0;
	int boot_slot = INVALID;

	/* Initialise wdog to catch early lk crashes */
#if WDOG_SUPPORT
	msm_wdog_init();
#endif

	/* Setup page size information for nv storage */
	if (target_is_emmc_boot())
	{
		page_size = mmc_page_size();
		page_mask = page_size - 1;
		mmc_blocksize = mmc_get_device_blocksize();
		mmc_blocksize_mask = mmc_blocksize - 1;
	}
	else
	{
		page_size = flash_page_size();
		page_mask = page_size - 1;
	}
	ASSERT((MEMBASE + MEMSIZE) > MEMBASE);

	read_device_info(&device);
	read_allow_oem_unlock(&device);

	/* Detect multi-slot support */
	if (partition_multislot_is_supported())
	{
		boot_slot = partition_find_active_slot();
		if (boot_slot == INVALID)
		{
			boot_into_fastboot = true;
			dprintf(INFO, "Active Slot: (INVALID)/n");
		}
		else
		{
			/* Setting the state of system to boot active slot */
			partition_mark_active_slot(boot_slot);
			dprintf(INFO, "Active Slot: (%s)/n", SUFFIX_SLOT(boot_slot));
		}
	}

	/* Display splash screen if enabled */
#if DISPLAY_SPLASH_SCREEN
#if NO_ALARM_DISPLAY
	if (!check_alarm_boot()) {
#endif
		dprintf(SPEW, "Display Init: Start/n");
#if DISPLAY_HDMI_PRIMARY
	if (!strlen(device.display_panel))
		strlcpy(device.display_panel, DISPLAY_PANEL_HDMI,
			sizeof(device.display_panel));
#endif
#if ENABLE_WBC
		/* Wait if the display shutdown is in progress */
		while(pm_app_display_shutdown_in_prgs());
		if (!pm_appsbl_display_init_done())
			target_display_init(device.display_panel);
		else
			display_image_on_screen();
#else
		target_display_init(device.display_panel);
#endif
		dprintf(SPEW, "Display Init: Done/n");
#if NO_ALARM_DISPLAY
	}
#endif
#endif

	target_serialno((unsigned char *) sn_buf);
	dprintf(SPEW,"serial number: %s/n",sn_buf);

	memset(display_panel_buf, '/0', MAX_PANEL_BUF_SIZE);

	/*
	 * Check power off reason if user force reset,
	 * if yes phone will do normal boot.
	 */
	if (is_user_force_reset())
		goto normal_boot;

	/* Check if we should do something other than booting up */
	if (keys_get_state(KEY_VOLUMEUP) && keys_get_state(KEY_VOLUMEDOWN))
	{
		dprintf(ALWAYS,"dload mode key sequence detected/n");
		reboot_device(EMERGENCY_DLOAD);
		dprintf(CRITICAL,"Failed to reboot into dload mode/n");

		boot_into_fastboot = true;
	}
	if (!boot_into_fastboot)
	{
		if (keys_get_state(KEY_HOME) || keys_get_state(KEY_VOLUMEUP))
			boot_into_recovery = 1;
		if (!boot_into_recovery &&
			(keys_get_state(KEY_BACK) || keys_get_state(KEY_VOLUMEDOWN)))
			boot_into_fastboot = true;
	}
	#if NO_KEYPAD_DRIVER
	if (fastboot_trigger())
		boot_into_fastboot = true;
	#endif

#if USE_PON_REBOOT_REG
	reboot_mode = check_hard_reboot_mode();
#else
	reboot_mode = check_reboot_mode();
#endif
	if (reboot_mode == RECOVERY_MODE)
	{
		boot_into_recovery = 1;
	}
	else if(reboot_mode == FASTBOOT_MODE)
	{
		boot_into_fastboot = true;
	}
	else if(reboot_mode == ALARM_BOOT)
	{
		boot_reason_alarm = true;
	}
#if VERIFIED_BOOT || VERIFIED_BOOT_2
	else if (VB_M <= target_get_vb_version())
	{
		if (reboot_mode == DM_VERITY_ENFORCING)
		{
			device.verity_mode = 1;
			write_device_info(&device);
		}
#if ENABLE_VB_ATTEST
		else if (reboot_mode == DM_VERITY_EIO)
#else
		else if (reboot_mode == DM_VERITY_LOGGING)
#endif
		{
			device.verity_mode = 0;
			write_device_info(&device);
		}
		else if (reboot_mode == DM_VERITY_KEYSCLEAR)
		{
			if(send_delete_keys_to_tz())
				ASSERT(0);
		}
	}
#endif

normal_boot:
	if (!boot_into_fastboot)
	{
		if (target_is_emmc_boot())
		{
			if(emmc_recovery_init())
				dprintf(ALWAYS,"error in emmc_recovery_init/n");
			if(target_use_signed_kernel())
			{
				if((device.is_unlocked) || (device.is_tampered))
				{
				#ifdef TZ_TAMPER_FUSE
					set_tamper_fuse_cmd(HLOS_IMG_TAMPER_FUSE);
				#endif
				#if USE_PCOM_SECBOOT
					set_tamper_flag(device.is_tampered);
				#endif
				}
			}

retry_boot:
			/* Trying to boot active partition */
			if (partition_multislot_is_supported())
			{
				boot_slot = partition_find_boot_slot();
				partition_mark_active_slot(boot_slot);
				if (boot_slot == INVALID)
					goto fastboot;
			}

			boot_err_type = boot_linux_from_mmc();
			switch (boot_err_type)
			{
				case ERR_INVALID_PAGE_SIZE:
				case ERR_DT_PARSE:
				case ERR_ABOOT_ADDR_OVERLAP:
				case ERR_INVALID_BOOT_MAGIC:
					if(partition_multislot_is_supported())
					{
						/*
						 * Deactivate current slot, as it failed to
						 * boot, and retry next slot.
						 */
						partition_deactivate_slot(boot_slot);
						goto retry_boot;
					}
					else
						break;
				default:
					break;
				/* going to fastboot menu */
			}
		}
		else
		{
		    //见3.2节
			recovery_init();
	#if USE_PCOM_SECBOOT
		if((device.is_unlocked) || (device.is_tampered))
			set_tamper_flag(device.is_tampered);
	#endif
			boot_linux_from_flash();
		}
		dprintf(CRITICAL, "ERROR: Could not do normal boot. Reverting "
			"to fastboot mode./n");
	}

fastboot:
	/* We are here means regular boot did not happen. Start fastboot. */

	/* register aboot specific fastboot commands */
	aboot_fastboot_register_commands();

	/* dump partition table for debug info */
	partition_dump();

	/* initialize and start fastboot */
#if !VERIFIED_BOOT_2
	fastboot_init(target_get_scratch_address(), target_get_max_flash_size());
#else
	/* Add salt buffer offset at start of image address to copy VB salt */
	fastboot_init(ADD_SALT_BUFF_OFFSET(target_get_scratch_address()),
		SUB_SALT_BUFF_OFFSET(target_get_max_flash_size()));
#endif
#if FBCON_DISPLAY_MSG
	display_fastboot_menu();
#endif
}

aboot执行后读取bootloader中command命令,执行recovery_init.

3.2 recovery.recovery_init

[->recovery.c]

/* Bootloader / Recovery Flow
 *
 * On every boot, the bootloader will read the recovery_message
 * from flash and check the command field.  The bootloader should
 * deal with the command field not having a 0 terminator correctly
 * (so as to not crash if the block is invalid or corrupt).
 *
 * The bootloader will have to publish the partition that contains
 * the recovery_message to the linux kernel so it can update it.
 *
 * if command == "boot-recovery" -> boot recovery.img
 * else if command == "update-radio" -> update radio image (below)
 * else -> boot boot.img (normal boot)
 *
 * Radio Update Flow
 * 1. the bootloader will attempt to load and validate the header
 * 2. if the header is invalid, status="invalid-update", goto #8
 * 3. display the busy image on-screen
 * 4. if the update image is invalid, status="invalid-radio-image", goto #8
 * 5. attempt to update the firmware (depending on the command)
 * 6. if successful, status="okay", goto #8
 * 7. if failed, and the old image can still boot, status="failed-update"
 * 8. write the recovery_message, leaving the recovery field
 *    unchanged, updating status, and setting command to
 *    "boot-recovery"
 * 9. reboot
 *
 * The bootloader will not modify or erase the cache partition.
 * It is recovery's responsibility to clean up the mess afterwards.
 */

int recovery_init (void)
{
	struct recovery_message msg;
	char partition_name[32];
	unsigned valid_command = 0;
	int update_status = 0;

	// get recovery message
	if (get_recovery_message(&msg))
		return -1;
	msg.command[sizeof(msg.command)-1] = '/0'; //Ensure termination
	if (msg.command[0] != 0 && msg.command[0] != 255) {
		dprintf(INFO,"Recovery command: %d %s/n",
			sizeof(msg.command), msg.command);
	}

	if (!strcmp("boot-recovery",msg.command))
	{
		if(!strcmp("RADIO",msg.status))
		{
			/* We're now here due to radio update, so check for update status */
			int ret = get_boot_info_apps(UPDATE_STATUS, (unsigned int *) &update_status);

			if(!ret && (update_status & 0x01))
			{
				dprintf(INFO,"radio update success/n");
				strlcpy(msg.status, "OKAY", sizeof(msg.status));
			}
			else
			{
				dprintf(INFO,"radio update failed/n");
				strlcpy(msg.status, "failed-update", sizeof(msg.status));
			}
			strlcpy(msg.command, "", sizeof(msg.command));	// clearing recovery command
			set_recovery_message(&msg);	// send recovery message
			boot_into_recovery = 1;		// Boot in recovery mode
			return 0;
		}
		boot_into_recovery = 1;		// Boot in recovery mode
		return 0;
	}

	if (!strcmp("update-radio",msg.command)) {
		dprintf(INFO,"start radio update/n");
		valid_command = 1;
		strlcpy(partition_name, "FOTA", sizeof(partition_name));
	}

	//Todo: Add support for bootloader update too.

	if(!valid_command) {
		//We need not to do anything
		return 0; // Boot in normal mode
	}

	if (set_ssd_radio_update(partition_name)) {
		/* If writing to FOTA partition fails */
		strlcpy(msg.command, "", sizeof(msg.command));
		strlcpy(msg.status, "failed-update", sizeof(msg.status));
		goto SEND_RECOVERY_MSG;
	}
	else {
		/* Setting this to check the radio update status */
		strlcpy(msg.command, "boot-recovery", sizeof(msg.command));
		strlcpy(msg.status, "RADIO", sizeof(msg.status));
		goto SEND_RECOVERY_MSG;
	}
	strlcpy(msg.status, "OKAY", sizeof(msg.status));

SEND_RECOVERY_MSG:
	set_recovery_message(&msg);	// send recovery message
	boot_into_recovery = 1;		// Boot in recovery mode
	reboot_device(0);
	return 0;
}

参照代码前注释boot recovery.img之后,将会执行recovery.cpp中的main方法。

3.3 recovery.main

[->recovery.cpp]

/*
 * The recovery tool communicates with the main system through /cache files.
 *   /cache/recovery/command - INPUT - command line for tool, one arg per line
 *   /cache/recovery/log - OUTPUT - combined log file from recovery run(s)
 *
 * The arguments which may be supplied in the recovery.command file:
 *   --update_package=path - verify install an OTA package file
 *   --wipe_data - erase user data (and cache), then reboot
 *   --prompt_and_wipe_data - prompt the user that data is corrupt,
 *       with their consent erase user data (and cache), then reboot
 *   --wipe_cache - wipe cache (but not user data), then reboot
 *   --set_encrypted_filesystem=on|off - enables / diasables encrypted fs
 *   --just_exit - do nothing; exit and reboot
 *
 * After completing, we remove /cache/recovery/command and reboot.
 * Arguments may also be supplied in the bootloader control block (BCB).
 * These important scenarios must be safely restartable at any point:
 *
 * FACTORY RESET
 * 1. user selects "factory reset"
 * 2. main system writes "--wipe_data" to /cache/recovery/command
 * 3. main system reboots into recovery
 * 4. get_args() writes BCB with "boot-recovery" and "--wipe_data"
 *    -- after this, rebooting will restart the erase --
 * 5. erase_volume() reformats /data
 * 6. erase_volume() reformats /cache
 * 7. finish_recovery() erases BCB
 *    -- after this, rebooting will restart the main system --
 * 8. main() calls reboot() to boot main system
 *
 * OTA INSTALL
 * 1. main system downloads OTA package to /cache/some-filename.zip
 * 2. main system writes "--update_package=/cache/some-filename.zip"
 * 3. main system reboots into recovery
 * 4. get_args() writes BCB with "boot-recovery" and "--update_package=..."
 *    -- after this, rebooting will attempt to reinstall the update --
 * 5. install_package() attempts to install the update
 *    NOTE: the package install must itself be restartable from any point
 * 6. finish_recovery() erases BCB
 *    -- after this, rebooting will (try to) restart the main system --
 * 7. ** if install failed **
 *    7a. prompt_and_wait() shows an error icon and waits for the user
 *    7b. the user reboots (pulling the battery, etc) into the main system
 */
 
 int main(int argc, char **argv) {
  // We don't have logcat yet under recovery; so we'll print error on screen and
  // log to stdout (which is redirected to recovery.log) as we used to do.
  android::base::InitLogging(argv, &UiLogger);

  // Take last pmsg contents and rewrite it to the current pmsg session.
  static const char filter[] = "recovery/";
  // Do we need to rotate?
  bool doRotate = false;

  __android_log_pmsg_file_read(LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter, logbasename, &doRotate);
  // Take action to refresh pmsg contents
  __android_log_pmsg_file_read(LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter, logrotate, &doRotate);

  // If this binary is started with the single argument "--adbd",
  // instead of being the normal recovery binary, it turns into kind
  // of a stripped-down version of adbd that only supports the
  // 'sideload' command.  Note this must be a real argument, not
  // anything in the command file or bootloader control block; the
  // only way recovery should be run with this argument is when it
  // starts a copy of itself from the apply_from_adb() function.
  if (argc == 2 && strcmp(argv[1], "--adbd") == 0) {
    minadbd_main();
    return 0;
  }

  time_t start = time(nullptr);

  // redirect_stdio should be called only in non-sideload mode. Otherwise
  // we may have two logger instances with different timestamps.
  redirect_stdio(TEMPORARY_LOG_FILE);

  printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start));

  load_volume_table();
  has_cache = volume_for_mount_point(CACHE_ROOT) != nullptr;

  std::vector<std::string> args = get_args(argc, argv);
  std::vector<char*> args_to_parse(args.size());
  std::transform(args.cbegin(), args.cend(), args_to_parse.begin(),
                 [](const std::string& arg) { return const_cast<char*>(arg.c_str()); });

  const char* update_package = nullptr;
  bool should_wipe_data = false;
  bool should_prompt_and_wipe_data = false;
  bool should_wipe_cache = false;
  bool should_wipe_ab = false;
  size_t wipe_package_size = 0;
  bool show_text = false;
  bool sideload = false;
  bool sideload_auto_reboot = false;
  bool just_exit = false;
  bool shutdown_after = false;
  int retry_count = 0;
  bool security_update = false;
  int status = INSTALL_SUCCESS;
  bool mount_required = true;

  if (has_cache && ensure_path_mounted(CACHE_ROOT) == 0) {
  //Create /cache/recovery specifically if it is not created
  //As in cases where device is booted into recovery directly after
  //flashing recovery folder is not created in init
    mkdir_recursively(CACHE_LOG_DIR, 0777, false, sehandle);
  }

  int arg;
  int option_index;
  //解析参数
  while ((arg = getopt_long(args_to_parse.size(), args_to_parse.data(), "", OPTIONS,
                            &option_index)) != -1) {
    switch (arg) {
      case 'n':
        android::base::ParseInt(optarg, &retry_count, 0);
        break;
      case 'u':
        update_package = optarg;
        break;
      case 'w':
        should_wipe_data = true;
        break;
      case 'c':
        should_wipe_cache = true;
        break;
      case 't':
        show_text = true;
        break;
      case 's':
        sideload = true;
        break;
      case 'a':
        sideload = true;
        sideload_auto_reboot = true;
        break;
      case 'x':
        just_exit = true;
        break;
      case 'l':
        locale = optarg;
        break;
      case 'p':
        shutdown_after = true;
        break;
      case 'r':
        reason = optarg;
        break;
      case 'e':
        security_update = true;
        break;
      case 0: {
        std::string option = OPTIONS[option_index].name;
        if (option == "wipe_ab") {
          should_wipe_ab = true;
        } else if (option == "wipe_package_size") {
          android::base::ParseUint(optarg, &wipe_package_size);
        } else if (option == "prompt_and_wipe_data") {
          should_prompt_and_wipe_data = true;
        }
        break;
      }
      case '?':
        LOG(ERROR) << "Invalid command argument";
        continue;
    }
  }

  if (locale.empty()) {
    if (has_cache) {
      locale = load_locale_from_cache();
    }

    if (locale.empty()) {
      locale = DEFAULT_LOCALE;
    }
  }

  printf("locale is [%s]/n", locale.c_str());
  printf("stage is [%s]/n", stage.c_str());
  printf("reason is [%s]/n", reason);

  Device* device = make_device();
  if (android::base::GetBoolProperty("ro.boot.quiescent", false)) {
    printf("Quiescent recovery mode./n");
    ui = new StubRecoveryUI();
  } else {
    ui = device->GetUI();

    if (!ui->Init(locale)) {
      printf("Failed to initialize UI, use stub UI instead./n");
      ui = new StubRecoveryUI();
    }
  }

  // Set background string to "installing security update" for security update,
  // otherwise set it to "installing system update".
  ui->SetSystemUpdateText(security_update);

  int st_cur, st_max;
  if (!stage.empty() && sscanf(stage.c_str(), "%d/%d", &st_cur, &st_max) == 2) {
    ui->SetStage(st_cur, st_max);
  }

  ui->SetBackground(RecoveryUI::NONE);
  if (show_text) ui->ShowText(true);

  sehandle = selinux_android_file_context_handle();
  selinux_android_set_sehandle(sehandle);
  if (!sehandle) {
    ui->Print("Warning: No file_contexts/n");
  }

  device->StartRecovery();

  printf("Command:");
  for (const auto& arg : args) {
    printf(" /"%s/"", arg.c_str());
  }
  printf("/n/n");

   if (update_package) {
        if (!strncmp("/sdcard", update_package, 7)) {
            //If this is a UFS device lets mount the sdcard ourselves.Depending
            //on if the device is UFS or EMMC based the path to the sdcard
            //device changes so we cannot rely on the block dev path from
            //recovery.fstab file
            if (is_ufs_dev()) {
                if(do_sdcard_mount_for_ufs() != 0) {
                    status = INSTALL_ERROR;
                    goto error;
                }
                mount_required = false;
            } else {
                ui->Print("Update via sdcard on EMMC dev. Using path from fstab/n");
            }
        }
    }

  property_list(print_property, nullptr);
  printf("/n");

  ui->Print("Supported API: %d/n", kRecoveryApiVersion);

  if (update_package != nullptr) {
    // It's not entirely true that we will modify the flash. But we want
    // to log the update attempt since update_package is non-NULL.
    modified_flash = true;

    if (!is_battery_ok()) {
      ui->Print("battery capacity is not enough for installing package, needed is %d%%/n",
                BATTERY_OK_PERCENTAGE);
      // Log the error code to last_install when installation skips due to
      // low battery.
      log_failure_code(kLowBattery, update_package);
      status = INSTALL_SKIPPED;
    } else if (bootreason_in_blacklist()) {
      // Skip update-on-reboot when bootreason is kernel_panic or similar
      ui->Print("bootreason is in the blacklist; skip OTA installation/n");
      log_failure_code(kBootreasonInBlacklist, update_package);
      status = INSTALL_SKIPPED;
    } else {
      // It's a fresh update. Initialize the retry_count in the BCB to 1; therefore we can later
      // identify the interrupted update due to unexpected reboots.
      if (retry_count == 0) {
        set_retry_bootloader_message(retry_count + 1, args);
      }

      //见3.4节,安装升级包
      status = install_package(update_package, &should_wipe_cache, TEMPORARY_INSTALL_FILE, mount_required,
                                retry_count);
      if (status == INSTALL_SUCCESS && should_wipe_cache) {
        wipe_cache(false, device);
      }
      if (status != INSTALL_SUCCESS) {
        ui->Print("Installation aborted./n");
        // When I/O error happens, reboot and retry installation RETRY_LIMIT
        // times before we abandon this OTA update.
        if (status == INSTALL_RETRY && retry_count < RETRY_LIMIT) {
          copy_logs();
          retry_count += 1;
          set_retry_bootloader_message(retry_count, args);
          // Print retry count on screen.
          ui->Print("Retry attempt %d/n", retry_count);

          // Reboot and retry the update
          if (!reboot("reboot,recovery")) {
            ui->Print("Reboot failed/n");
          } else {
            while (true) {
              pause();
            }
          }
        }
        // If this is an eng or userdebug build, then automatically
        // turn the text display on if the script fails so the error
        // message is visible.
        if (is_ro_debuggable()) {
          ui->ShowText(true);
        }
      }
    }
  } else if (should_wipe_data) {
    if (!wipe_data(device)) {
      status = INSTALL_ERROR;
    }
  } else if (should_prompt_and_wipe_data) {
    ui->ShowText(true);
    ui->SetBackground(RecoveryUI::ERROR);
    if (!prompt_and_wipe_data(device)) {
      status = INSTALL_ERROR;
    }
    ui->ShowText(false);
  } else if (should_wipe_cache) {
    if (!wipe_cache(false, device)) {
      status = INSTALL_ERROR;
    }
  } else if (should_wipe_ab) {
    if (!wipe_ab_device(wipe_package_size)) {
      status = INSTALL_ERROR;
    }
  } else if (sideload) {
    // 'adb reboot sideload' acts the same as user presses key combinations
    // to enter the sideload mode. When 'sideload-auto-reboot' is used, text
    // display will NOT be turned on by default. And it will reboot after
    // sideload finishes even if there are errors. Unless one turns on the
    // text display during the installation. This is to enable automated
    // testing.
    if (!sideload_auto_reboot) {
      ui->ShowText(true);
    }
    status = apply_from_adb(&should_wipe_cache, TEMPORARY_INSTALL_FILE);
    if (status == INSTALL_SUCCESS && should_wipe_cache) {
      if (!wipe_cache(false, device)) {
        status = INSTALL_ERROR;
      }
    }
    ui->Print("/nInstall from ADB complete (status: %d)./n", status);
    if (sideload_auto_reboot) {
      ui->Print("Rebooting automatically./n");
    }
  } else if (!just_exit) {
    // If this is an eng or userdebug build, automatically turn on the text display if no command
    // is specified. Note that this should be called before setting the background to avoid
    // flickering the background image.
    if (is_ro_debuggable()) {
      ui->ShowText(true);
    }
    status = INSTALL_NONE;  // No command specified
    ui->SetBackground(RecoveryUI::NO_COMMAND);
  }

error:
  if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) {
    ui->SetBackground(RecoveryUI::ERROR);
    if (!ui->IsTextVisible()) {
      sleep(5);
    }
  }

  Device::BuiltinAction after = shutdown_after ? Device::SHUTDOWN : Device::REBOOT;
  // 1. If the recovery menu is visible, prompt and wait for commands.
  // 2. If the state is INSTALL_NONE, wait for commands. (i.e. In user build, manually reboot into
  //    recovery to sideload a package.)
  // 3. sideload_auto_reboot is an option only available in user-debug build, reboot the device
  //    without waiting.
  // 4. In all other cases, reboot the device. Therefore, normal users will observe the device
  //    reboot after it shows the "error" screen for 5s.
  if ((status == INSTALL_NONE && !sideload_auto_reboot) || ui->IsTextVisible()) {
    Device::BuiltinAction temp = prompt_and_wait(device, status);
    if (temp != Device::NO_ACTION) {
      after = temp;
    }
  }

  // Save logs and clean up before rebooting or shutting down.
  // 见3.8节
  finish_recovery();

  switch (after) {
    case Device::SHUTDOWN:
      ui->Print("Shutting down.../n");
      android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown,");
      break;

    case Device::REBOOT_BOOTLOADER:
      ui->Print("Rebooting to bootloader.../n");
      android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader");
      break;

    default:
      ui->Print("Rebooting.../n");
      reboot("reboot,");
      break;
  }
  while (true) {
    pause();
  }
  // Should be unreachable.
  return EXIT_SUCCESS;
}

获取参数后,根据相应的参数执行相应的操作,这里是执行OTA INSTALL的流程,install_package完成之执行finish_recovery操作,之后正式完成升级的操作。

3.4 install.install_package

[->install.cpp]

int install_package(const std::string& path, bool* wipe_cache, const std::string& install_file,
                    bool needs_mount, int retry_count) {
  CHECK(!path.empty());
  CHECK(!install_file.empty());
  CHECK(wipe_cache != nullptr);

  modified_flash = true;
  auto start = std::chrono::system_clock::now();

  int start_temperature = GetMaxValueFromThermalZone();
  int max_temperature = start_temperature;

  int result = 0;
  std::vector<std::string> log_buffer;
  if (needs_mount == true)
          result = setup_install_mounts();
  if (result != 0 ) {
    LOG(ERROR) << "failed to set up expected mounts for install; aborting";
    result = INSTALL_ERROR;
  } else {
    //见3.5节
    result = really_install_package(path, wipe_cache, needs_mount, &log_buffer, retry_count,
                                    &max_temperature);
  }

  // Measure the time spent to apply OTA update in seconds.
  std::chrono::duration<double> duration = std::chrono::system_clock::now() - start;
  int time_total = static_cast<int>(duration.count());

  bool has_cache = volume_for_mount_point("/cache") != nullptr;
  // Skip logging the uncrypt_status on devices without /cache.
  if (has_cache) {
    static constexpr const char* UNCRYPT_STATUS = "/cache/recovery/uncrypt_status";
    if (ensure_path_mounted(UNCRYPT_STATUS) != 0) {
      LOG(WARNING) << "Can't mount " << UNCRYPT_STATUS;
    } else {
      std::string uncrypt_status;
      if (!android::base::ReadFileToString(UNCRYPT_STATUS, &uncrypt_status)) {
        PLOG(WARNING) << "failed to read uncrypt status";
      } else if (!android::base::StartsWith(uncrypt_status, "uncrypt_")) {
        LOG(WARNING) << "corrupted uncrypt_status: " << uncrypt_status;
      } else {
        log_buffer.push_back(android::base::Trim(uncrypt_status));
      }
    }
  }

  // The first two lines need to be the package name and install result.
  std::vector<std::string> log_header = {
    path,
    result == INSTALL_SUCCESS ? "1" : "0",
    "time_total: " + std::to_string(time_total),
    "retry: " + std::to_string(retry_count),
  };

  int end_temperature = GetMaxValueFromThermalZone();
  max_temperature = std::max(end_temperature, max_temperature);
  if (start_temperature > 0) {
    log_buffer.push_back("temperature_start: " + std::to_string(start_temperature));
  }
  if (end_temperature > 0) {
    log_buffer.push_back("temperature_end: " + std::to_string(end_temperature));
  }
  if (max_temperature > 0) {
    log_buffer.push_back("temperature_max: " + std::to_string(max_temperature));
  }

  std::string log_content =
      android::base::Join(log_header, "/n") + "/n" + android::base::Join(log_buffer, "/n") + "/n";
  if (!android::base::WriteStringToFile(log_content, install_file)) {
    PLOG(ERROR) << "failed to write " << install_file;
  }

  // Write a copy into last_log.
  LOG(INFO) << log_content;

  return result;
}

3.5 install.really_install_package

[->install.cpp]

static int really_install_package(const std::string& path, bool* wipe_cache, bool needs_mount,
                                  std::vector<std::string>* log_buffer, int retry_count,
                                  int* max_temperature) {
  //ui显示                                
  ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
  ui->Print("Finding update package.../n");
  // Give verification half the progress bar...
  ui->SetProgressType(RecoveryUI::DETERMINATE);
  ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME);
  LOG(INFO) << "Update location: " << path;

  // Map the update package into memory.
  ui->Print("Opening update package.../n");

  if (needs_mount) {
    if (path[0] == '@') {
      ensure_path_mounted(path.substr(1).c_str());
    } else {
      ensure_path_mounted(path.c_str());
    }
  }

  MemMapping map;
  if (!map.MapFile(path)) {
    LOG(ERROR) << "failed to map file";
    log_buffer->push_back(android::base::StringPrintf("error: %d", kMapFileFailure));
    return INSTALL_CORRUPT;
  }

  // Verify package.
  // 校验升级包
  if (!verify_package(map.addr, map.length)) {
    log_buffer->push_back(android::base::StringPrintf("error: %d", kZipVerificationFailure));
    return INSTALL_CORRUPT;
  }

  // Try to open the package.
  ZipArchiveHandle zip;
  int err = OpenArchiveFromMemory(map.addr, map.length, path.c_str(), &zip);
  if (err != 0) {
    LOG(ERROR) << "Can't open " << path << " : " << ErrorCodeString(err);
    log_buffer->push_back(android::base::StringPrintf("error: %d", kZipOpenFailure));

    CloseArchive(zip);
    return INSTALL_CORRUPT;
  }

  // Additionally verify the compatibility of the package.
  if (!verify_package_compatibility(zip)) {
    log_buffer->push_back(android::base::StringPrintf("error: %d", kPackageCompatibilityFailure));
    CloseArchive(zip);
    return INSTALL_CORRUPT;
  }

  // Verify and install the contents of the package.
  ui->Print("Installing update.../n");
  if (retry_count > 0) {
    ui->Print("Retry attempt: %d/n", retry_count);
  }
  ui->SetEnableReboot(false);
  //见3.6节
  int result = try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature);
  ui->SetEnableReboot(true);
  ui->Print("/n");

  CloseArchive(zip);
  return result;
}

3.6 install.try_update_binary

[->install.cpp]

static int try_update_binary(const std::string& package, ZipArchiveHandle zip, bool* wipe_cache,
                             std::vector<std::string>* log_buffer, int retry_count,
                             int* max_temperature) {
  read_source_target_build(zip, log_buffer);

  int pipefd[2];
  pipe(pipefd);

  std::vector<std::string> args;
#ifdef AB_OTA_UPDATER
  //执行update_binary_command操作,见3.7节
  int ret = update_binary_command(package, zip, "/sbin/update_engine_sideload", retry_count,
                                  pipefd[1], &args);
#else
  int ret = update_binary_command(package, zip, "/tmp/update-binary", retry_count, pipefd[1],
                                  &args);
#endif
  if (ret) {
    close(pipefd[0]);
    close(pipefd[1]);
    log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
    return ret;
  }

  // When executing the update binary contained in the package, the
  // arguments passed are:
  //
  //   - the version number for this interface
  //
  //   - an FD to which the program can write in order to update the
  //     progress bar.  The program can write single-line commands:
  //
  //        progress <frac> <secs>
  //            fill up the next <frac> part of of the progress bar
  //            over <secs> seconds.  If <secs> is zero, use
  //            set_progress commands to manually control the
  //            progress of this segment of the bar.
  //
  //        set_progress <frac>
  //            <frac> should be between 0.0 and 1.0; sets the
  //            progress bar within the segment defined by the most
  //            recent progress command.
  //
  //        ui_print <string>
  //            display <string> on the screen.
  //
  //        wipe_cache
  //            a wipe of cache will be performed following a successful
  //            installation.
  //
  //        clear_display
  //            turn off the text display.
  //
  //        enable_reboot
  //            packages can explicitly request that they want the user
  //            to be able to reboot during installation (useful for
  //            debugging packages that don't exit).
  //
  //        retry_update
  //            updater encounters some issue during the update. It requests
  //            a reboot to retry the same package automatically.
  //
  //        log <string>
  //            updater requests logging the string (e.g. cause of the
  //            failure).
  //
  //   - the name of the package zip file.
  //
  //   - an optional argument "retry" if this update is a retry of a failed
  //   update attempt.
  //

  // Convert the vector to a NULL-terminated char* array suitable for execv.
  const char* chr_args[args.size() + 1];
  chr_args[args.size()] = nullptr;
  for (size_t i = 0; i < args.size(); i++) {
    chr_args[i] = args[i].c_str();
  }

  pid_t pid = fork();

  if (pid == -1) {
    close(pipefd[0]);
    close(pipefd[1]);
    PLOG(ERROR) << "Failed to fork update binary";
    log_buffer->push_back(android::base::StringPrintf("error: %d", kForkUpdateBinaryFailure));
    return INSTALL_ERROR;
  }

  if (pid == 0) {
    umask(022);
    close(pipefd[0]);
    execv(chr_args[0], const_cast<char**>(chr_args));
    // Bug: 34769056
    // We shouldn't use LOG/PLOG in the forked process, since they may cause
    // the child process to hang. This deadlock results from an improperly
    // copied mutex in the ui functions.
    fprintf(stdout, "E:Can't run %s (%s)/n", chr_args[0], strerror(errno));
    _exit(EXIT_FAILURE);
  }
  close(pipefd[1]);

  std::atomic<bool> logger_finished(false);
  std::thread temperature_logger(log_max_temperature, max_temperature, std::ref(logger_finished));

  *wipe_cache = false;
  bool retry_update = false;

  char buffer[1024];
  FILE* from_child = fdopen(pipefd[0], "r");
  while (fgets(buffer, sizeof(buffer), from_child) != nullptr) {
    std::string line(buffer);
    size_t space = line.find_first_of(" /n");
    std::string command(line.substr(0, space));
    if (command.empty()) continue;

    // Get rid of the leading and trailing space and/or newline.
    std::string args = space == std::string::npos ? "" : android::base::Trim(line.substr(space));

    if (command == "progress") {
      std::vector<std::string> tokens = android::base::Split(args, " ");
      double fraction;
      int seconds;
      if (tokens.size() == 2 && android::base::ParseDouble(tokens[0].c_str(), &fraction) &&
          android::base::ParseInt(tokens[1], &seconds)) {
        ui->ShowProgress(fraction * (1 - VERIFICATION_PROGRESS_FRACTION), seconds);
      } else {
        LOG(ERROR) << "invalid /"progress/" parameters: " << line;
      }
    } else if (command == "set_progress") {
      std::vector<std::string> tokens = android::base::Split(args, " ");
      double fraction;
      if (tokens.size() == 1 && android::base::ParseDouble(tokens[0].c_str(), &fraction)) {
        ui->SetProgress(fraction);
      } else {
        LOG(ERROR) << "invalid /"set_progress/" parameters: " << line;
      }
    } else if (command == "ui_print") {
      ui->PrintOnScreenOnly("%s/n", args.c_str());
      fflush(stdout);
    } else if (command == "wipe_cache") {
      *wipe_cache = true;
    } else if (command == "clear_display") {
      ui->SetBackground(RecoveryUI::NONE);
    } else if (command == "enable_reboot") {
      // packages can explicitly request that they want the user
      // to be able to reboot during installation (useful for
      // debugging packages that don't exit).
      ui->SetEnableReboot(true);
    } else if (command == "retry_update") {
      retry_update = true;
    } else if (command == "log") {
      if (!args.empty()) {
        // Save the logging request from updater and write to last_install later.
        log_buffer->push_back(args);
      } else {
        LOG(ERROR) << "invalid /"log/" parameters: " << line;
      }
    } else {
      LOG(ERROR) << "unknown command [" << command << "]";
    }
  }
  fclose(from_child);

  int status;
  waitpid(pid, &status, 0);

  logger_finished.store(true);
  finish_log_temperature.notify_one();
  temperature_logger.join();

  if (retry_update) {
    return INSTALL_RETRY;
  }
  if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
    LOG(ERROR) << "Error in " << package << " (Status " << WEXITSTATUS(status) << ")";
    return INSTALL_ERROR;
  }

  return INSTALL_SUCCESS;
}

3.7 install.update_binary_command

[->install.cpp]

int update_binary_command(const std::string& package, ZipArchiveHandle zip,
                          const std::string& binary_path, int retry_count, int status_fd,
                          std::vector<std::string>* cmd) {
  CHECK(cmd != nullptr);

  // On traditional updates we extract the update binary from the package.
  static constexpr const char* UPDATE_BINARY_NAME = "META-INF/com/google/android/update-binary";
  ZipString binary_name(UPDATE_BINARY_NAME);
  ZipEntry binary_entry;
  if (FindEntry(zip, binary_name, &binary_entry) != 0) {
    LOG(ERROR) << "Failed to find update binary " << UPDATE_BINARY_NAME;
    return INSTALL_CORRUPT;
  }

  unlink(binary_path.c_str());
  int fd = open(binary_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0755);
  if (fd == -1) {
    PLOG(ERROR) << "Failed to create " << binary_path;
    return INSTALL_ERROR;
  }

  int32_t error = ExtractEntryToFile(zip, &binary_entry, fd);
  close(fd);
  if (error != 0) {
    LOG(ERROR) << "Failed to extract " << UPDATE_BINARY_NAME << ": " << ErrorCodeString(error);
    return INSTALL_ERROR;
  }

  *cmd = {
    binary_path,
    std::to_string(kRecoveryApiVersion),
    std::to_string(status_fd),
    package,
  };
  if (retry_count > 0) {
    cmd->push_back("retry");
  }
  return 0;
}

前面介绍到update-binary相当于一个脚本解释器,能够识别updater-script中描述的操作。来看下updater-script中的内容:

...
show_progress(0.650000, 0);
ui_print("Patching system image unconditionally...");
block_image_update("/dev/block/bootdevice/by-name/system", package_extract_file("system.transfer.list"), "system.new.dat.br", "system.patch.dat") ||
  abort("E1001: Failed to update system image.");
show_progress(0.100000, 0);
ui_print("Patching vendor image unconditionally...");
block_image_update("/dev/block/bootdevice/by-name/vendor", package_extract_file("vendor.transfer.list"), "vendor.new.dat.br", "vendor.patch.dat") ||
  abort("E2001: Failed to update vendor image.");
show_progress(0.050000, 5);
package_extract_file("boot.img", "/dev/block/bootdevice/by-name/boot");
show_progress(0.200000, 10);
...

调用的是block_image_update,传入的是升级包里面的system.transfer.list和system.new.dat.br来实现升级。

block_image_update在bootable/recovery/updater/blockimg.cpp中,具体的实现PerformBlockImageUpdate函数中,这里不再详细展开。

void RegisterBlockImageFunctions() {
  RegisterFunction("block_image_verify", BlockImageVerifyFn);
  RegisterFunction("block_image_update", BlockImageUpdateFn);
  RegisterFunction("block_image_recover", BlockImageRecoverFn);
  RegisterFunction("check_first_block", CheckFirstBlockFn);
  RegisterFunction("range_sha1", RangeSha1Fn);
}

来system.transfer.list中的内容:

4
583603
0
0
erase 6,1020,7774,524808,527824,580432,773491
new 6,0,207,222,508,8286,8817
new 2,8817,9841
new 2,9841,10865
...
zero 6,524360,524808,527824,528336,579920,579984
zero 6,579984,580432,773491,774003,786268,786332
zero 2,786332,786431

其中

4:为transfer的版本,目前支持1-4版本

583603:为总共new的block数量

0:stash slots没有使用,所以这里两个都是0

erase:需要擦除的block块范围数

new: 需要写入的block块范围数

zero: 需要填充0的block块范围数

3.8 recovery.finish_recovery

[->recovery.cpp]

static void finish_recovery() {
  // Save the locale to cache, so if recovery is next started up without a '--locale' argument
  // (e.g., directly from the bootloader) it will use the last-known locale.
  if (!locale.empty() && has_cache) {
    LOG(INFO) << "Saving locale /"" << locale << "/"";
    if (ensure_path_mounted(LOCALE_FILE) != 0) {
      LOG(ERROR) << "Failed to mount " << LOCALE_FILE;
    } else if (!android::base::WriteStringToFile(locale, LOCALE_FILE)) {
      PLOG(ERROR) << "Failed to save locale to " << LOCALE_FILE;
    }
  }

  copy_logs();

  // Reset to normal system boot so recovery won't cycle indefinitely.
  std::string err;
  if (!clear_bootloader_message(&err)) {
    LOG(ERROR) << "Failed to clear BCB message: " << err;
  }

  // Remove the command file, so recovery won't repeat indefinitely.
  if (has_cache) {
    if (ensure_path_mounted(COMMAND_FILE) != 0 || (unlink(COMMAND_FILE) && errno != ENOENT)) {
      LOG(WARNING) << "Can't unlink " << COMMAND_FILE;
    }
    ensure_path_unmounted(CACHE_ROOT);
  }

  sync();  // For good measure.
}

完成升级后,清除BCB操作。

3.9 小结

ota升级重启后,主要的操作如下:

1.加载bootloader,读取bootloader中的command命令

2.读取到升级的命令后,boot recovery.img,recovery.cpp中的main函数执行。

3.执行install_package操作,这里会解析ota包中的内容(block.map的形式),执行相应的升级包中脚本操作,同时会同步进行一些ui的显示操作。

4.install_package操作完成后,最后finish_recovery,完成升级的操作。

四、总结

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Dk2eqWs-1577705272566)(https://skytoby.github.io/2019/Android%20OTA%E5%8D%87%E7%BA%A7%E6%B5%81%E7%A8%8B%E5%88%86%E6%9E%90/OTA.jpg)]
本文分析Android终端ota升级的全过程。从介绍ota升级包的格式和系统启动模式开始作为基础,后面详细分析了从升级重启前到重启后的详细流程,其中升级重启前的流程如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zInb41pn-1577704943833)(/Android OTA升级流程分析/OTA.jpg)]

OTA升级重启前,主要的操作是对升级包进行处理的过程,对升级包提前处理的原因是因为在进入recovery模式进行升级时无法加载升级包所在的分区。在重启前的主要操作如下:

1.通过SystemProperties设置属性(setup-bcb),向BCB中设置升级的Command;

2.通过SystemProperties设置属性(uncrypt),开启uncrypt服务,将升级包生成一系列的block块,recovery可以读取block.map文件并获取这个文件的数据作为升级包;

3.uncrypt完成后,重启。

ota升级重启后,主要的操作如下:

1.加载bootloader,读取bootloader中的command命令

2.读取到升级的命令后,boot recovery.img,recovery.cpp中的main函数执行。

3.执行install_package操作,这里会解析ota包中的内容(block.map的形式),执行相应的升级包中脚本操作,同时会同步进行一些ui的显示操作。

4.install_package操作完成后,最后finish_recovery,完成升级的操作。

附录

源码路径

frameworks/base/core/java/android/os/RecoverySystem.java
frameworks/base/services/core/java/com/android/server/RecoverySystemService.java
frameworks/base/core/java/android/os/PowerManager.java
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java

bootable/recovery/uncrypt/uncrypt.cpp
bootable/recovery/uncrypt/uncrypt.rc
bootable/bootloader/lk/app/aboot/aboot.c
bootable/bootloader/lk/app/aboot/recovery.c
bootable/recovery/updater/install.cpp
bootable/recovery/install.cpp
bootable/recovery/bootloader_message/include/bootloader_message/bootloader_message.h
bootable/recovery/updater/blockimg.cpp

  • 3
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值