Article directory
1.Recovery process
2.Recovery UI initialization process analysis
I've talked about Recovery's process before. In the customization process, then, changes to Recovery UI may be involved. Here are some of the things about Recovery UI.
Firstly, the main function of recovery is analyzed.
int main(int argc, char **argv){ //Partial Ellipsis of Intermediate Processing Commands 1.Here's how to start creating UI An object Device* device = make_device(); ui = device->GetUI(); gCurrentUI = ui; ui->SetLocale(locale); //Initialization begins after the instance is complete ui->Init();//Open thread and listen for buttons ... if (update_package != NULL) { status = install_package(update_package, &should_wipe_cache, TEMPORARY_INSTALL_FILE, mount_required); ... if (status != INSTALL_SUCCESS) { ui->Print("Installation aborted.\n"); // If this is an eng or userdebug build, then automatically // turn the text display on if the script fails so the error // message is visible. if (is_ro_debuggable()) {//If it's debug, the upgraded log will be displayed ui->ShowText(true); } } } }
Below will be divided into two parts to analyze:
1.UI Part Analysis
Let's first look at what's going on in make_device.
Device* make_device() { return new Device(new ScreenRecoveryUI); }
The implementation is simple. It's a new Screen Recovery UI object.
Take a look at how Screen Recovery UI is implemented:
bootable/recovery/screen_ui.h class ScreenRecoveryUI : public RecoveryUI {}
Screen Recovery UI inherits from Recovery UI. Screen Recovery UI implements various interfaces needed for drawing.
Let's take a look at the interfaces mentioned above.
void ScreenRecoveryUI::Init() { gr_init();//Initialize display device, load font gr_font_size(&char_width, &char_height); text_rows_ = gr_fb_height() / char_height; text_cols_ = gr_fb_width() / char_width; text_ = Alloc2d(text_rows_, text_cols_ + 1); file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1); menu_ = Alloc2d(text_rows_, text_cols_ + 1); text_col_ = text_row_ = 0; text_top_ = 1; //Load Image Resources 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]); backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR]; LoadBitmap("progress_empty", &progressBarEmpty); LoadBitmap("progress_fill", &progressBarFill); LoadBitmap("stage_empty", &stageMarkerEmpty); LoadBitmap("stage_fill", &stageMarkerFill); 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); //Call the initialization function of the parent class. RecoveryUI::Init(); }
3. Font loading function gr_init
int gr_init(void) { gr_init_font();//Load font //Open the display module if (!gr_draw) { gr_backend = open_fbdev(); gr_draw = gr_backend->init(gr_backend); if (gr_draw == NULL) { return -1; } } } static void gr_init_font(void) { gr_font = reinterpret_cast<GRFont*>(calloc(sizeof(*gr_font), 1)); int res = res_create_alpha_surface("font", &(gr_font->texture));//Read data from font pictures ··· }
4. Let's take a look at the image loading function LoadBitmap
void ScreenRecoveryUI::LoadBitmap(const char* filename, GRSurface** surface) { int result = res_create_display_surface(filename, surface); if (result < 0) { LOGE("missing bitmap %s\n(Code %d)\n", filename, result); } } int res_create_display_surface(const char* name, GRSurface** pSurface) { GRSurface* surface = NULL; //Open the picture and get the detailed parameters of the picture result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels); if (result < 0) return result; //Initialize a GRSurface based on the parameters of the image to save the image data surface = init_display_surface(width, height); if (surface == NULL) { result = -8; goto exit; } //The data in the picture is read out by line and saved in surface. for (y = 0; y < height; ++y) { png_read_row(png_ptr, p_row, NULL); transform_rgb_to_draw(p_row, surface->data + y * surface->row_bytes, channels, width); } free(p_row); *pSurface = surface; exit: png_destroy_read_struct(&png_ptr, &info_ptr, NULL); if (result < 0 && surface != NULL) free(surface); return result; }
The above is how to read the picture and save it. The text in recovery is also read in this way, and it is no longer written here. It should be noted that the text in recovery is also intercepted from the picture and then displayed.
5.Recovery keyboard event monitoring
void RecoveryUI::Init() { ev_init(InputCallback, this);//Open / dev/input, read event s from it, and register the key handler InputCallback pthread_create(&input_thread_, nullptr, InputThreadLoop, nullptr);//Open a thread to listen for key event s } static void* InputThreadLoop(void*) { while (true) { if (!ev_wait(-1)) { ev_dispatch();//Distribution event calls InputCallback } } return nullptr; }
6. Display of pictures in Recovery
The above is about reading pictures, and the following is about recovery by displaying the progress bar in stages.
void ScreenRecoveryUI::draw_progress_locked() { ... if (progressBarType != EMPTY) { //Width and Height of Reading Pictures int em_width = gr_get_width(progressBarEmpty); int em_height = gr_get_height(progressBarEmpty); int fl_width = gr_get_width(progressBarFill); int fl_height = gr_get_height(progressBarFill); //Get the width of the lcd int dx = gr_fb_width(); int dy = (3*gr_fb_height() - 2*em_height)/4; if (progressBarType == DETERMINATE) { float p = progressScopeStart + progress * progressScopeSize; int pos = (int) (p * em_height);//Calculate the display position of the progress bar if (rtl_locale){//right to left if (pos >= 0) {//Draw a color progress bar gr_blit(progressBarFill, 0, fl_height, fl_width, dy/2 + pos, dx/2, dy/2); } if (pos < em_height-1){//Draw empty progress bar gr_blit(progressBarEmpty, 0, 0, em_width, dy/2 + em_height - pos, dx/2, dy/2 + fl_height-pos); } }else {//left to right if (pos >= 0) { gr_blit(progressBarFill, 0, 0, fl_width, pos, dx/2, dy/2); } // this maybe incorrect. but if use em_height, maybe not fullfill if (pos < fl_height-1){ gr_blit(progressBarEmpty, 0, pos, em_width, fl_height - pos, dx/2, dy/2 + pos); } } } } }
The main thing here is gr_blit. Let's take a look at this function.
/* *source:Picture data drawn *sx/sy:This is a pair of coordinates, indicating from which point to start drawing, this is a relative dx, dy data. *w/h: source Length and width *dx/dy: From which point do you begin to draw? */ void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) { if (source == NULL) return; if (gr_draw->pixel_bytes != source->pixel_bytes) { printf("gr_blit: source has wrong format\n"); return; } dx += overscan_offset_x; dy += overscan_offset_y; if (outside(dx, dy) || outside(dx+w-1, dy+h-1)) return; unsigned char* src_p = source->data + sy*source->row_bytes + sx*source->pixel_bytes;//Point to the data to be drawn unsigned char* dst_p = gr_draw->data + dy*gr_draw->row_bytes + dx*gr_draw->pixel_bytes;//Point to where you're going to draw. int i; //Copy the data over for (i = 0; i < h; ++i) { memcpy(dst_p, src_p, w * source->pixel_bytes); src_p += source->row_bytes; dst_p += gr_draw->row_bytes; } }
So far, data has only been put into the display buffer, and can not be displayed. To display, you need to call the function gr_flip(). This function is to display the data in the buffer to the screen.
7.Recovery UI updates
Now let's talk about how Recovery UI gets updated messages. The process of Recovery upgrade has been described before, and I won't go into details here. Direct will be the key part.
static int try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache) { ··· //Get the upgraded bin file from the upgrade package, META-INF/com/google/android/update-binary const ZipEntry* binary_entry = mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME); const char* binary = "/tmp/update_binary"; unlink(binary); int fd = creat(binary, 0755); bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd);Read into the handle int pipefd[2]; pipe(pipefd); const char** args = (const char**)malloc(sizeof(char*) * 5); args[0] = binary; args[1] = EXPAND(RECOVERY_API_VERSION); // defined in Android.mk char* temp = (char*)malloc(10); sprintf(temp, "%d", pipefd[1]); args[2] = temp; args[3] = (char*)path; args[4] = NULL; //New thread with open body pid_t pid = fork(); if (pid == 0) { umask(022); close(pipefd[0]); execv(binary, (char* const*)args);//Running update-binary in a subprocess fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno)); _exit(-1); } close(pipefd[1]); *wipe_cache = false; char buffer[1024]; FILE* from_child = fdopen(pipefd[0], "r");//Read messages from child processes in parent processes while (fgets(buffer, sizeof(buffer), from_child) != NULL) { char* command = strtok(buffer, " \n"); if (command == NULL) { continue; } else if (strcmp(command, "progress") == 0) { char* fraction_s = strtok(NULL, " \n"); char* seconds_s = strtok(NULL, " \n"); float fraction = strtof(fraction_s, NULL); int seconds = strtol(seconds_s, NULL, 10); ui->ShowProgress(fraction * (1-VERIFICATION_PROGRESS_FRACTION), seconds); } else if (strcmp(command, "set_progress") == 0) { char* fraction_s = strtok(NULL, " \n"); float fraction = strtof(fraction_s, NULL); ui->SetProgress(fraction); } else if (strcmp(command, "ui_print") == 0) { char* str = strtok(NULL, "\n"); if (str) { ui->Print("%s", str); } else { ui->Print("\n"); } fflush(stdout); } else if (strcmp(command, "wipe_cache") == 0) { *wipe_cache = true; } else if (strcmp(command, "clear_display") == 0) { ui->SetBackground(RecoveryUI::NONE); } else if (strcmp(command, "enable_reboot") == 0) { // packages can explicitly request that they want the user // to be able to reboot during installation (useful for // debugging packages that don't exit). ui->SetEnableReboot(true); } else { LOGE("unknown command [%s]\n", command); } } fclose(from_child); int status; waitpid(pid, &status, 0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status)); return INSTALL_ERROR; } return INSTALL_SUCCESS; }
Here's a brief posting of the scripts in the ota upgrade package
ui_print("Target: ASKEY/CDR6013/msm8952_64:6.0.1/CDR6013-DS-180307-1-40-4,release-keys"); show_progress(0.750000, 0);
Let's see how the ui_print function is implemented.
RegisterFunction("ui_print", UIPrintFn); Value* UIPrintFn(const char* name, State* state, int argc, Expr* argv[]) { char** args = ReadVarArgs(state, argc, argv); //Middle uiPrint(state, buffer); return StringValue(buffer); } void uiPrint(State* state, char* buffer) { char* line = strtok(buffer, "\n"); UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); while (line) { fprintf(ui->cmd_pipe, "ui_print %s\n", line); line = strtok(NULL, "\n"); } fprintf(ui->cmd_pipe, "ui_print\n"); }
This way, the parent process receives the ui_print command and processes the display on the UI.
Recovery's UI is over here.
Font pictures: