Android recovery UI Implementation Analysis
Why does Android recovery mode work?
There are already countless answers to this question on baidu. If you don't understand it, you need to study it first. From a purely technical perspective, recovery and android are essentially two independent rootfs. The meaning of recovery rootfs is to serve the android rootfs and thus be interpreted as part of the Android system. As a simple rootfs, recovery provides several very limited functions, including only a few simple libraries. The UI display adopts the form of directly refreshing framebuffer, as a code farmer at the android framework and app layer, he is a stranger to this form and takes some time to sort it out. First, check the UI-related statements in the main Function Code of reocvery.
main(int argc, char **argv) { ...... Device* device = make_device(); ui = device->GetUI(); gCurrentUI = ui; ui->Init(); ui->SetLocale(locale); ui->SetBackground(RecoveryUI::NONE); if (show_text) ui->ShowText(true); ...... if (status != INSTALL_SUCCESS || ui->IsTextVisible()) { prompt_and_wait(device, status); } ......}
1. First, a new Device class object is created. The Device class encapsulates some operations, including UI operations. 2. Call GetUI () of the Device class to return a defaui UI object, there are three UI classes involved in the recovery process. The Inheritance relationships are among the three classes: DefaultUI, ScreenRecoveryUI, RecoveryUI3, and Init () that calls the DefaultUI class. The defaui UI class does not have the Init () method, therefore, the Init () 4 of the ScreenRecoveryUI parent class will be called. Similarly, the SetLocale () of the ScreenRecoveryUI class will be called to identify several special areas. 5. Likewise, call SetBackground () of the ScreenRecoveryUI class to set the background image of the initial state. 6. display the recovery main interface, that is, a selection menu.
void ScreenRecoveryUI::Init(){ gr_init(); gr_font_size(&char_width, &char_height); text_col = text_row = 0; text_rows = gr_fb_height() / char_height; if (text_rows > kMaxRows) text_rows = kMaxRows; text_top = 1; text_cols = gr_fb_width() / char_width; if (text_cols > kMaxCols - 1) text_cols = kMaxCols - 1; LoadBitmap("icon_installing", &backgroundIcon[INSTALLING_UPDATE]); backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE]; LoadBitmap("icon_error", &backgroundIcon[ERROR]); backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR]; LoadBitmap("progress_empty", &progressBarEmpty); LoadBitmap("progress_fill", &progressBarFill); LoadLocalizedBitmap("installing_text", &backgroundText[INSTALLING_UPDATE]); LoadLocalizedBitmap("erasing_text", &backgroundText[ERASING]); LoadLocalizedBitmap("no_command_text", &backgroundText[NO_COMMAND]); LoadLocalizedBitmap("error_text", &backgroundText[ERROR]); int i; progressBarIndeterminate = (gr_surface*)malloc(indeterminate_frames * sizeof(gr_surface)); for (i = 0; i < indeterminate_frames; ++i) { char filename[40]; // "indeterminate01.png", "indeterminate02.png", ... sprintf(filename, "indeterminate%02d", i+1); LoadBitmap(filename, progressBarIndeterminate+i); } if (installing_frames > 0) { installationOverlay = (gr_surface*)malloc(installing_frames * sizeof(gr_surface)); for (i = 0; i < installing_frames; ++i) { char filename[40]; // "icon_installing_overlay01.png", // "icon_installing_overlay02.png", ... sprintf(filename, "icon_installing_overlay%02d", i+1); LoadBitmap(filename, installationOverlay+i); } } else { installationOverlay = NULL; } pthread_create(&progress_t, NULL, progress_thread, NULL); RecoveryUI::Init();}
1. gr_init () initializes the Graphics Device and allocates the Pixelflinger library Rendering Memory.
2. gr_font_size () assigns the surface length and width of the font to char_width and char_height.
3. LoadBitmap () generates a png surface. Each png Image corresponds to a surface, and all surfaces are stored in an array. 4. LoadLocalizedBitmap () extract text information from the image where the area text is located according to the current locale to generate the corresponding surface. Therefore, the surface is also stored in an array. 6. pthread_create (& progress_t, NULL, progress_thread, NULL) create a thread. The task of this thread is an endless loop. It constantly checks currentIcon and progressBarType to determine whether to update the progress bar. 7. Call Init () of RecoveryUI to initialize input event processing.
void ScreenRecoveryUI::SetLocale(const char* locale) { if (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); }}
SetLocale of the ScreenRecoveryUI class. Based on locale, this function determines whether the font used belongs to the Arabic family. The writing habit of Arabic is from right to left. If it is Arabic, set a flag, the text or progress bar is displayed from right to left Based on the logo. The logic of SetLocale parameter locale assignment is as follows: read from the command file first, and set the locale command in the command file, for example "--
Locale= Zh_CN ". If locale is not input, locale will be read from/cache/recovery/last_locale during initialization. If the file does not exist, locale will not be assigned a value, english is used by default.
void ScreenRecoveryUI::SetBackground(Icon icon){ pthread_mutex_lock(&updateMutex); // Adjust the offset to account for the positioning of the // base image on the screen. if (backgroundIcon[icon] != NULL) { gr_surface bg = backgroundIcon[icon]; gr_surface text = backgroundText[icon]; overlay_offset_x = install_overlay_offset_x + (gr_fb_width() - gr_get_width(bg)) / 2; overlay_offset_y = install_overlay_offset_y + (gr_fb_height() - (gr_get_height(bg) + gr_get_height(text) + 40)) / 2; } currentIcon = icon; update_screen_locked(); pthread_mutex_unlock(&updateMutex);}
The SetBackground function is concise. The key part is update_screen_locked. Next we will focus on the analysis. Update_screen_locked and update_progress_locked are key functions of the UI part of the recovery ing. update_screen_locked is used to update the background and update_progress_locked is used to update the progress bar because the displayed screen is constantly updated, so these two functions are called repeatedly in different places.
void ScreenRecoveryUI::update_screen_locked(){ draw_screen_locked(); gr_flip();}
Update_screen_locked contains two operations: Update screen and buffer before and after switching.
void ScreenRecoveryUI::draw_screen_locked(){ draw_background_locked(currentIcon); draw_progress_locked(); if (show_text) { SetColor(TEXT_FILL); gr_fill(0, 0, gr_fb_width(), gr_fb_height()); int y = 0; int i = 0; if (show_menu) { SetColor(HEADER); for (; i < menu_top + menu_items; ++i) { if (i == menu_top) SetColor(MENU); if (i == menu_top + menu_sel) { // draw the highlight bar SetColor(MENU_SEL_BG); gr_fill(0, y-2, gr_fb_width(), y+char_height+2); // white text of selected item SetColor(MENU_SEL_FG); if (menu[i][0]) gr_text(4, y, menu[i], 1); SetColor(MENU); } else { if (menu[i][0]) gr_text(4, y, menu[i], i < menu_top); } y += char_height+4; } SetColor(MENU); y += 4; gr_fill(0, y, gr_fb_width(), y+2); y += 4; ++i; } SetColor(LOG); // display from the bottom up, until we hit the top of the // screen, the bottom of the menu, or we've displayed the // entire text buffer. int ty; int row = (text_top+text_rows-1) % text_rows; for (int ty = gr_fb_height() - char_height, count = 0; ty > y+2 && count < text_rows; ty -= char_height, ++count) { gr_text(4, ty, text[row], 0); --row; if (row < 0) row = text_rows-1; } }}
Several functions starting with gr _ appear in the implementation code of the draw_background_locked function. functions starting with gr _ come from the minui library, and the minui library code is in the minui directory under the recovery source code, the interfaces provided by minui provide graphical representations and fixed-size text display.
Gr_color (unsigned char r, unsigned char g, unsigned char B, unsigned char a);/* set the font color */gr_fill (int x, int y, int w, int h);/* fill in the rectangular area. The parameters represent the starting coordinate and the size of the rectangular area */gr_blit (gr_surface source, int sx, int sy, int w, int h, int dx, int dy);/* fill in the image specified by source */
The draw_background_locked function first fills the Rendering buffer in black, then calculates the background surface length and width, and the text surface length and width. Then, it calculates the coordinates of the background surface and the text surface Display Based on the fb length and width, with the length and coordinates, you can call the Pixelflinger interface to render the rendering buffer.
void ScreenRecoveryUI::draw_progress_locked(){ if (currentIcon == ERROR) return; if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) { draw_install_overlay_locked(installingFrame); } if (progressBarType != EMPTY) { int iconHeight = gr_get_height(backgroundIcon[INSTALLING_UPDATE]); int width = gr_get_width(progressBarEmpty); int height = gr_get_height(progressBarEmpty); int dx = (gr_fb_width() - width)/2; int dy = (3*gr_fb_height() + iconHeight - 2*height)/4; // Erase behind the progress bar (in case this was a progress-only update) gr_color(0, 0, 0, 255); gr_fill(dx, dy, width, height); if (progressBarType == DETERMINATE) { float p = progressScopeStart + progress * progressScopeSize; int pos = (int) (p * width); if (rtl_locale) { // Fill the progress bar from right to left. if (pos > 0) { gr_blit(progressBarFill, width-pos, 0, pos, height, dx+width-pos, dy); } if (pos < width-1) { gr_blit(progressBarEmpty, 0, 0, width-pos, height, dx, dy); } } else { // Fill the progress bar from left to right. if (pos > 0) { gr_blit(progressBarFill, 0, 0, pos, height, dx, dy); } if (pos < width-1) { gr_blit(progressBarEmpty, pos, 0, width-pos, height, dx+pos, dy); } } } if (progressBarType == INDETERMINATE) { static int frame = 0; gr_blit(progressBarIndeterminate[frame], 0, 0, width, height, dx, dy); // in RTL locales, we run the animation backwards, which // makes the spinner spin the other way. if (rtl_locale) { frame = (frame + indeterminate_frames - 1) % indeterminate_frames; } else { frame = (frame + 1) % indeterminate_frames; } } }}
The principle of the draw_progress_locked function is similar to that of the update_screen_locked function. In the end, the surface of the progress bar is output to the rendering buffer. The screen of each scenario in recovery is composed of overlapping backgrounds, texts, and progress bars, the difference is the coordinates of the surface and surface used. The UI code in the recovery main function has basically been analyzed. The display of the Last main Menu is to display text images through the interfaces described above, so I will not talk about it more. In general, the UI display of recovery is not very difficult. The application layer calls the minui library to depict images and display texts of a fixed size. The minui library calls the Pixelflinger library for rendering. Attach the description of some minui interfaces for your reference.
Int gr_init (void);/* initialize the graphical display, mainly to open the device, allocate memory, initialize some parameters */void gr_exit (void);/* cancel the graphical display, disable the device and release the memory */int gr_fb_width (void);/* obtain the screen width */int gr_fb_height (void ); /* obtain the screen height */gr_pixel * gr_fb_data (void);/* obtain the cache address of the displayed data */void gr_flip (void ); /* refresh the display content */void gr_fb_blank (bool blank);/* clear screen */void gr_color (unsigned char r, unsigned char g, unsigned char B, unsigned char ); /* set the font color */void gr_fill (int x, int y, int w, int h);/* fill the rectangular area, the parameters represent the starting coordinate and the size of the rectangle */int gr_text (int x, int y, const char * s ); /* display string */int gr_measure (const char * s);/* obtain the pixel length occupied by the string in the default font */void gr_font_size (int * x, int * y);/* Get the length and width of a character in the current font */void gr_b133 (gr_surface source, int sx, int sy, int w, int h, int dx, int dy);/* fill in the image specified by source */unsigned int gr_get_width (gr_surface surface);/* Get the image width */unsigned int gr_get_height (gr_surface surface ); /* obtain the image height * // * Create display resource data based on the image. The name is the relative path specified by the image in the mk file */int res_create_surface (const char * name, gr_surface * pSurface); void res_free_surface (gr_surface surface);/* release resource data */