summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--applypatch/applypatch.c18
-rw-r--r--bootloader.h15
-rw-r--r--recovery.cpp18
-rw-r--r--res/images/stage_empty.pngbin0 -> 322 bytes
-rw-r--r--res/images/stage_fill.pngbin0 -> 258 bytes
-rw-r--r--screen_ui.cpp31
-rw-r--r--screen_ui.h10
-rw-r--r--ui.h2
-rw-r--r--updater/install.c115
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
new file mode 100644
index 000000000..251ec1969
--- /dev/null
+++ b/res/images/stage_empty.png
Binary files differ
diff --git a/res/images/stage_fill.png b/res/images/stage_fill.png
new file mode 100644
index 000000000..1ab79e862
--- /dev/null
+++ b/res/images/stage_fill.png
Binary files differ
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();
diff --git a/ui.h b/ui.h
index d85fc659b..2dfe848e2 100644
--- a/ui.h
+++ b/ui.h
@@ -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);
}