diff options
-rw-r--r-- | applypatch/applypatch.c | 18 | ||||
-rw-r--r-- | bootloader.h | 15 | ||||
-rw-r--r-- | recovery.cpp | 18 | ||||
-rw-r--r-- | res/images/stage_empty.png | bin | 0 -> 322 bytes | |||
-rw-r--r-- | res/images/stage_fill.png | bin | 0 -> 258 bytes | |||
-rw-r--r-- | screen_ui.cpp | 31 | ||||
-rw-r--r-- | screen_ui.h | 10 | ||||
-rw-r--r-- | ui.h | 2 | ||||
-rw-r--r-- | updater/install.c | 115 |
9 files changed, 180 insertions, 29 deletions
diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c index 9d32ee904..9e631dd4f 100644 --- a/applypatch/applypatch.c +++ b/applypatch/applypatch.c @@ -424,20 +424,18 @@ int WriteToPartition(unsigned char* data, size_t len, { size_t start = 0; int success = 0; - int fd = open(partition, O_RDWR | O_SYNC); + int fd = open(partition, O_RDWR); if (fd < 0) { printf("failed to open %s: %s\n", partition, strerror(errno)); return -1; } int attempt; - for (attempt = 0; attempt < 10; ++attempt) { - size_t next_sync = start + (1<<20); - printf("raw O_SYNC write %s attempt %d start at %d\n", partition, attempt+1, start); + for (attempt = 0; attempt < 2; ++attempt) { lseek(fd, start, SEEK_SET); while (start < len) { size_t to_write = len - start; - if (to_write > 4096) to_write = 4096; + if (to_write > 1<<20) to_write = 1<<20; ssize_t written = write(fd, data+start, to_write); if (written < 0) { @@ -450,10 +448,6 @@ int WriteToPartition(unsigned char* data, size_t len, } } start += written; - if (start >= next_sync) { - fsync(fd); - next_sync = start + (1<<20); - } } fsync(fd); @@ -506,8 +500,6 @@ int WriteToPartition(unsigned char* data, size_t len, success = true; break; } - - sleep(2); } if (!success) { @@ -519,11 +511,7 @@ int WriteToPartition(unsigned char* data, size_t len, printf("error closing %s (%s)\n", partition, strerror(errno)); return -1; } - // hack: sync and sleep after closing in hopes of getting - // the data actually onto flash. - printf("sleeping after close\n"); sync(); - sleep(5); break; } } diff --git a/bootloader.h b/bootloader.h index 712aa1a2d..c2895dd91 100644 --- a/bootloader.h +++ b/bootloader.h @@ -38,11 +38,24 @@ extern "C" { * The recovery field is only written by linux and used * for the system to send a message to recovery or the * other way around. + * + * The stage field is written by packages which restart themselves + * multiple times, so that the UI can reflect which invocation of the + * package it is. If the value is of the format "#/#" (eg, "1/3"), + * the UI will add a simple indicator of that status. */ struct bootloader_message { char command[32]; char status[32]; - char recovery[1024]; + char recovery[768]; + + // The 'recovery' field used to be 1024 bytes. It has only ever + // been used to store the recovery command line, so 768 bytes + // should be plenty. We carve off the last 256 bytes to store the + // stage string (for multistage packages) and possible future + // expansion. + char stage[32]; + char reserved[224]; }; /* Read and write the bootloader command from the "misc" partition. diff --git a/recovery.cpp b/recovery.cpp index 448f315b0..57e19c091 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -56,6 +56,7 @@ static const struct option OPTIONS[] = { { "show_text", no_argument, NULL, 't' }, { "just_exit", no_argument, NULL, 'x' }, { "locale", required_argument, NULL, 'l' }, + { "stages", required_argument, NULL, 'g' }, { NULL, 0, NULL, 0 }, }; @@ -76,6 +77,7 @@ static const char *SIDELOAD_TEMP_DIR = "/tmp/sideload"; RecoveryUI* ui = NULL; char* locale = NULL; char recovery_version[PROPERTY_VALUE_MAX+1]; +char* stage = NULL; /* * The recovery tool communicates with the main system through /cache files. @@ -172,6 +174,7 @@ get_args(int *argc, char ***argv) { struct bootloader_message boot; memset(&boot, 0, sizeof(boot)); get_bootloader_message(&boot); // this may fail, leaving a zeroed structure + stage = strndup(boot.stage, sizeof(boot.stage)); if (boot.command[0] != 0 && boot.command[0] != 255) { LOGI("Boot command: %.*s\n", (int)sizeof(boot.command), boot.command); @@ -959,6 +962,14 @@ main(int argc, char **argv) { case 't': show_text = 1; break; case 'x': just_exit = true; break; case 'l': locale = optarg; break; + case 'g': { + if (stage == NULL || *stage == '\0') { + char buffer[20] = "1/"; + strncat(buffer, optarg, sizeof(buffer)-3); + stage = strdup(buffer); + } + break; + } case '?': LOGE("Invalid command argument\n"); continue; @@ -969,12 +980,19 @@ main(int argc, char **argv) { load_locale_from_cache(); } printf("locale is [%s]\n", locale); + printf("stage is [%s]\n", stage, stage); Device* device = make_device(); ui = device->GetUI(); gCurrentUI = ui; ui->Init(); + + int st_cur, st_max; + if (stage != NULL && sscanf(stage, "%d/%d", &st_cur, &st_max) == 2) { + ui->SetStage(st_cur, st_max); + } + ui->SetLocale(locale); ui->SetBackground(RecoveryUI::NONE); if (show_text) ui->ShowText(true); diff --git a/res/images/stage_empty.png b/res/images/stage_empty.png Binary files differnew file mode 100644 index 000000000..251ec1969 --- /dev/null +++ b/res/images/stage_empty.png diff --git a/res/images/stage_fill.png b/res/images/stage_fill.png Binary files differnew file mode 100644 index 000000000..1ab79e862 --- /dev/null +++ b/res/images/stage_fill.png diff --git a/screen_ui.cpp b/screen_ui.cpp index 589c935dc..86429c9c1 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -70,7 +70,9 @@ ScreenRecoveryUI::ScreenRecoveryUI() : menu_items(0), menu_sel(0), animation_fps(20), - installing_frames(-1) { + installing_frames(-1), + stage(-1), + max_stage(-1) { for (int i = 0; i < 5; i++) backgroundIcon[i] = NULL; @@ -97,15 +99,29 @@ void ScreenRecoveryUI::draw_background_locked(Icon icon) int iconHeight = gr_get_height(surface); int textWidth = gr_get_width(text_surface); int textHeight = gr_get_height(text_surface); + int stageHeight = gr_get_height(stageMarkerEmpty); + + int sh = (max_stage >= 0) ? stageHeight : 0; iconX = (gr_fb_width() - iconWidth) / 2; - iconY = (gr_fb_height() - (iconHeight+textHeight+40)) / 2; + iconY = (gr_fb_height() - (iconHeight+textHeight+40+sh)) / 2; int textX = (gr_fb_width() - textWidth) / 2; - int textY = ((gr_fb_height() - (iconHeight+textHeight+40)) / 2) + iconHeight + 40; + int textY = ((gr_fb_height() - (iconHeight+textHeight+40+sh)) / 2) + iconHeight + 40; gr_blit(surface, 0, 0, iconWidth, iconHeight, iconX, iconY); + if (stageHeight > 0) { + int sw = gr_get_width(stageMarkerEmpty); + int x = (gr_fb_width() - max_stage * gr_get_width(stageMarkerEmpty)) / 2; + int y = iconY + iconHeight + 20; + for (int i = 0; i < max_stage; ++i) { + gr_blit((i < stage) ? stageMarkerFill : stageMarkerEmpty, + 0, 0, sw, stageHeight, x, y); + x += sw; + } + } + gr_color(255, 255, 255, 255); gr_texticon(textX, textY, text_surface); } @@ -349,6 +365,8 @@ void ScreenRecoveryUI::Init() 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]); @@ -436,6 +454,13 @@ void ScreenRecoveryUI::SetProgress(float fraction) pthread_mutex_unlock(&updateMutex); } +void ScreenRecoveryUI::SetStage(int current, int max) { + pthread_mutex_lock(&updateMutex); + stage = current; + max_stage = max; + pthread_mutex_unlock(&updateMutex); +} + void ScreenRecoveryUI::Print(const char *fmt, ...) { char buf[256]; diff --git a/screen_ui.h b/screen_ui.h index ada006d1e..aa114a4d9 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -39,6 +39,8 @@ class ScreenRecoveryUI : public RecoveryUI { void ShowProgress(float portion, float seconds); void SetProgress(float fraction); + void SetStage(int current, int max); + // text log void ShowText(bool visible); bool IsTextVisible(); @@ -58,9 +60,6 @@ class ScreenRecoveryUI : public RecoveryUI { enum UIElement { HEADER, MENU, MENU_SEL_BG, MENU_SEL_FG, LOG, TEXT_FILL }; virtual void SetColor(UIElement e); - protected: - int install_overlay_offset_x, install_overlay_offset_y; - private: Icon currentIcon; int installingFrame; @@ -72,6 +71,8 @@ class ScreenRecoveryUI : public RecoveryUI { gr_surface *installation; gr_surface progressBarEmpty; gr_surface progressBarFill; + gr_surface stageMarkerEmpty; + gr_surface stageMarkerFill; ProgressType progressBarType; @@ -103,7 +104,8 @@ class ScreenRecoveryUI : public RecoveryUI { int iconX, iconY; - void draw_install_overlay_locked(int frame); + int stage, max_stage; + void draw_background_locked(Icon icon); void draw_progress_locked(); void draw_screen_locked(); @@ -30,6 +30,8 @@ class RecoveryUI { // Initialize the object; called before anything else. virtual void Init(); + // Show a stage indicator. Call immediately after Init(). + virtual void SetStage(int current, int max) { } // After calling Init(), you can tell the UI what locale it is operating in. virtual void SetLocale(const char* locale) { } diff --git a/updater/install.c b/updater/install.c index 1455557ca..872cbf857 100644 --- a/updater/install.c +++ b/updater/install.c @@ -34,6 +34,9 @@ #include <linux/xattr.h> #include <inttypes.h> +#include "bootloader.h" +#include "applypatch/applypatch.h" +#include "cutils/android_reboot.h" #include "cutils/misc.h" #include "cutils/properties.h" #include "edify/expr.h" @@ -42,7 +45,6 @@ #include "mtdutils/mounts.h" #include "mtdutils/mtdutils.h" #include "updater.h" -#include "applypatch/applypatch.h" #ifdef USE_EXT4 #include "make_ext4fs.h" @@ -1269,7 +1271,6 @@ Value* Sha1CheckFn(const char* name, State* state, int argc, Expr* argv[]) { } if (args[0]->size < 0) { - printf("%s(): no file contents received", name); return StringValue(strdup("")); } uint8_t digest[SHA_DIGEST_SIZE]; @@ -1322,12 +1323,11 @@ Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) { FileContents fc; if (LoadFileContents(filename, &fc, RETOUCH_DONT_MASK) != 0) { - ErrorAbort(state, "%s() loading \"%s\" failed: %s", - name, filename, strerror(errno)); free(filename); - free(v); + v->size = -1; + v->data = NULL; free(fc.data); - return NULL; + return v; } v->size = fc.size; @@ -1337,6 +1337,105 @@ Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) { return v; } +// Immediately reboot the device. Recovery is not finished normally, +// so if you reboot into recovery it will re-start applying the +// current package (because nothing has cleared the copy of the +// arguments stored in the BCB). +// +// The argument is the partition name passed to the android reboot +// property. It can be "recovery" to boot from the recovery +// partition, or "" (empty string) to boot from the regular boot +// partition. +Value* RebootNowFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + } + + char* filename; + char* property; + if (ReadArgs(state, argv, 2, &filename, &property) < 0) return NULL; + + char buffer[80]; + + // zero out the 'command' field of the bootloader message. + memset(buffer, 0, sizeof(((struct bootloader_message*)0)->command)); + FILE* f = fopen(filename, "r+b"); + fseek(f, offsetof(struct bootloader_message, command), SEEK_SET); + fwrite(buffer, sizeof(((struct bootloader_message*)0)->command), 1, f); + fclose(f); + free(filename); + + strcpy(buffer, "reboot,"); + if (property != NULL) { + strncat(buffer, property, sizeof(buffer)-10); + } + + property_set(ANDROID_RB_PROPERTY, buffer); + + sleep(5); + free(property); + ErrorAbort(state, "%s() failed to reboot", name); + return NULL; +} + +// Store a string value somewhere that future invocations of recovery +// can access it. This value is called the "stage" and can be used to +// drive packages that need to do reboots in the middle of +// installation and keep track of where they are in the multi-stage +// install. +// +// The first argument is the block device for the misc partition +// ("/misc" in the fstab), which is where this value is stored. The +// second argument is the string to store; it should not exceed 31 +// bytes. +Value* SetStageFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + } + + char* filename; + char* stagestr; + if (ReadArgs(state, argv, 2, &filename, &stagestr) < 0) return NULL; + + // Store this value in the misc partition, immediately after the + // bootloader message that the main recovery uses to save its + // arguments in case of the device restarting midway through + // package installation. + FILE* f = fopen(filename, "r+b"); + fseek(f, offsetof(struct bootloader_message, stage), SEEK_SET); + int to_write = strlen(stagestr)+1; + int max_size = sizeof(((struct bootloader_message*)0)->stage); + if (to_write > max_size) { + to_write = max_size; + stagestr[max_size-1] = 0; + } + fwrite(stagestr, to_write, 1, f); + fclose(f); + + free(stagestr); + return StringValue(filename); +} + +// Return the value most recently saved with SetStageFn. The argument +// is the block device for the misc partition. +Value* GetStageFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); + } + + char* filename; + if (ReadArgs(state, argv, 1, &filename) < 0) return NULL; + + char buffer[sizeof(((struct bootloader_message*)0)->stage)]; + FILE* f = fopen(filename, "rb"); + fseek(f, offsetof(struct bootloader_message, stage), SEEK_SET); + fread(buffer, sizeof(buffer), 1, f); + fclose(f); + buffer[sizeof(buffer)-1] = '\0'; + + return StringValue(strdup(buffer)); +} + void RegisterInstallFunctions() { RegisterFunction("mount", MountFn); RegisterFunction("is_mounted", IsMountedFn); @@ -1379,4 +1478,8 @@ void RegisterInstallFunctions() { RegisterFunction("ui_print", UIPrintFn); RegisterFunction("run_program", RunProgramFn); + + RegisterFunction("reboot_now", RebootNowFn); + RegisterFunction("get_stage", GetStageFn); + RegisterFunction("set_stage", SetStageFn); } |