一、ota流程之framwork installpackage流程

本文深入解析了OTA升级过程中的关键步骤,重点介绍了framework installpackage流程,涵盖了从安装包验证、解密、block map生成到系统重启的全过程。文章详细解释了如何通过binder机制与uncrypt服务交互,实现升级包的高效解密和block map文件的生成。

一、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
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值