Android Recovery 模式工作原理与定制实战

news2026/5/17 12:18:39
Recovery 是 Android 的救命系统,负责 OTA 升级、恢复出厂、用户数据加密管理。本文剖析 Recovery 的架构、启动流程、与主系统的通信机制,并演示如何修改并构建一个自定义 Recovery。一、Recovery 到底是什么?很多人以为 Recovery 是 Android 系统的一个模式,其实它是一个完全独立的 Linux 系统:有自己的 Kernel(实际上和主系统共享一个 boot 镜像的内核)、自己的 ramdisk、自己的 init、自己的应用。它和主系统的关系类似:PC 主系统 - PE 启动盘 Android - RecoveryRecovery 启动时只挂载最小化的文件系统,不启动 Android 框架(没有 Java VM、Zygote、SystemServer),纯 C/C 应用,占用资源极小。二、Recovery 的两种存在形式1. 传统形式:独立 recovery 分区分区表: boot (Kernel 主系统 ramdisk) recovery (Kernel recovery ramdisk)启动时 Bootloader 根据按键 / BCB 决定加载哪个 boot image。2. 现代形式:合并到 boot 中(A/B 设备)A/B 设备砍掉了独立 recovery 分区,把 recovery 的内容融合进 boot 的 ramdisk:boot.img ├── kernel └── ramdisk ├── /init ├── /system/bin/recovery ├── /sbin/... └── /etc/recovery.fstab启动到主系统还是 recovery,看 Kernel cmdline:# 主系统启动 androidboot.force_normal_boot1 # Recovery 启动 (不带 force_normal_boot)init在第一阶段读到这个参数后,执行不同的init.rc。三、Recovery 的目录结构(精简版)/ ├── init (静态链接的 init 二进制) ├── init.recovery.${hw}.rc (硬件相关 init 脚本) ├── etc/ │ ├── recovery.fstab (Recovery 用的 fstab) │ └── recovery-resource.dat ├── sbin/ │ ├── recovery (主程序) │ ├── adbd (USB adb 守护进程) │ └── busybox (基础命令) ├── res/ │ ├── images/ (UI 图片) │ └── recovery.cfg ├── system/ │ └── bin/ └── tmp/ (临时挂载点)recovery.fstab描述了 Recovery 需要挂载的分区:# src mnt_point type mnt_flags fs_mgr_flags /dev/block/by-name/system /system ext4 ro,barrier1 wait,first_stage_mount,logical /dev/block/by-name/userdata /data f2fs defaults wait,check,formattable,quota /dev/block/by-name/cache /cache ext4 defaults wait,check /dev/block/by-name/misc /misc emmc defaults defaults /dev/block/by-name/metadata /metadata f2fs defaults wait,formattable四、Recovery 主程序的核心流程源码在bootable/recovery/,核心入口在recovery_main.cpp:intmain(intargc,char**argv){// 1. 重定向 stdio 到 last_logredirect_stdio(/tmp/recovery.log);// 2. 启动 logd 服务staticconstexprstructoptionOPTIONS[]{{locale,required_argument,NULL,0},{fastboot,no_argument,NULL,0},{0,0,0,0},};// 3. 加载 fstabautofstabLoadFstab();// 4. 解析启动参数(来自 BCB)std::vectorstd::stringargsget_args(argc,argv);// 5. 初始化 Device 抽象autodevicemake_device();autouidevice-GetUI();ui-Init(locale);// 6. 进入主循环InstallResult statusINSTALL_NONE;if(update_package!nullptr){// OTA 包安装statusinstall_package(update_package,...);}elseif(should_wipe_data){wipe_data(device);}elseif(should_wipe_cache){wipe_cache(device);}elseif(should_wipe_ab){wipe_ab_device();}else{// 进入交互式菜单statusprompt_and_wait(device,retry_count);}// 7. 写回结果到 BCBfinish_recovery();// 8. 重启reboot(normal);return0;}prompt_and_wait 交互菜单InstallResultprompt_and_wait(Device*device,intretry_count){staticconstchar*HEADERS[]{Android Recovery,Use volume up/down to move highlight;,power button to select.,nullptr,};staticconstchar*ITEMS[]{Reboot system now,Apply update from ADB,Apply update from SD card,Wipe data/factory reset,Wipe cache partition,Mount /system,View recovery logs,Power off,nullptr,};while(true){intchosenget_menu_selection(HEADERS,ITEMS,...);switch(chosen){case0:returnINSTALL_SUCCESS;case1:apply_from_adb(device);break;case2:apply_from_sdcard(device);break;case3:wipe_data(device);break;case4:wipe_cache(device);break;case5:mount(/system);break;case6:choose_recovery_file(device);break;case7:reboot(poweroff);break;}}}五、与主系统的通信:BCB(Bootloader Control Block)我们在前文讲过,misc 分区存放 BCB。Recovery 主要靠 BCB 与外界沟通:主系统触发 Recovery// frameworks/base/services/core/java/com/android/server/RecoverySystem.javapublicstaticvoidrebootWipeUserData(Context context,...){bootCommand(context,--wipe_data,--reasonreason);}privatestaticvoidbootCommand(Context context,String...args){File RECOVERY_DIRnewFile(/cache/recovery);File COMMAND_FILEnewFile(RECOVERY_DIR,command);StringBuilder commandnewStringBuilder();for(String arg:args){command.append(arg).append(\n);}Files.write(command.toString().getBytes(),COMMAND_FILE);// 触发 reboot 到 recoverySystemProperties.set(sys.powerctl,reboot,recovery);}Recovery 启动后读命令std::vectorstd::stringget_args(intargc,char**argv){std::vectorstd::stringargs;// 1. 优先从 BCB 读 (Bootloader 写入)bootloader_message boot;if(read_bootloader_message(boot)){if(boot.command[0]!0){// boot.recovery 中是用 \n 分隔的命令列表std::vectorstd::stringtokensSplit(boot.recovery,\n);args.insert(args.end(),tokens.begin(),tokens.end());}}// 2. 从 /cache/recovery/command 读if(args.empty()){std::string content;if(ReadFileToString(/cache/recovery/command,content)){std::vectorstd::stringtokensSplit(content,\n);args.insert(args.end(),tokens.begin(),tokens.end());}}returnargs;}退出时清理 BCBvoidfinish_recovery(){// 把 BCB 清掉,下次启动直接进主系统bootloader_message boot{};write_bootloader_message(boot);// 删除 /cache 中的命令文件unlink(/cache/recovery/command);}六、OTA 安装的核心:install_packageRecovery 最重要的工作就是安装 OTA 包。流程:InstallResultinstall_package(conststd::stringpath,...){// 1. 校验 ZIP 签名 (PKCS#7)if(!verify_package(path,key_path)){returnINSTALL_CORRUPT;}// 2. 打开 ZIPZipArchiveHandle zip;if(OpenArchive(path.c_str(),zip)!0){returnINSTALL_CORRUPT;}// 3. 提取并执行 META-INF/com/google/android/update-binaryintpipefd[2];pipe(pipefd);pid_t pidfork();if(pid0){// 子进程close(pipefd[0]);charstatus_fd[8];snprintf(status_fd,sizeof(status_fd),%d,pipefd[1]);execl(/tmp/update-binary,update-binary,3,// versionstatus_fd,path.c_str(),nullptr);exit(1);}// 父进程读 update-binary 的进度close(pipefd[1]);FILE*ffdopen(pipefd[0],r);charbuf[256];while(fgets(buf,sizeof(buf),f)){// 解析 ui_print xxx, progress xxx, set_progress xxxhandle_progress(buf);}intstatus;waitpid(pid,status,0);returnWEXITSTATUS(status)0?INSTALL_SUCCESS:INSTALL_ERROR;}update-binary 协议update-binary通过文件描述符 3 与 Recovery 主进程通信,简单的文本协议:ui_print Hello world!\n progress 0.5 30\n (进度条 50%,持续 30 秒动画) set_progress 0.75\n (进度条 75%) firmware hboot hboot.img\n clear_display\n七、Recovery 的 UI 系统Recovery UI 是个独立的图形系统,不依赖 SurfaceFlinger / WindowManager,直接操作 framebuffer / DRM。核心类:RecoveryUI// bootable/recovery/recovery_ui/include/recovery_ui/ui.hclassRecoveryUI{public:virtualvoidInit(conststd::stringlocale)0;virtualvoidSetBackground(Icon icon)0;virtualvoidSetProgress(floatfraction)0;virtualvoidPrint(constchar*fmt,...)0;virtualintWaitKey()0;virtualvoidShowText(boolvisible)0;};具体实现ScreenRecoveryUI直接操作 framebuffer:voidScreenRecoveryUI::draw_screen_locked(){if(!show_text){// 显示进度条 logodraw_background_locked();draw_foreground_locked();}else{// 显示文字菜单SetColor(MENU);// ...for(inti0;imenu_items;i){if(iselected){DrawHighlightBar(0,y,width,char_height);SetColor(MENU_SEL_BG);}gr_text(menu_font,x,ybaseline,items[i].text,1);}}}voidScreenRecoveryUI::update_screen_locked(){draw_screen_locked();gr_flip();// 把 framebuffer 内容显示到屏幕}gr_flip()内部通过 ioctl 通知 DRM 切换显示缓冲区:intgr_flip(void){gr_drawgr_backup_disp_buffer(gr_draw);structfb_var_screeninfovi;ioctl(fd,FBIOGET_VSCREENINFO,vi);vi.yoffset(current_buffer?vi.yres:0);ioctl(fd,FBIOPAN_DISPLAY,vi);current_buffer!current_buffer;return0;}八、定制 Recovery 实战如果想给设备做一个自定义 Recovery,通常要做几件事:1. 准备源码# 拉 AOSP 源码repo init-uhttps://android.googlesource.com/platform/manifest-bandroid-13.0.0_r80 reposync# 配置自己的设备sourcebuild/envsetup.sh lunchyour_device-userdebug2. 修改设备 Device 类// device/yourcompany/yourdevice/recovery/recovery_ui.cpp#includerecovery_ui/device.h#includerecovery_ui/screen_ui.hclassYourDevice:publicDevice{public:YourDevice(RecoveryUI*ui):Device(ui){}boolPostWipeData()override{// 擦数据后做点啥ResetCustomerSettings();returntrue;}BuiltinActionInvokeMenuItem(size_t menu_position)override{// 添加自定义菜单项if(menu_positionYOUR_CUSTOM_ITEM){DoSomethingCool();returnNO_ACTION;}returnDevice::InvokeMenuItem(menu_position);}};Device*make_device(){returnnewYourDevice(newScreenRecoveryUI);}3. 修改 BoardConfig.mk# device/yourcompany/yourdevice/BoardConfig.mk TARGET_RECOVERY_UI_LIB : librecovery_ui_yourdevice TARGET_RECOVERY_FSTAB : device/yourcompany/yourdevice/recovery.fstab # 启用 fastbootd TARGET_USERIMAGES_USE_F2FS : true BOARD_USES_RECOVERY_AS_BOOT : true BOARD_BUILD_SYSTEM_ROOT_IMAGE : false # UI 主题 TARGET_RECOVERY_PIXEL_FORMAT : RGBX_88884. 编译makebootimage -j$(nproc)# 或者只编译 recovery (传统 boot/recovery 分离的设备)makerecoveryimage -j$(nproc)输出在out/target/product/device/:boot.img— A/B 设备recovery.img— 传统设备5. 刷写adbrebootbootloader# A/B 设备fastboot flash boot boot.img# 传统设备fastboot flash recovery recovery.img九、minimal recovery UI 代码示例(脱离 AOSP)下面是一个最简化的 framebuffer Recovery UI 主循环,展示原理:#includestdio.h#includestdint.h#includefcntl.h#includeunistd.h#includestring.h#includelinux/fb.h#includelinux/input.h#includesys/ioctl.h#includesys/mman.hstructfb_ctx{intfd;uint8_t*pixels;intwidth,height,stride;};staticintfb_init(structfb_ctx*fb){fb-fdopen(/dev/graphics/fb0,O_RDWR);if(fb-fd0)return-1;structfb_var_screeninfovi;structfb_fix_screeninfofi;ioctl(fb-fd,FBIOGET_VSCREENINFO,vi);ioctl(fb-fd,FBIOGET_FSCREENINFO,fi);fb-widthvi.xres;fb-heightvi.yres;fb-stridefi.line_length;size_tsizefb-stride*fb-height;fb-pixelsmmap(0,size,PROT_READ|PROT_WRITE,MAP_SHARED,fb-fd,0);return(fb-pixelsMAP_FAILED)?-1:0;}staticvoidfb_fill(structfb_ctx*fb,uint32_tcolor){uint32_t*p(uint32_t*)fb-pixels;intcountfb-stride*fb-height/4;for(inti0;icount;i)p[i]color;}staticintread_key(intinput_fd){structinput_eventev;while(read(input_fd,ev,sizeof(ev))sizeof(ev)){if(ev.typeEV_KEYev.value1){// key downreturnev.code;}}return-1;}intmain(void){structfb_ctxfb;if(fb_init(fb)0){fprintf(stderr,fb init failed\n);return1;}intinput_fdopen(/dev/input/event0,O_RDONLY);intselected0;constchar*items[]{Reboot system,Wipe data,Apply update,Power off,};intn_itemssizeof(items)/sizeof(items[0]);while(1){// 简化:仅用纯色背景表示当前选项uint32_tcolors[]{0xFF008000,0xFF800000,0xFF000080,0xFF808000};fb_fill(fb,colors[selected]);intkeyread_key(input_fd);if(keyKEY_VOLUMEUP)selected(selected-1n_items)%n_items;if(keyKEY_VOLUMEDOWN)selected(selected1)%n_items;if(keyKEY_POWER){printf(Selected: %s\n,items[selected]);if(selected0)execl(/sbin/reboot,reboot,NULL);if(selected3)execl(/sbin/poweroff,poweroff,NULL);break;}}return0;}这个程序展示了 Recovery UI 的底层本质:直接 mmap framebuffer 读 input event,没有任何 GUI 库。十、踩坑经验fstab 字段错一个,挂载失败:Recovery 没法挂数据分区,wipe 不了 → 注意wait,formattable这些 flagAVB 拒绝刷未签名的 recovery:解锁后才能刷自制版本,或者要用 OEM 签名sideload(adb sideload)的工作目录:推过来的包默认存到/tmp/update.zip,大包会 OOM,要把/tmp挂大点fastbootd 和 recovery 共享 ramdisk:同一个二进制,只是启动参数不同(--fastboot)国行机型可能锁 AVB key:无法刷自制 Recovery,需要单独的解锁工具十一、总结Recovery 是一个被严重低估的系统:它是一个完整的 Linux 系统,只是为了救命而设计得极度精简BCB /cache/command是主系统与 Recovery 之间的桥梁OTA 安装 update-binary是它的核心使命直接操作 framebuffer让它在最恶劣的环境下也能工作理解 Recovery 不仅能帮你做 OEM 定制,也能帮你在用户报开不了机时,快速定位 BCB 或者 fstab 的问题。最后一篇我们来讲 Bootloader 的世界 — U-Boot 的移植实战。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2616844.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…