一、ota流程之framwork installpackage流程

一、ota流程之framwork installpackage流程

关于install_package的流程,大部分应该已经比较了解,按方法流程执行,这里我们流程上还是做简单说明,主要要要说明其中的一些细节

1、setup-bcb

2、block.map的生成

 

按方法流程分析如下

framwork/base/core/java/android/os/RecoverySystem.java
@RequiresPermission(android.Manifest.permission.RECOVERY)
public static void installPackage(Context context, File packageFile)
            throws IOException {
  installPackage(context, packageFile, false);
}
//传入context,文件路径,processed = false
public static void installPackage(Context context, File packageFile, boolean processed)
            throws IOException {
        //防止并发执行请求。
        synchronized (sRequestLock) {
            //删除cache/recovery/log
            LOG_FILE.delete();
            // Must delete the file in case it was created by system server.
            //刪除cache/recovery/uncrypt文件,保证为当前系统服务创建
            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/")) {
                //这里传进来的就是false,如果是直接调取的installpackage
                if (processed) {
                    if (!BLOCK_MAP_FILE.exists()) {
                        Log.e(TAG, "Package claimed to have been processed but failed to find "
                                + "the block map file.");
                        throw new IOException("Failed to find block map file");
                    }
                } else {
                    //创建文件,该类属于字符流,所以使用完成后需要关闭该流
                    FileWriter uncryptFile = new FileWriter(UNCRYPT_PACKAGE_FILE);
                    try {
                        //写入升级包路径
                        uncryptFile.write(filename + "\n");
                    } finally {
                        uncryptFile.close();
                    }
                    // UNCRYPT_PACKAGE_FILE needs to be readable and writable
                    // by system server.如果unvrypt_file不可读也不可写,打印出错误
                    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文件
                    BLOCK_MAP_FILE.delete();
                }

                // If the package is on the /data partition, use the block map
                // file as the package name instead.
                //设定文件名为@/cache/recovery/block.map
                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";
            //最初的command,包含文件名和语言
            String command = filenameArg + localeArg;
            if (securityUpdate) {
                command += securityArg;
            }
            
            RecoverySystem rs = (RecoverySystem) context.getSystemService(
                    Context.RECOVERY_SERVICE);
            //判断是否可以正常写入bcb,传入command,
            //这里存在一个优化点,我们碰到很多解密失败的问题,那为什么不先解密完成后返回状态,再写入bcb呢,
            //这样直接判定失败,就不用在recovery出现failed to map file的问题
            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);
            //public static final String REBOOT_RECOVERY_UPDATE = "recovery-update";
            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?)");
        }
    }

setupBcb(command)

 

framwork/base/core/java/android/os/RecoverySystem.java
    /**
     * Talks to RecoverySystemService via Binder to clear up the BCB.
     */
    private boolean clearBcb() {
        try {
            return mService.clearBcb();
        } catch (RemoteException unused) {
        }
        return false;
    }

 

framwork/base/services/core/java/com/android/server/RecoverySystemService.java
@Override // Binder call
        public boolean clearBcb() {
            if (DEBUG) Slog.d(TAG, "clearBcb");
            synchronized (sRequestLock) {
                return setupOrClearBcb(false, null);
            }
        }

 

framwork/base/services/core/java/com/android/server/RecoverySystemService.java
//setupbcb传入 isSetup = true , command = 路径 + 语言
        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;
            }

            if (isSetup) {
                //属性“ ctrl.start ”和“ ctrl.stop ”是用来启动和停止服务。每一项服务必须在/init.rc中定义.系统启动时,
                //与init守护进程将解析init.rc和启动属性服务。一旦收到设置“ ctrl.start ”属性的请求,属性服务将使用该属性值作为服务名找到该服务,启动该服务。
                //这项服务的启动结果将会放入“ init.svc.<服务名>“属性中 。客户端应用程序可以轮询那个属性值,以确定结果。
                SystemProperties.set("ctl.start", "setup-bcb");
            } else {
                SystemProperties.set("ctl.start", "clear-bcb");
            }

            // Connect to the uncrypt service socket.
            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.
                // 将commands参数传入uncrypt.cpp,执行write_bootloader_message写入command信息
                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;
        }
    }

通过socket连接到uncryt服务,传入commang数据,并执行写入

framwork/base/services/core/java/com/android/server/RecoverySystemService.java
//通过socket连接到服务
        private LocalSocket connectService() {
            LocalSocket socket = new LocalSocket();
            boolean done = false;
            // The uncrypt socket will be created by init upon receiving the
            // service request. It may not be ready by this point. So we will
            // keep retrying until success or reaching timeout.
            for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {
                try {
                    socket.connect(new LocalSocketAddress(UNCRYPT_SOCKET,
                            LocalSocketAddress.Namespace.RESERVED));
                    done = true;
                    break;
                } catch (IOException ignored) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        Slog.w(TAG, "Interrupted:", e);
                    }
                }
            }
            if (!done) {
                Slog.e(TAG, "Timed out connecting to uncrypt socket");
                return null;
            }
            return socket;
        }

SystemProperties.set("ctl.start", "setup-bcb");后会启动system/etc/init/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

该服务的编译是在bootable/recovery/uncrypt/编译生成的,生成为system/bin/下的可执行文件uncrypt

开启服务后,这里传入了setup-bcb,那么我们看下uncrypt.cpp的main方法,

 

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;
    }
    bool success = false;
    switch (action) {
        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;
    }
}

setup_bcb(socket_fd)

 

static bool setup_bcb(const int socket) {
    // c5. receive message length
    int length;
    if (!android::base::ReadFully(socket, &length, 4)) {
        PLOG(ERROR) << "failed to read the length";
        return false;
    }
    length = ntohl(length);

    // c7. receive message
    std::string content;
    content.resize(length);
    if (!android::base::ReadFully(socket, &content[0], length)) {
        PLOG(ERROR) << "failed to read the message";
        return false;
    }
    LOG(INFO) << "  received command: [" << content << "] (" << content.size() << ")";
    std::vector<std::string> options = android::base::Split(content, "\n");
    std::string wipe_package;
    for (auto& option : options) {
        if (android::base::StartsWith(option, "--wipe_package=")) {
            std::string path = option.substr(strlen("--wipe_package="));
            if (!android::base::ReadFileToString(path, &wipe_package)) {
                PLOG(ERROR) << "failed to read " << path;
                return false;
            }
            option = android::base::StringPrintf("--wipe_package_size=%zu", wipe_package.size());
        }
    }

    // c8. setup the bcb command
    std::string err;
    //具体写入的方法,之前分析recovery流程的时候也有这个方法
    if (!write_bootloader_message(options, &err)) {
        LOG(ERROR) << "failed to set bootloader message: " << err;
        write_status_to_socket(-1, socket);
        return false;
    }
    if (!wipe_package.empty() && !write_wipe_package(wipe_package, &err)) {
        PLOG(ERROR) << "failed to set wipe package: " << err;
        write_status_to_socket(-1, socket);
        return false;
    }
    // c10. send "100" status
    write_status_to_socket(100, socket);
    return true;
}

pm.reboot(reason);

//public static final String REBOOT_RECOVERY_UPDATE = "recovery-update";
framwok/base/core/java/android/os/PowerManager.java
public void reboot(String reason) {
        try {
            mService.reboot(false, reason, true);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

 

framwork/base/services/core/java/com/android/server/power/PowerManagerService.java
@Override // Binder call 
        //mService.reboot(false, reason, true)
        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);
            }
            //关于clearCallingIdentity,restoreCallingIdentity https://blog.csdn.net/windskier/article/details/6921672
            final long ident = Binder.clearCallingIdentity();
            try {
                shutdownOrRebootInternal(HALT_MODE_REBOOT, confirm, reason, wait);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

 

    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()");
            }
        }

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                synchronized (this) {
                    if (haltMode == HALT_MODE_REBOOT_SAFE_MODE) {
                        ShutdownThread.rebootSafeMode(getUiContext(), confirm);
                    //传入的参数为HALT_MODE_REBOOT,执行reboot
                    } 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) {
                    }
                }
            }
        }
    }

 ShutdownThread.reboot(getUiContext(), reason, confirm);

framwok/base/services/core/java/com/android/server/power/ShutdownThread.java
public static void reboot(final Context context, String reason, boolean confirm) {
        mReboot = true;
        mRebootSafeMode = false;
        mRebootHasProgressBar = false;
        mReason = reason;
        shutdownInner(context, confirm);
    }

shutdownInner(context, confirm) ---> beginShutdownSequence(Context context)--->sInstance.start()--->run()

 

framwok/base/services/core/java/com/android/server/power/ShutdownThread.java
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);

        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.
            
        }
        rebootOrShutdown(mContext, mReboot, mReason);
    }

uncrypt();

 

framwok/base/services/core/java/com/android/server/power/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);
            }
        }
    }

 

framwork/base/core/java/android/os/RecoverySystem.java

@RequiresPermission(android.Manifest.permission.RECOVERY)
public static void processPackage(Context context,
                                      File packageFile,
                                      final ProgressListener listener)
        throws IOException {
   processPackage(context, packageFile, listener, null);
}

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

        if (!rs.uncrypt(filename, progressListener)) {
            throw new IOException("process package failed");
        }
    }

rs.uncrypt(filename, progressListener) 这个时候生成block.map的流程与前面setup-bcb的流程是相似的,使用binder机制回调uncrypt方法,使用socket写入数据和返回结果,具体由uncrypt.cpp执行

 

framwork/base/core/java/android/os/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;
    }

 

framwork/base/services/core/java/com/android/server/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.
                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;
            }
        }

uncrypt_wrapper(input_path, map_file, socket_fd);

uncrypt.cpp 官方注解

// This program takes a file on an ext4 filesystem and produces a list

// of the blocks that file occupies, which enables the file contents

// to be read directly from the block device without mounting the

// filesystem.

//

// 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.

bootable/recovery/uncrypt/uncrypt.cpp
传入参数input_path /data/data/com.adups.fota/files/adupsfota/update.zip
map_file /cache/recovery/block.map
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;
    //如果传入的input_path为空
    if (input_path == nullptr) {
        //从UNCRYPT_PATH_FILE中找到对应升级包的路径
        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并返回status的状态通过socket给到socket给到recoverysystem
    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;
    }

    write_status_to_socket(100, socket);

    return true;
}

uncrypt(input_path, map_file, socket);

bootable/recovery/uncrypt/uncrypt.cpp
static int uncrypt(const char* input_path, const char* map_file, const int socket) {
    //update package is "/data/data/com.adups.fota/files/adupsfota/update.zip"
    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];
    //函数说明:realpath()用来将参数path所指的相对路径转换成绝对路径后存于参数resolved_path所指的字符串数组或指针中
   //返回值: 成功则返回指向resolved_path的指针,失败返回NULL,错误代码存于errno
    if (realpath(input_path, path) == nullptr) {
        PLOG(ERROR) << "failed to convert \"" << input_path << "\" to absolute path";
        return kUncryptRealpathFindError;
    }
    //这三个参数来着fstab文件
    bool encryptable;
    bool encrypted;
    bool f2fs_fs;
    //block_dev /dev/block/platform/bootdevice/by-name/userdata
    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).    
    //I /system/bin/uncrypt: encryptable: yes
    //I /system/bin/uncrypt:   encrypted: yes
    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.
    //I /system/bin/uncrypt: writing block map /cache/recovery/block.ma
    if (strncmp(path, "/data/", 6) == 0) {
        LOG(INFO) << "writing block map " << map_file;
        return produce_block_map(path, map_file, blk_dev, encrypted, f2fs_fs, socket);
            
    }
    return 0;
}

produce_block_map(path, map_file, blk_dev, encrypted, f2fs_fs, socket); 核心方法

 

bootable/recovery/uncrypt/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;
    //如果block.map存在值,先删除之前的内容
    if (!android::base::RemoveFileIfExists(map_file, &err)) {
        LOG(ERROR) << "failed to remove the existing map file " << map_file << ": " << err;
        return kUncryptFileRemoveError;
    }
    //使用block.map.tmp进行写入操作
    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;
    //stat函数可以返回一个结构,里面包括文件的全部属性
    if (stat(path, &sb) != 0) {
        LOG(ERROR) << "failed to stat " << path;
        return kUncryptFileStatError;
    }
    ///system/bin/uncrypt:  block size: 4096 bytes
    LOG(INFO) << " block size: " << sb.st_blksize << " bytes";
    //计算出升级包大小占用的block总数
    int blocks = ((sb.st_size-1) / sb.st_blksize) + 1;
    ///system/bin/uncrypt:   file size: 13766599 bytes, 3361 blocks
    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));
    //将blk_dev block块,sb.st_size 所占用的总大小,sb.st_blksize单个block块大小
    //通过writestringtofd方法写入到block.map.tmp文件中
    if (!android::base::WriteStringToFd(s, mapfd)) {
        PLOG(ERROR) << "failed to write " << tmp_map_file;
        return kUncryptWriteError;
    }

    std::vector<std::vector<unsigned char>> buffers;
    if (encrypted) {
       //resize()的作用是改变vector中元素的数目。
       //如果n比当前的vector元素数目要小,vector的容量要缩减到resize的第一个参数大小,既n。并移除那些超出n的元素同时销毁他们。
       //如果n比当前vector元素数目要大,在vector的末尾扩展需要的元素数目,如果第二个参数val指定了,扩展的新元素初始化为val的副本,否则按类型默认初始化。
       //申请5个blocksize大小的buffer空间
       buffers.resize(WINDOW_SIZE, std::vector<unsigned char>(sb.st_blksize));
    }
    int head_block = 0;
    int head = 0, tail = 0;
    //打开升级包路径data/data..../update.zip 句柄为fd
    android::base::unique_fd fd(open(path, O_RDONLY));
    if (fd == -1) {
        PLOG(ERROR) << "failed to open " << path << " for reading";
        return kUncryptFileOpenError;
    }
    //打开dev_blk /dev/block/platform/bootdevice/by-name/userdata 句柄为wfd
    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;
        }
    }

// F2FS-specific ioctl
// It requires the below kernel commit merged in v4.16-rc1.
//   1ad71a27124c ("f2fs: add an ioctl to disable GC for specific file")
// In android-4.4,
//   56ee1e817908 ("f2fs: updates on v4.16-rc1")
// In android-4.9,
//   2f17e34672a8 ("f2fs: updates on v4.16-rc1")
// In android-4.14,
//   ce767d9a55bc ("f2fs: updates on v4.16-rc1")
#ifndef F2FS_IOC_SET_PIN_FILE
#ifndef F2FS_IOCTL_MAGIC
#define F2FS_IOCTL_MAGIC    0xf5
#endif
#define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32)
#define F2FS_IOC_GET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 14, __u32)
#endif
//生成于升级包对应的稀疏列表
    if (f2fs_fs) {
        __u32 set = 1;
        int error = ioctl(fd, F2FS_IOC_SET_PIN_FILE, &set);
        // Don't break the old kernels which don't support it.
        if (error < 0) {
            PLOG(ERROR) << "Failed to set pin_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);
            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
        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);
        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;
    }
    //把稀疏列表写入到block.map.tmp
    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;
        }
    }
    //修改名称block.map.tmp 为 block.map
    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;
}

rebootOrShutdown(mContext, mReboot, mReason);

framwork/base/services/core/java/com/android/server/power/ShutdownThread.java 
public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
        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) {
            }
        }

        ///M: added for shutdown Enhancement@{
        sInstance.mLowLevelShutdownSeq(context);
        /// @}

        // Shutdown power
        Log.i(TAG, "Performing low-level shutdown...");
        PowerManagerService.lowLevelShutdown(reason);
    }

lowLevelReboot(String reason)

填坑1、关于把boot-recovery写入bcb

一开始我一直认为写入bcb的时候,直接把boot-command传入了 boot-recovery,但是在流程中,是最后写入属性SystemProperties.set("sys.powerctl", "reboot," + reason);调用reboot可执行程序,最后通过linux内核与bootloader交互进入recovery,这里给到的command就是boot-recovery

framwok/base/services/core/java/com/android/server/power/PowerManagerService.java 
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";
        }
        //设置重启参数,调用init和可执行程序reboot
        SystemProperties.set("sys.powerctl", "reboot," + reason);
        try {
            Thread.sleep(20 * 1000L);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        Slog.wtf(TAG, "Unexpected return from lowLevelReboot!");
    }

这基本上就时重启之前的流程了

 

上一篇:记一次yii2 上传文件


下一篇:用函数式编程,从0开发3D引擎和编辑器(二):函数式编程准备