recovery模式代码存放位置:bootable\recovery
一、主入口:
bootable\recovery\recovery.cpp -> int main()
具体分析下main函数:
int main(int argc, char **argv) {}
函数接受两个参数,grgc代表参数个数,argv代表参数列表
//对log进行了初始化:
__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);
//重定向logger的IO(只有non-sideload模式才去重定向):
redirect_stdio(TEMPORARY_LOG_FILE);
stdout:标准输出
stderr:标准错误输出
linux中的函数pipe管道,把两个进程之间的标准输入和标准输出连接起来的机制,从而提供一种让多个进程间通信的方法。当进程创建管道时,每次都需要提供两个文件描述符来操作管道,一个用来读,一个用来写,如代码中的栗子:int pipefd[2],pipefd[1]表示write,pipefd[0]表示 read。一个进程在由 pipe()创建管道后,一般再fork一个子进程,然后通过管道实现父子进程间的通信
Linux中的函数fork:将原有的进程拷贝一份儿,像git做了一个分支
//读取/fstab.rk30board文件内容,并填充fstab结构体,但是并没有执行挂载操作; roots.app
load_volume_table();
//挂载cache分区
has_cache = volume_for_mount_point(CACHE_ROOT) != nullptr;
//显示UI界面
device = make_device();
ui = device->GetUI();
//根据之前获取参数的标志位进行格式化及升级等操作,install_package是升级的代码
代码太多就不贴了
二、recovery 的UI
Recovery 的UI使用的是minui库,该库源码在:bootable\recovery\minui
main函数中构建了一个device,通过device的getUI函数获取到ScreenRecoveryUI。代码如下在default_device.cpp中:
Device* make_device() { return new Device(new ScreenRecoveryUI); }
这里有一定的跨度,用grep搜索代码你会发现有多个文件定义了make_device()函数:
Vr_device.cpp, default_device.app, wear_device.cpp,分别打开看看,发现逻辑一致,返回的UI不同罢了。用grep搜索default_device.app,发现是在编译时做了区分。
wear_device.cpp会编译成一个叫librecovery_ui_wear的静态库,
vr_device.cpp会编译成一个叫librecovery_ui_vr的静态库,
通过TARGET_RECOVERY_UI_LIB配置使用default_device.app还是静态库。
在main函数中获取到了ScreenRecoveryUI后调用RecoveryUI的Init()函数初始化输入并创建input线程。ScreenRecoveryUI继承了RecoveryUI
prompt_and_wait() 主菜单及处理逻辑
首先里面做了一个死循环保证菜单的循环。
//显示菜单信息
int chosen_item = get_menu_selection(nullptr, device->GetMenuItems(), false, 0, device);
函数原型:static int get_menu_selection(const char* const* headers, const char* const* items, bool menu_only, int initial_selection, Device* device) {}
headers:显示在菜单头部的内容
Items:菜单每项显示内容
initial_selection:初始选中的菜单项
//调用ScreenRecoveryUI的StartMenu绘制菜单
ui->StartMenu(headers, items, initial_selection);
最终调用到draw_screen_locked() 函数:
DrawTextLine -> gr_text() 显示字符串
DrawHighlightBar –> gr_fill() 填充矩形区域
文字绘制完毕,那图片呢, 主要有两个函数:draw_background_locked(),draw_foreground_locked()
draw_background_locked() 绘制背景:
gr_texticon()是将图片中的文字根据坐标显示出来,如下图:
draw_foreground_locked()绘制前景:
这个函数和draw_background_locked()做的事情差不多,都是绘制了一些东西(animation and progress bar)到屏幕上,最最最大的区别是draw_foreground_locked()会重绘整个屏幕,而draw_foreground_locked()不会
虽然这两个函数名字一个是背景一个是前景,但看代码逻辑的话背景是在前景里画的。
常用android minui fb显示相关函数
int gr_init(void); /* 初始化图形显示,主要是打开设备、分配内存、初始化一些参数 /
void gr_exit(void); / 注销图形显示,关闭设备并释放内存 /
int gr_fb_width(void); / 获取屏幕的宽度 /
int gr_fb_height(void); / 获取屏幕的高度 */
gr_pixel gr_fb_data(void); / 获取显示数据缓存的地址 /
void gr_flip(void); / 刷新显示内容 /
void gr_fb_blank(bool blank); / 清屏 /
void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a); / 设置字体颜色 /
void gr_fill(int x, int y, int w, int h); / 填充矩形区域,参数分别代表起始坐标、矩形区域大小 */
int gr_text(int x, int y, const char s); / 显示字符串 */
int gr_measure(const char s); / 获取字符串在默认字库中占用的像素长度 /
void gr_font_size(int x, int y); / 获取当前字库一个字符所占的长宽 /
void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy); / 填充由source指定的图片 /
unsigned int gr_get_width(gr_surface surface); / 获取图片宽度 /
unsigned int gr_get_height(gr_surface surface); / 获取图片高度 /
/ 根据图片创建显示资源数据,name为图片在mk文件指定的相对路径 /
int res_create_surface(const char name, gr_surface pSurface);
void res_free_surface(gr_surface surface); / 释放资源数据 */
尝试添加一个背景图片:
1、准备资源
准备一张png格式的图片,命名为test.png放到\bootable\recovery\res-mdpi\images目录下, 这个地方要去适配你的屏幕分辨率。
2、加载图片
在screen_ui.cpp –> Init函数中加载图片资源:
LoadBitmap(“test”, &my_test);
这点我们需要声明一个GRSurface* 类型的指针保存我们的图片,在screen_ui.h中声明一下:
GRSurface* my_test;
3、调用
找一合适位置使用SetBackground(RecoveryUI::TEST)
TEST是一个枚举icon,需要在ui.h 的 enum Icon{ }中定义
我们可以理一下逻辑:
SetBackground(RecoveryUI::TEST)
update_screen_locked()
draw_screen_locked()
如果show_text为true
draw_background_locked()
draw_foreground_locked()
我们只想显示一个背景图片,因此draw_background_locked()中的逻辑不是我们想要的,所以在该方法中自己添加绘制逻辑:
if(currentIcon == TEST){
GRSurface* frame = my_test;//图片source
int frame_width = gr_get_width(frame);//图片宽度
int frame_height = gr_get_height(frame);//图片高度
int frame_x = (ScreenWidth() - frame_width) / 2;
int frame_y = GetAnimationBaseline();
DrawSurface(frame, 0, 0, frame_width, frame_height, frame_x, frame_y);
return;
}
运行代码后如果你发现你的图片没有生效时,请确认下图片有没有加载成功:
从log中可以看出:couldn’t load bitmap " << filename << " (error " << result << ")
图片没有加载成功,我们可以调整图片的大小,图片的宽高不能超过屏幕,确定图片资源是否有误等方式检查下
Recovery 模式的log保存在 /cache/recovery目录下,我们可以pull出来:
我们的log保存在log或者last_log中,linux kernel log保存在last_kmsg中
三、编译验证:
make recoveryimage
编译成功后会更新: \out\target\product\XXXXXX\recovery.img
- 首先要确认recovery.img 在那个位置。
在/dev/block/by-name 这个目录下使用 ls -al 的命令获取到recovery的block分区。 - 将 recovery.img 放到手机里。
adb remount
adb push reocovery.img /data 这个是将img 放到了data下面 - 使用DD命令
adb shell dd if=data/recovery.img of=XXXXXX
该处的 XXXXXX 即为步骤1 获取到的分区位置 - reboot.
四、关于input
回到RecoveryUI 的 Init函数:
注册监听InputEvent,如果touch_screen_allowed_ 为true ,表示允许监听触摸输入,默认为false
通过解析输入设备的事件,不同类型做不同的处理
Linux中输入设备的事件类型有
EV_SYN 0x00 同步事件
EV_KEY 0x01 按键事件,如KEY_VOLUMEDOWN
EV_REL 0x02 相对坐标, 如shubiao上报的坐标
EV_ABS 0x03 绝对坐标,如触摸屏上报的坐标
EV_MSC 0x04 其它
EV_LED 0x11 LED
EV_SND 0x12 声音
EV_REP 0x14 Repeat
EV_FF 0x15 力反馈
~~~~~~~~~~~~~~~~~~~~~~~~
EV_PWR 电源
EV_FF_STATUS 状态