Recovery啟動流程系列文章把recvoery目錄下文件分成小塊講解,最後再以一條主線貫穿所有的內容。這篇文章主要講解Recovery UI的相關內容。 我們知道,當我們通過按鍵或者應用進入recovery模式,實質是kernel後載入recovery.img,kernel起來後執行的第一個進程 ...
Recovery啟動流程系列文章把recvoery目錄下文件分成小塊講解,最後再以一條主線貫穿所有的內容。這篇文章主要講解Recovery-UI的相關內容。
我們知道,當我們通過按鍵或者應用進入recovery模式,實質是kernel後載入recovery.img,kernel起來後執行的第一個進程就是init,此進程會讀入init.rc啟動相應的服務。在recovery模式中,啟動的服務是執行recovery可執行文件,此文件是bootable/recovery/recovery.cpp文件生成,我們就從recovery.cpp文件開始分析。
bootable/recovery/recovery.cpp
int
main(int argc, char **argv) {
....
Device* device = make_device();
ui = device->GetUI();
gCurrentUI = ui;
ui->SetLocale(locale);
ui->Init();
ui->SetBackground(RecoveryUI::NONE);
if (show_text) ui->ShowText(true);
....
if (status != INSTALL_SUCCESS || ui->IsTextVisible()) {
prompt_and_wait(device, status);
}
....
}
- 首先新建了一個Device類的對象, Device類封裝了一些操作,包括UI的操作
調用Device類的GetUI()返回一個RecoveryUI對象
調用ui->SetLocale(locale)設置語言,調用SetBackground方法設置背景圖片
調用Init()進行初始化。
這裡的Init從代碼上看應該是ui.cpp文件中RecoveryUI類的Init()方法,是ScreenRecoveryUI,這裡我是按照ScreenRecoveryUI::Init追的代碼。其中RecoveryUI是ScreenRecoveryUI的父類。
顯示recovery的主界面,即一個選擇菜單
實現頭部顯示和列表項device.h
static const char* MENU_ITEMS[] = {
"Reboot system now",
"Apply update from ADB",
"Wipe data/factory reset",
"Wipe cache partition",
"Reboot to bootloader",
"Power off",
"View recovery logs",
"Apply update from sdcard",
"Apply update from usbotg",
"Security unlock",
"Download secure info",
"Download hwc info",
"Apply OTAconfig update from sdcard",
"Apply OTAconfig update from usbotg",
"Apply OTAconfig update from usbotg path",
NULL,
};
static const Device::BuiltinAction MENU_ACTIONS[] = {
Device::REBOOT,
Device::APPLY_ADB_SIDELOAD,
Device::WIPE_DATA,
Device::WIPE_CACHE,
Device::REBOOT_BOOTLOADER,
Device::SHUTDOWN,
Device::VIEW_RECOVERY_LOGS,
Device::APPLY_SDCARD,
Device::APPLY_USB,
Device::SECURE_UNLOCK,
Device::DOWNLOAD_SECURE_INFO,
Device::DOWNLOAD_HWC_INFO,
Device::APPLY_OTACONFIG_EXT,
Device::APPLY_OTACONFIG_USB,
Device::APPLY_OTACONFIG_USB_PATH,
//Device::MOUNT_SYSTEM,
};
void ScreenRecoveryUI::Init() {
gr_init(); //初始化圖形設備,分配Pixelflinger庫渲染的記憶體
gr_font_size(&char_width, &char_height);
text_rows_ = gr_fb_height() / char_height;
text_cols_ = gr_fb_width() / char_width;
#ifdef SUPPORT_UTF8_MULTILINGUAL
int ml_cols_ = 6 * text_cols_; //max is 6 char for 1 utf8 character.
text_ = Alloc2d(text_rows_, ml_cols_ + 1);
file_viewer_text_ = Alloc2d(text_rows_, ml_cols_ + 1);
menu_ = Alloc2d(text_rows_, ml_cols_ + 1);
menu_headers_wrap = Alloc2d(text_rows_, ml_cols_ + 1);
#else
text_ = Alloc2d(text_rows_, text_cols_ + 1);
file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1);
menu_ = Alloc2d(text_rows_, text_cols_ + 1);
#endif
text_col_ = text_row_ = 0;
text_top_ = 1;
backgroundIcon[NONE] = nullptr;
LoadBitmapArray("icon_installing", &installing_frames, &installation);
backgroundIcon[INSTALLING_UPDATE] = installing_frames ? installation[0] : nullptr;
backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE];
LoadBitmap("icon_error", &backgroundIcon[ERROR]); //LoadBitmap() 將png生成surface, 每個png圖片對應一個surface, 所有surface存放在一個數組中
backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR];
LoadBitmap("icon_recovery", &backgroundIcon[RECOVERY]);
LoadBitmap("progress_empty", &progressBarEmpty);
LoadBitmap("progress_fill", &progressBarFill);
LoadBitmap("stage_empty", &stageMarkerEmpty);
LoadBitmap("stage_fill", &stageMarkerFill);
/* add for AT&T recovery update install UI begin */
#ifdef TARGET_ATT_RECOVERY_UI
LoadBitmap("icon_attinstalling", &backgroundIcon[ATT_INSTALLING_UPDATE]);
LoadBitmap("progress_attempty", &progressBarEmpty_ATT);
LoadBitmap("progress_attfill", &progressBarFill_ATT);
LoadLocalizedBitmap("installing_atttext", &backgroundText[ATT_INSTALLING_UPDATE]); //LoadLocalizedBitmap() 將區域文字所在的圖片中的text信息根據當前的locale提取出來,生成對應的surface, 所有
surface也存放在一個數組中
#endif
/* add for AT&T recovery update install UI end */
LoadLocalizedBitmap("installing_text", &backgroundText[INSTALLING_UPDATE]);
LoadLocalizedBitmap("erasing_text", &backgroundText[ERASING]);
LoadLocalizedBitmap("no_command_text", &backgroundText[NO_COMMAND]);
LoadLocalizedBitmap("error_text", &backgroundText[ERROR]);
pthread_create(&progress_thread_, nullptr, ProgressThreadStartRoutine, this); //創建一個線程,在該迴圈中不停地檢測currentIcon以及progressBarType來決定是不是要更新進度條。
RecoveryUI::Init(); //初始化RecoveryUI類
}
bootable/recovery/minui/ui.cpp
void RecoveryUI::Init() {
ev_init(InputCallback, this);
ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));
pthread_create(&input_thread_, nullptr, InputThreadLoop, nullptr);
}
通過RecoveryUI::Init(); 調用events.cpp文件,界面和按鍵/觸摸聯繫在一起了,後面會用單獨的文章介紹recovery按鍵和觸屏的相關內容。
下麵介紹幾個常用的函數
void ScreenRecoveryUI::SetLocale(const char* new_locale) {
if (new_locale) {
this->locale = new_locale;
char* lang = strdup(locale);
for (char* p = lang; *p; ++p) {
if (*p == '_') {
*p = '\0';
break;
}
}
// A bit cheesy: keep an explicit list of supported languages
// that are RTL.
if (strcmp(lang, "ar") == 0 || // Arabic
strcmp(lang, "fa") == 0 || // Persian (Farsi)
strcmp(lang, "he") == 0 || // Hebrew (new language code)
strcmp(lang, "iw") == 0 || // Hebrew (old language code)
strcmp(lang, "ur") == 0) { // Urdu
rtl_locale = true;
}
free(lang);
} else {
new_locale = nullptr;
}
}
從recovery.cpp main()中可知,進入recovery後會分析/cache/recovery/command文件,根據內容來設定顯示的文字語言
SetLocale函數根據locale判斷所用的字體是否屬於阿拉伯語系,阿拉伯語的書寫習慣是從右到左,如果是阿拉伯語系的話,就設置一個標誌,後面根據這個標誌決定從右到左顯示文字或進度條。關於顯示文字的語言通過代碼即可查看,這裡只簡單的列出語言設置的幾條主線,不貼出具體的代碼(太多了)。
g_ml_str[] (mi_string.h)-> ml_string_fetch() (multilingual.c)
ml_set_language (multilingual.c) -> ml_select() (recovery.cpp) -> prompt_and_wait() (recovery.cpp) -> main() (recovery.cpp)
SetBackground函數比較簡潔,關鍵部分在update_screen_locked。
update_screen_locked 和update_progress_locked是recovery的UI部分的關鍵函數,update_screen_locked用來更新背 景, update_progress_locked用來更新進度條,因為顯示的畫面會一直在更新,所以這兩個函數會在不同的地方被反覆調用
void ScreenRecoveryUI::SetBackground(Icon icon) {
pthread_mutex_lock(&updateMutex);
currentIcon = icon;
update_screen_locked();
pthread_mutex_unlock(&updateMutex);
}
void ScreenRecoveryUI::update_screen_locked() {
draw_screen_locked();
gr_flip();
}
void ScreenRecoveryUI::draw_screen_locked() {
if (!show_text) {
draw_background_locked(currentIcon); //************ 有一個bug因為此行沒有,導致SetBackground函數無法更換背景圖片
draw_progress_locked();
} else {
gr_color(0, 0, 0, 255);
gr_clear();
draw_background_locked(currentIcon); //************
.........
}
}