Update TWRP to AOSP 7.1.2

Change-Id: I19c1546efb4182aac62c690e3cc05b04e3a9a32e
diff --git a/Android.mk b/Android.mk
index 5a37b62..3d3428f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -652,9 +652,9 @@
     include $(commands_recovery_local_path)/minadbd/Android.mk \
         $(commands_recovery_local_path)/minui/Android.mk
 else
-    TARGET_GLOBAL_CFLAGS += -DTW_USE_OLD_MINUI_H
-    include $(commands_recovery_local_path)/minadbd.old/Android.mk \
-        $(commands_recovery_local_path)/minui.old/Android.mk
+    TARGET_GLOBAL_CFLAGS += -DTW_USE_MINUI_21
+    include $(commands_recovery_local_path)/minadbd21/Android.mk \
+        $(commands_recovery_local_path)/minui21/Android.mk
 endif
 
 #includes for TWRP
diff --git a/error_code.h b/error_code.h
index fe38ba4..dfea0eb 100644
--- a/error_code.h
+++ b/error_code.h
@@ -44,4 +44,26 @@
     kVendorFailure = 200
 };
 
+enum UncryptErrorCode {
+    kUncryptNoError = -1,
+    kUncryptErrorPlaceholder = 50,
+    kUncryptTimeoutError = 100,
+    kUncryptFileRemoveError,
+    kUncryptFileOpenError,
+    kUncryptSocketOpenError,
+    kUncryptSocketWriteError,
+    kUncryptSocketListenError,
+    kUncryptSocketAcceptError,
+    kUncryptFstabReadError,
+    kUncryptFileStatError,
+    kUncryptBlockOpenError,
+    kUncryptIoctlError,
+    kUncryptReadError,
+    kUncryptWriteError,
+    kUncryptFileSyncError,
+    kUncryptFileCloseError,
+    kUncryptFileRenameError,
+    kUncryptPackageMissingError,
+};
+
 #endif
diff --git a/install.cpp b/install.cpp
index 02c845c..e144d9b 100644
--- a/install.cpp
+++ b/install.cpp
@@ -30,6 +30,7 @@
 #include <string>
 #include <vector>
 
+#include <android-base/file.h>
 #include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -54,6 +55,7 @@
 static constexpr const char* AB_OTA_PAYLOAD = "payload.bin";
 #define PUBLIC_KEYS_FILE "/res/keys"
 static constexpr const char* METADATA_PATH = "META-INF/com/android/metadata";
+static constexpr const char* UNCRYPT_STATUS = "/cache/recovery/uncrypt_status";
 
 // Default allocation of progress bar segments to operations
 static const int VERIFICATION_PROGRESS_TIME = 60;
@@ -371,6 +373,14 @@
     }
 
     pid_t pid = fork();
+
+    if (pid == -1) {
+        close(pipefd[0]);
+        close(pipefd[1]);
+        LOGE("Failed to fork update binary: %s\n", strerror(errno));
+        return INSTALL_ERROR;
+    }
+
     if (pid == 0) {
         umask(022);
         close(pipefd[0]);
@@ -511,13 +521,6 @@
     modified_flash = true;
     auto start = std::chrono::system_clock::now();
 
-    FILE* install_log = fopen_path(install_file, "w");
-    if (install_log) {
-        fputs(path, install_log);
-        fputc('\n', install_log);
-    } else {
-        LOGE("failed to open last_install: %s\n", strerror(errno));
-    }
     int result;
     std::vector<std::string> log_buffer;
     if (setup_install_mounts() != 0) {
@@ -526,21 +529,40 @@
     } else {
         result = really_install_package(path, wipe_cache, needs_mount, log_buffer, retry_count);
     }
-    if (install_log != nullptr) {
-        fputc(result == INSTALL_SUCCESS ? '1' : '0', install_log);
-        fputc('\n', install_log);
-        std::chrono::duration<double> duration = std::chrono::system_clock::now() - start;
-        int count = static_cast<int>(duration.count());
-        // Report the time spent to apply OTA update in seconds.
-        fprintf(install_log, "time_total: %d\n", count);
-        fprintf(install_log, "retry: %d\n", retry_count);
 
-        for (const auto& s : log_buffer) {
-            fprintf(install_log, "%s\n", s.c_str());
+    // Measure the time spent to apply OTA update in seconds.
+    std::chrono::duration<double> duration = std::chrono::system_clock::now() - start;
+    int time_total = static_cast<int>(duration.count());
+
+    if (ensure_path_mounted(UNCRYPT_STATUS) != 0) {
+        LOGW("Can't mount %s\n", UNCRYPT_STATUS);
+    } else {
+        std::string uncrypt_status;
+        if (!android::base::ReadFileToString(UNCRYPT_STATUS, &uncrypt_status)) {
+            LOGW("failed to read uncrypt status: %s\n", strerror(errno));
+        } else if (!android::base::StartsWith(uncrypt_status, "uncrypt_")) {
+            LOGW("corrupted uncrypt_status: %s: %s\n", uncrypt_status.c_str(), strerror(errno));
+        } else {
+            log_buffer.push_back(android::base::Trim(uncrypt_status));
         }
-
-        fclose(install_log);
     }
+
+    // The first two lines need to be the package name and install result.
+    std::vector<std::string> log_header = {
+        path,
+        result == INSTALL_SUCCESS ? "1" : "0",
+        "time_total: " + std::to_string(time_total),
+        "retry: " + std::to_string(retry_count),
+    };
+    std::string log_content = android::base::Join(log_header, "\n") + "\n" +
+            android::base::Join(log_buffer, "\n");
+    if (!android::base::WriteStringToFile(log_content, install_file)) {
+        LOGE("failed to write %s: %s\n", install_file, strerror(errno));
+    }
+
+    // Write a copy into last_log.
+    LOGI("%s\n", log_content.c_str());
+
     return result;
 }
 
diff --git a/minadbd.old/Android.mk b/minadbd21/Android.mk
similarity index 100%
rename from minadbd.old/Android.mk
rename to minadbd21/Android.mk
diff --git a/minadbd.old/README.txt b/minadbd21/README.txt
similarity index 100%
rename from minadbd.old/README.txt
rename to minadbd21/README.txt
diff --git a/minadbd.old/adb.c b/minadbd21/adb.c
similarity index 100%
rename from minadbd.old/adb.c
rename to minadbd21/adb.c
diff --git a/minadbd.old/adb.h b/minadbd21/adb.h
similarity index 100%
rename from minadbd.old/adb.h
rename to minadbd21/adb.h
diff --git a/minadbd.old/fdevent.c b/minadbd21/fdevent.c
similarity index 100%
rename from minadbd.old/fdevent.c
rename to minadbd21/fdevent.c
diff --git a/minadbd.old/fdevent.h b/minadbd21/fdevent.h
similarity index 100%
rename from minadbd.old/fdevent.h
rename to minadbd21/fdevent.h
diff --git a/minadbd.old/fuse_adb_provider.c b/minadbd21/fuse_adb_provider.c
similarity index 100%
rename from minadbd.old/fuse_adb_provider.c
rename to minadbd21/fuse_adb_provider.c
diff --git a/minadbd.old/fuse_adb_provider.h b/minadbd21/fuse_adb_provider.h
similarity index 100%
rename from minadbd.old/fuse_adb_provider.h
rename to minadbd21/fuse_adb_provider.h
diff --git a/minadbd.old/mutex_list.h b/minadbd21/mutex_list.h
similarity index 100%
rename from minadbd.old/mutex_list.h
rename to minadbd21/mutex_list.h
diff --git a/minadbd.old/services.c b/minadbd21/services.c
similarity index 100%
rename from minadbd.old/services.c
rename to minadbd21/services.c
diff --git a/minadbd.old/sockets.c b/minadbd21/sockets.c
similarity index 100%
rename from minadbd.old/sockets.c
rename to minadbd21/sockets.c
diff --git a/minadbd.old/sysdeps.h b/minadbd21/sysdeps.h
similarity index 100%
rename from minadbd.old/sysdeps.h
rename to minadbd21/sysdeps.h
diff --git a/minadbd.old/transport.c b/minadbd21/transport.c
similarity index 100%
rename from minadbd.old/transport.c
rename to minadbd21/transport.c
diff --git a/minadbd.old/transport.h b/minadbd21/transport.h
similarity index 100%
rename from minadbd.old/transport.h
rename to minadbd21/transport.h
diff --git a/minadbd.old/transport_usb.c b/minadbd21/transport_usb.c
similarity index 100%
rename from minadbd.old/transport_usb.c
rename to minadbd21/transport_usb.c
diff --git a/minadbd.old/usb_linux_client.c b/minadbd21/usb_linux_client.c
similarity index 100%
rename from minadbd.old/usb_linux_client.c
rename to minadbd21/usb_linux_client.c
diff --git a/minadbd.old/utils.c b/minadbd21/utils.c
similarity index 100%
rename from minadbd.old/utils.c
rename to minadbd21/utils.c
diff --git a/minadbd.old/utils.h b/minadbd21/utils.h
similarity index 100%
rename from minadbd.old/utils.h
rename to minadbd21/utils.h
diff --git a/minui/Android.mk b/minui/Android.mk
index 09409a4..34bf5f5 100644
--- a/minui/Android.mk
+++ b/minui/Android.mk
@@ -79,6 +79,9 @@
 ifneq ($(BOARD_USE_CUSTOM_RECOVERY_FONT),)
   LOCAL_CFLAGS += -DBOARD_USE_CUSTOM_RECOVERY_FONT=$(BOARD_USE_CUSTOM_RECOVERY_FONT)
 endif
+ifneq ($(wildcard system/core/healthd/animation.h),)
+    LOCAL_CFLAGS += -DTW_USE_MINUI_CUSTOM_FONTS
+endif
 include $(BUILD_STATIC_LIBRARY)
 
 # Used by OEMs for factory test images.
diff --git a/minui/font_10x18.h b/minui/font_10x18.h
index 29d7053..30dfb9c 100644
--- a/minui/font_10x18.h
+++ b/minui/font_10x18.h
@@ -1,14 +1,14 @@
 struct {
   unsigned width;
   unsigned height;
-  unsigned cwidth;
-  unsigned cheight;
+  unsigned char_width;
+  unsigned char_height;
   unsigned char rundata[2973];
 } font = {
   .width = 960,
   .height = 18,
-  .cwidth = 10,
-  .cheight = 18,
+  .char_width = 10,
+  .char_height = 18,
   .rundata = {
 0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x55,0x82,0x06,0x82,0x02,0x82,0x10,0x82,
 0x11,0x83,0x08,0x82,0x0a,0x82,0x04,0x82,0x46,0x82,0x08,0x82,0x07,0x84,0x06,
diff --git a/minui/graphics.cpp b/minui/graphics.cpp
index 244db3c..3d847b5 100644
--- a/minui/graphics.cpp
+++ b/minui/graphics.cpp
@@ -40,12 +40,6 @@
 #include "minui.h"
 #include "graphics.h"
 
-struct GRFont {
-    GRSurface* texture;
-    int cwidth;
-    int cheight;
-};
-
 static GRFont* gr_font = NULL;
 static minui_backend* gr_backend = NULL;
 
@@ -68,17 +62,35 @@
 {
     return x < 0 || x >= gr_draw->width || y < 0 || y >= gr_draw->height;
 }
-
+//#define TW_USE_MINUI_CUSTOM_FONTS 1
+#ifndef TW_USE_MINUI_CUSTOM_FONTS
 int gr_measure(const char *s)
 {
-    return gr_font->cwidth * strlen(s);
+    return gr_font->char_width * strlen(s);
 }
 
 void gr_font_size(int *x, int *y)
 {
-    *x = gr_font->cwidth;
-    *y = gr_font->cheight;
+    *x = gr_font->char_width;
+    *y = gr_font->char_height;
 }
+#else // TW_USE_MINUI_CUSTOM_FONTS
+const GRFont* gr_sys_font()
+{
+    return gr_font;
+}
+
+int gr_measure(const GRFont* font, const char *s)
+{
+    return font->char_width * strlen(s);
+}
+
+void gr_font_size(const GRFont* font, int *x, int *y)
+{
+    *x = font->char_width;
+    *y = font->char_height;
+}
+#endif // TW_USE_MINUI_CUSTOM_FONTS
 
 void blend_16bpp(unsigned char* px, unsigned r5, unsigned g5, unsigned b5, unsigned char a)
 {
@@ -146,36 +158,67 @@
     }
 }
 
+#ifndef TW_USE_MINUI_CUSTOM_FONTS
 void gr_text(int x, int y, const char *s, bool bold)
 {
     GRFont* font = gr_font;
 
     if (!font->texture || gr_current_a == 0) return;
 
-    bold = bold && (font->texture->height != font->cheight);
+    bold = bold && (font->texture->height != font->char_height);
 
     x += overscan_offset_x;
     y += overscan_offset_y;
 
     unsigned char ch;
     while ((ch = *s++)) {
-        if (outside(x, y) || outside(x+font->cwidth-1, y+font->cheight-1)) break;
+        if (outside(x, y) || outside(x+font->char_width-1, y+font->char_height-1)) break;
 
         if (ch < ' ' || ch > '~') {
             ch = '?';
         }
 
-        unsigned char* src_p = font->texture->data + ((ch - ' ') * font->cwidth) +
-                               (bold ? font->cheight * font->texture->row_bytes : 0);
+        unsigned char* src_p = font->texture->data + ((ch - ' ') * font->char_width) +
+                               (bold ? font->char_height * font->texture->row_bytes : 0);
         unsigned char* dst_p = gr_draw->data + y*gr_draw->row_bytes + x*gr_draw->pixel_bytes;
 
         text_blend(src_p, font->texture->row_bytes,
                    dst_p, gr_draw->row_bytes,
-                   font->cwidth, font->cheight);
+                   font->char_width, font->char_height);
 
-        x += font->cwidth;
+        x += font->char_width;
     }
 }
+#else //TW_USE_MINUI_CUSTOM_FONTS
+void gr_text(const GRFont* font, int x, int y, const char *s, bool bold)
+{
+    if (!font->texture || gr_current_a == 0) return;
+
+    bold = bold && (font->texture->height != font->char_height);
+
+    x += overscan_offset_x;
+    y += overscan_offset_y;
+
+    unsigned char ch;
+    while ((ch = *s++)) {
+        if (outside(x, y) || outside(x+font->char_width-1, y+font->char_height-1)) break;
+
+        if (ch < ' ' || ch > '~') {
+            ch = '?';
+        }
+
+        unsigned char* src_p = font->texture->data + ((ch - ' ') * font->char_width) +
+                               (bold ? font->char_height * font->texture->row_bytes : 0);
+        unsigned char* dst_p = gr_draw->data + y*gr_draw->row_bytes + x*gr_draw->pixel_bytes;
+
+        text_blend(src_p, font->texture->row_bytes,
+                   dst_p, gr_draw->row_bytes,
+                   font->char_width, font->char_height);
+
+        x += font->char_width;
+    }
+}
+#endif //TW_USE_MINUI_CUSTOM_FONTS
 
 void gr_texticon(int x, int y, GRSurface* icon) {
     if (icon == NULL) return;
@@ -383,6 +426,7 @@
     return surface->height;
 }
 
+#ifndef TW_USE_MINUI_CUSTOM_FONTS
 static void gr_init_font(void)
 {
     gr_font = reinterpret_cast<GRFont*>(calloc(sizeof(*gr_font), 1));
@@ -392,8 +436,8 @@
         // The font image should be a 96x2 array of character images.  The
         // columns are the printable ASCII characters 0x20 - 0x7f.  The
         // top row is regular text; the bottom row is bold.
-        gr_font->cwidth = gr_font->texture->width / 96;
-        gr_font->cheight = gr_font->texture->height / 2;
+        gr_font->char_width = gr_font->texture->width / 96;
+        gr_font->char_height = gr_font->texture->height / 2;
     } else {
         printf("failed to read font: res=%d\n", res);
 
@@ -414,11 +458,73 @@
             bits += (data & 0x7f);
         }
 
-        gr_font->cwidth = font.cwidth;
-        gr_font->cheight = font.cheight;
+        gr_font->char_width = font.char_width;
+        gr_font->char_height = font.char_height;
     }
 }
 
+void gr_set_font(__attribute__ ((unused))const char* name) {
+	//this cm function is made to change font. Don't care, just init the font:
+	gr_init_font();
+	return;
+}
+#else
+int gr_init_font(const char* name, GRFont** dest) {
+    GRFont* font = reinterpret_cast<GRFont*>(calloc(1, sizeof(*gr_font)));
+    if (font == nullptr) {
+        return -1;
+    }
+
+    int res = res_create_alpha_surface(name, &(font->texture));
+    if (res < 0) {
+        free(font);
+        return res;
+    }
+
+    // The font image should be a 96x2 array of character images.  The
+    // columns are the printable ASCII characters 0x20 - 0x7f.  The
+    // top row is regular text; the bottom row is bold.
+    font->char_width = font->texture->width / 96;
+    font->char_height = font->texture->height / 2;
+
+    *dest = font;
+
+    return 0;
+}
+
+static void gr_init_font(void)
+{
+    int res = gr_init_font("font", &gr_font);
+    if (res == 0) {
+        return;
+    }
+
+    printf("failed to read font: res=%d\n", res);
+
+
+    // fall back to the compiled-in font.
+    gr_font = reinterpret_cast<GRFont*>(calloc(1, sizeof(*gr_font)));
+    gr_font->texture = reinterpret_cast<GRSurface*>(malloc(sizeof(*gr_font->texture)));
+    gr_font->texture->width = font.width;
+    gr_font->texture->height = font.height;
+    gr_font->texture->row_bytes = font.width;
+    gr_font->texture->pixel_bytes = 1;
+
+    unsigned char* bits = reinterpret_cast<unsigned char*>(malloc(font.width * font.height));
+    gr_font->texture->data = reinterpret_cast<unsigned char*>(bits);
+
+    unsigned char data;
+    unsigned char* in = font.rundata;
+    while((data = *in++)) {
+        memset(bits, (data & 0x80) ? 255 : 0, data & 0x7f);
+        bits += (data & 0x7f);
+    }
+
+    gr_font->char_width = font.char_width;
+    gr_font->char_height = font.char_height;
+}
+#endif
+
 #if 0
 // Exercises many of the gr_*() functions; useful for testing.
 static void gr_test() {
@@ -547,9 +653,3 @@
 {
     gr_backend->blank(gr_backend, blank);
 }
-
-void gr_set_font(__attribute__ ((unused))const char* name) {
-	//this cm function is made to change font. Don't care, just init the font:
-	gr_init_font();
-	return;
-}
diff --git a/minui/minui.h b/minui/minui.h
index dee4641..abef036 100644
--- a/minui/minui.h
+++ b/minui/minui.h
@@ -17,7 +17,7 @@
 #ifndef _MINUI_H_
 #define _MINUI_H_
 
-#ifndef TW_USE_OLD_MINUI_H
+#ifndef TW_USE_MINUI_21
 
 #include <sys/types.h>
 
@@ -35,6 +35,12 @@
     unsigned char* data;
 };
 
+struct GRFont {
+    GRSurface* texture;
+    int char_width;
+    int char_height;
+};
+
 int gr_init();
 void gr_exit();
 
@@ -47,11 +53,21 @@
 void gr_clear();  // clear entire surface to current color
 void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
 void gr_fill(int x1, int y1, int x2, int y2);
-void gr_text(int x, int y, const char *s, bool bold);
+
 void gr_texticon(int x, int y, GRSurface* icon);
+#ifndef TW_USE_MINUI_CUSTOM_FONTS
+void gr_text(int x, int y, const char *s, bool bold);
 int gr_measure(const char *s);
 void gr_font_size(int *x, int *y);
 void gr_set_font(__attribute__ ((unused))const char* name);
+#else
+
+const GRFont* gr_sys_font();
+int gr_init_font(const char* name, GRFont** dest);
+void gr_text(const GRFont* font, int x, int y, const char *s, bool bold);
+int gr_measure(const GRFont* font, const char *s);
+void gr_font_size(const GRFont* font, int *x, int *y);
+#endif
 
 void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy);
 unsigned int gr_get_width(GRSurface* surface);
@@ -127,9 +143,9 @@
 // functions.
 void res_free_surface(GRSurface* surface);
 
-#else //ifndef TW_USE_OLD_MINUI_H
+#else //ifndef TW_USE_MINUI_21
 
-// This the old minui.old/minui.h for compatibility with building TWRP
+// This the old minui21/minui.h for compatibility with building TWRP
 // in pre 6.0 trees.
 
 #include <stdbool.h>
@@ -217,5 +233,5 @@
 }
 #endif
 
-#endif // ifndef TW_USE_OLD_MINUI_H
+#endif // ifndef TW_USE_MINUI_21
 #endif // ifndef _MINUI_H_
diff --git a/minui/roboto_10x18.h b/minui/roboto_10x18.h
index 0d118ba..2d1535b 100644
--- a/minui/roboto_10x18.h
+++ b/minui/roboto_10x18.h
@@ -1,14 +1,14 @@
 struct {
   unsigned width;
   unsigned height;
-  unsigned cwidth;
-  unsigned cheight;
+  unsigned char_width;
+  unsigned char_height;
   unsigned char rundata[2718];
 } font = {
   .width = 960,
   .height = 18,
-  .cwidth = 10,
-  .cheight = 18,
+  .char_width = 10,
+  .char_height = 18,
   .rundata = {
 0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,
 0x3b,0x81,0x29,0x81,0x06,0x81,0x3f,0x81,0x7f,0x7f,0x7f,0x37,0x83,0x05,0x81,
diff --git a/minui/roboto_15x24.h b/minui/roboto_15x24.h
index 7857c59..aea1cbf 100644
--- a/minui/roboto_15x24.h
+++ b/minui/roboto_15x24.h
@@ -1,14 +1,14 @@
 struct {
   unsigned width;
   unsigned height;
-  unsigned cwidth;
-  unsigned cheight;
+  unsigned char_width;
+  unsigned char_height;
   unsigned char rundata[3979];
 } font = {
   .width = 1440,
   .height = 24,
-  .cwidth = 15,
-  .cheight = 24,
+  .char_width = 15,
+  .char_height = 24,
   .rundata = {
 0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x6e,0x81,0x3e,0x81,
 0x07,0x81,0x7f,0x7f,0x7f,0x7f,0x7f,0x76,0x84,0x17,0x84,0x7f,0x7f,0x7f,0x7f,
diff --git a/minui/roboto_23x41.h b/minui/roboto_23x41.h
index 6af2abc..793f784 100644
--- a/minui/roboto_23x41.h
+++ b/minui/roboto_23x41.h
@@ -1,14 +1,14 @@
 struct {
   unsigned width;
   unsigned height;
-  unsigned cwidth;
-  unsigned cheight;
+  unsigned char_width;
+  unsigned char_height;
   unsigned char rundata[6679];
 } font = {
   .width = 2208,
   .height = 41,
-  .cwidth = 23,
-  .cheight = 41,
+  .char_width = 23,
+  .char_height = 41,
   .rundata = {
 0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,
 0x7f,0x7f,0x7f,0x17,0x83,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,
diff --git a/minui.old/Android.mk b/minui21/Android.mk
similarity index 100%
rename from minui.old/Android.mk
rename to minui21/Android.mk
diff --git a/minui.old/events.c b/minui21/events.c
similarity index 100%
rename from minui.old/events.c
rename to minui21/events.c
diff --git a/minui.old/font_10x18.h b/minui21/font_10x18.h
similarity index 100%
rename from minui.old/font_10x18.h
rename to minui21/font_10x18.h
diff --git a/minui.old/font_7x16.h b/minui21/font_7x16.h
similarity index 100%
rename from minui.old/font_7x16.h
rename to minui21/font_7x16.h
diff --git a/minui.old/graphics.c b/minui21/graphics.c
similarity index 100%
rename from minui.old/graphics.c
rename to minui21/graphics.c
diff --git a/minui.old/graphics_overlay.c b/minui21/graphics_overlay.c
similarity index 100%
rename from minui.old/graphics_overlay.c
rename to minui21/graphics_overlay.c
diff --git a/minui.old/include/linux/msm_ion.h b/minui21/include/linux/msm_ion.h
similarity index 100%
rename from minui.old/include/linux/msm_ion.h
rename to minui21/include/linux/msm_ion.h
diff --git a/minui.old/include/linux/msm_mdp.h b/minui21/include/linux/msm_mdp.h
similarity index 100%
rename from minui.old/include/linux/msm_mdp.h
rename to minui21/include/linux/msm_mdp.h
diff --git a/minui.old/minui.h b/minui21/minui.h
similarity index 100%
rename from minui.old/minui.h
rename to minui21/minui.h
diff --git a/minui.old/mkfont.c b/minui21/mkfont.c
similarity index 100%
rename from minui.old/mkfont.c
rename to minui21/mkfont.c
diff --git a/minui.old/resources.c b/minui21/resources.c
similarity index 100%
rename from minui.old/resources.c
rename to minui21/resources.c
diff --git a/minui.old/roboto_10x18.h b/minui21/roboto_10x18.h
similarity index 100%
rename from minui.old/roboto_10x18.h
rename to minui21/roboto_10x18.h
diff --git a/minui.old/roboto_15x24.h b/minui21/roboto_15x24.h
similarity index 100%
rename from minui.old/roboto_15x24.h
rename to minui21/roboto_15x24.h
diff --git a/minui.old/roboto_23x41.h b/minui21/roboto_23x41.h
similarity index 100%
rename from minui.old/roboto_23x41.h
rename to minui21/roboto_23x41.h
diff --git a/recovery.cpp b/recovery.cpp
index ccb2d22..0f0b978 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -1429,15 +1429,18 @@
 }
 
 static void log_failure_code(ErrorCode code, const char *update_package) {
-    FILE* install_log = fopen_path(TEMPORARY_INSTALL_FILE, "w");
-    if (install_log != nullptr) {
-        fprintf(install_log, "%s\n", update_package);
-        fprintf(install_log, "0\n");
-        fprintf(install_log, "error: %d\n", code);
-        fclose(install_log);
-    } else {
-        LOGE("failed to open last_install: %s\n", strerror(errno));
+    std::vector<std::string> log_buffer = {
+        update_package,
+        "0",  // install result
+        "error: " + std::to_string(code),
+    };
+    std::string log_content = android::base::Join(log_buffer, "\n");
+    if (!android::base::WriteStringToFile(log_content, TEMPORARY_INSTALL_FILE)) {
+        LOGE("failed to write %s: %s\n", TEMPORARY_INSTALL_FILE, strerror(errno));
     }
+
+    // Also write the info into last_log.
+    LOGI("%s\n", log_content.c_str());
 }
 
 static ssize_t logbasename(
diff --git a/screen_ui.cpp b/screen_ui.cpp
index 2a0769e..95b97d1 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -53,8 +53,6 @@
 ScreenRecoveryUI::ScreenRecoveryUI() :
     currentIcon(NONE),
     locale(nullptr),
-    intro_done(false),
-    current_frame(0),
     progressBarType(EMPTY),
     progressScopeStart(0),
     progressScopeSize(0),
@@ -75,6 +73,8 @@
     file_viewer_text_(nullptr),
     intro_frames(0),
     loop_frames(0),
+    current_frame(0),
+    intro_done(false),
     animation_fps(30), // TODO: there's currently no way to infer this.
     stage(-1),
     max_stage(-1),
@@ -259,7 +259,7 @@
 }
 
 void ScreenRecoveryUI::DrawTextLine(int x, int* y, const char* line, bool bold) {
-    gr_text(x, *y, line, bold);
+    gr_text(gr_sys_font(), x, *y, line, bold);
     *y += char_height_ + 4;
 }
 
@@ -315,10 +315,10 @@
                     gr_fill(0, y - 2, gr_fb_width(), y + char_height_ + 2);
                     // Bold white text for the selected item.
                     SetColor(MENU_SEL_FG);
-                    gr_text(4, y, menu_[i], true);
+                    gr_text(gr_sys_font(), 4, y, menu_[i], true);
                     SetColor(MENU);
                 } else {
-                    gr_text(4, y, menu_[i], false);
+                    gr_text(gr_sys_font(), 4, y, menu_[i], false);
                 }
                 y += char_height_ + 4;
             }
@@ -334,7 +334,7 @@
         for (int ty = gr_fb_height() - char_height_;
              ty >= y && count < text_rows_;
              ty -= char_height_, ++count) {
-            gr_text(0, ty, text_[row], false);
+            gr_text(gr_sys_font(), 0, ty, text_[row], false);
             --row;
             if (row < 0) row = text_rows_ - 1;
         }
@@ -447,9 +447,18 @@
     Redraw();
 }
 
-void ScreenRecoveryUI::Init() {
+void ScreenRecoveryUI::InitTextParams() {
     gr_init();
 
+    gr_font_size(gr_sys_font(), &char_width_, &char_height_);
+    text_rows_ = gr_fb_height() / char_height_;
+    text_cols_ = gr_fb_width() / char_width_;
+}
+
+void ScreenRecoveryUI::Init() {
+    RecoveryUI::Init();
+    InitTextParams();
+
     density_ = static_cast<float>(property_get_int32("ro.sf.lcd_density", 160)) / 160.f;
 
     // Are we portrait or landscape?
@@ -457,10 +466,6 @@
     // Are we the large variant of our base layout?
     if (gr_fb_height() > PixelsFromDp(800)) ++layout_;
 
-    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);
@@ -487,37 +492,42 @@
     LoadAnimation();
 
     pthread_create(&progress_thread_, nullptr, ProgressThreadStartRoutine, this);
-
-    RecoveryUI::Init();
 }
 
 void ScreenRecoveryUI::LoadAnimation() {
-    // How many frames of intro and loop do we have?
     std::unique_ptr<DIR, decltype(&closedir)> dir(opendir("/res/images"), closedir);
     dirent* de;
+    std::vector<std::string> intro_frame_names;
+    std::vector<std::string> loop_frame_names;
+
     while ((de = readdir(dir.get())) != nullptr) {
-        int value;
-        if (sscanf(de->d_name, "intro%d", &value) == 1 && intro_frames < (value + 1)) {
-            intro_frames = value + 1;
-        } else if (sscanf(de->d_name, "loop%d", &value) == 1 && loop_frames < (value + 1)) {
-            loop_frames = value + 1;
+        int value, num_chars;
+        if (sscanf(de->d_name, "intro%d%n.png", &value, &num_chars) == 1) {
+            intro_frame_names.emplace_back(de->d_name, num_chars);
+        } else if (sscanf(de->d_name, "loop%d%n.png", &value, &num_chars) == 1) {
+            loop_frame_names.emplace_back(de->d_name, num_chars);
         }
     }
 
+    intro_frames = intro_frame_names.size();
+    loop_frames = loop_frame_names.size();
+
     // It's okay to not have an intro.
     if (intro_frames == 0) intro_done = true;
     // But you must have an animation.
     if (loop_frames == 0) abort();
 
+    std::sort(intro_frame_names.begin(), intro_frame_names.end());
+    std::sort(loop_frame_names.begin(), loop_frame_names.end());
+
     introFrames = new GRSurface*[intro_frames];
-    for (int i = 0; i < intro_frames; ++i) {
-        // TODO: remember the names above, so we don't have to hard-code the number of 0s.
-        LoadBitmap(android::base::StringPrintf("intro%05d", i).c_str(), &introFrames[i]);
+    for (size_t i = 0; i < intro_frames; i++) {
+        LoadBitmap(intro_frame_names.at(i).c_str(), &introFrames[i]);
     }
 
     loopFrames = new GRSurface*[loop_frames];
-    for (int i = 0; i < loop_frames; ++i) {
-        LoadBitmap(android::base::StringPrintf("loop%05d", i).c_str(), &loopFrames[i]);
+    for (size_t i = 0; i < loop_frames; i++) {
+        LoadBitmap(loop_frame_names.at(i).c_str(), &loopFrames[i]);
     }
 }
 
diff --git a/screen_ui.h b/screen_ui.h
index 8987757..de7b644 100644
--- a/screen_ui.h
+++ b/screen_ui.h
@@ -37,16 +37,16 @@
     void SetSystemUpdateText(bool security_update);
 
     // progress indicator
-    void SetProgressType(ProgressType type);
-    void ShowProgress(float portion, float seconds);
-    void SetProgress(float fraction);
+    void SetProgressType(ProgressType type) override;
+    void ShowProgress(float portion, float seconds) override;
+    void SetProgress(float fraction) override;
 
-    void SetStage(int current, int max);
+    void SetStage(int current, int max) override;
 
     // text log
-    void ShowText(bool visible);
-    bool IsTextVisible();
-    bool WasTextEverVisible();
+    void ShowText(bool visible) override;
+    bool IsTextVisible() override;
+    bool WasTextEverVisible() override;
 
     // printing messages
     void Print(const char* fmt, ...) __printflike(2, 3);
@@ -72,8 +72,6 @@
     Icon currentIcon;
 
     const char* locale;
-    bool intro_done;
-    int current_frame;
 
     // The scale factor from dp to pixels. 1.0 for mdpi, 4.0 for xxxhdpi.
     float density_;
@@ -123,8 +121,11 @@
     pthread_t progress_thread_;
 
     // Number of intro frames and loop frames in the animation.
-    int intro_frames;
-    int loop_frames;
+    size_t intro_frames;
+    size_t loop_frames;
+
+    size_t current_frame;
+    bool intro_done;
 
     // Number of frames per sec (default: 30) for both parts of the animation.
     int animation_fps;
@@ -136,11 +137,13 @@
     pthread_mutex_t updateMutex;
     bool rtl_locale;
 
-    void draw_background_locked();
-    void draw_foreground_locked();
-    void draw_screen_locked();
-    void update_screen_locked();
-    void update_progress_locked();
+    virtual void InitTextParams();
+
+    virtual void draw_background_locked();
+    virtual void draw_foreground_locked();
+    virtual void draw_screen_locked();
+    virtual void update_screen_locked();
+    virtual void update_progress_locked();
 
     GRSurface* GetCurrentFrame();
     GRSurface* GetCurrentText();
@@ -148,8 +151,8 @@
     static void* ProgressThreadStartRoutine(void* data);
     void ProgressThreadLoop();
 
-    void ShowFile(FILE*);
-    void PrintV(const char*, bool, va_list);
+    virtual void ShowFile(FILE*);
+    virtual void PrintV(const char*, bool, va_list);
     void PutChar(char);
     void ClearText();
 
@@ -158,9 +161,9 @@
     void LoadLocalizedBitmap(const char* filename, GRSurface** surface);
 
     int PixelsFromDp(int dp);
-    int GetAnimationBaseline();
-    int GetProgressBaseline();
-    int GetTextBaseline();
+    virtual int GetAnimationBaseline();
+    virtual int GetProgressBaseline();
+    virtual int GetTextBaseline();
 
     void DrawHorizontalRule(int* y);
     void DrawTextLine(int x, int* y, const char* line, bool bold);
diff --git a/twrp.cpp b/twrp.cpp
index 977fea2..6fd8f24 100644
--- a/twrp.cpp
+++ b/twrp.cpp
@@ -49,7 +49,7 @@
 #include "adb.h"
 #else
 extern "C" {
-#include "minadbd.old/adb.h"
+#include "minadbd21/adb.h"
 }
 #endif
 
diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp
index 5e804bc..280568d 100644
--- a/uncrypt/uncrypt.cpp
+++ b/uncrypt/uncrypt.cpp
@@ -118,6 +118,7 @@
 #define LOG_TAG "uncrypt"
 #include <log/log.h>
 
+#include "error_code.h"
 #include "unique_fd.h"
 
 #define WINDOW_SIZE 5
@@ -134,6 +135,7 @@
 // devices, on which /cache partitions always exist.
 static const std::string CACHE_BLOCK_MAP = "/cache/recovery/block.map";
 static const std::string UNCRYPT_PATH_FILE = "/cache/recovery/uncrypt_file";
+static const std::string UNCRYPT_STATUS = "/cache/recovery/uncrypt_status";
 static const std::string UNCRYPT_SOCKET = "uncrypt";
 
 static struct fstab* fstab = nullptr;
@@ -234,25 +236,25 @@
     std::string err;
     if (!android::base::RemoveFileIfExists(map_file, &err)) {
         ALOGE("failed to remove the existing map file %s: %s", map_file, err.c_str());
-        return -1;
+        return kUncryptFileRemoveError;
     }
     std::string tmp_map_file = std::string(map_file) + ".tmp";
     unique_fd mapfd(open(tmp_map_file.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR));
     if (!mapfd) {
         ALOGE("failed to open %s: %s\n", tmp_map_file.c_str(), strerror(errno));
-        return -1;
+        return kUncryptFileOpenError;
     }
 
     // Make sure we can write to the socket.
     if (!write_status_to_socket(0, socket)) {
         ALOGE("failed to write to socket %d\n", socket);
-        return -1;
+        return kUncryptSocketWriteError;
     }
 
     struct stat sb;
     if (stat(path, &sb) != 0) {
         ALOGE("failed to stat %s", path);
-        return -1;
+        return kUncryptFileStatError;
     }
 
     ALOGI(" block size: %ld bytes", static_cast<long>(sb.st_blksize));
@@ -266,7 +268,7 @@
                        blk_dev, sb.st_size, static_cast<long>(sb.st_blksize));
     if (!android::base::WriteStringToFd(s, mapfd.get())) {
         ALOGE("failed to write %s: %s", tmp_map_file.c_str(), strerror(errno));
-        return -1;
+        return kUncryptWriteError;
     }
 
     std::vector<std::vector<unsigned char>> buffers;
@@ -279,7 +281,7 @@
     unique_fd fd(open(path, O_RDONLY));
     if (!fd) {
         ALOGE("failed to open %s for reading: %s", path, strerror(errno));
-        return -1;
+        return kUncryptFileOpenError;
     }
 
     unique_fd wfd(-1);
@@ -287,7 +289,7 @@
         wfd = open(blk_dev, O_WRONLY);
         if (!wfd) {
             ALOGE("failed to open fd for writing: %s", strerror(errno));
-            return -1;
+            return kUncryptBlockOpenError;
         }
     }
 
@@ -306,13 +308,13 @@
             int block = head_block;
             if (ioctl(fd.get(), FIBMAP, &block) != 0) {
                 ALOGE("failed to find block %d", head_block);
-                return -1;
+                return kUncryptIoctlError;
             }
             add_block_to_ranges(ranges, block);
             if (encrypted) {
                 if (write_at_offset(buffers[head].data(), sb.st_blksize, wfd.get(),
                         static_cast<off64_t>(sb.st_blksize) * block) != 0) {
-                    return -1;
+                    return kUncryptWriteError;
                 }
             }
             head = (head + 1) % WINDOW_SIZE;
@@ -325,7 +327,7 @@
                     std::min(static_cast<off64_t>(sb.st_blksize), sb.st_size - pos));
             if (!android::base::ReadFully(fd.get(), buffers[tail].data(), to_read)) {
                 ALOGE("failed to read: %s", strerror(errno));
-                return -1;
+                return kUncryptReadError;
             }
             pos += to_read;
         } else {
@@ -342,13 +344,13 @@
         int block = head_block;
         if (ioctl(fd.get(), FIBMAP, &block) != 0) {
             ALOGE("failed to find block %d", head_block);
-            return -1;
+            return kUncryptIoctlError;
         }
         add_block_to_ranges(ranges, block);
         if (encrypted) {
             if (write_at_offset(buffers[head].data(), sb.st_blksize, wfd.get(),
                     static_cast<off64_t>(sb.st_blksize) * block) != 0) {
-                return -1;
+                return kUncryptWriteError;
             }
         }
         head = (head + 1) % WINDOW_SIZE;
@@ -358,41 +360,41 @@
     if (!android::base::WriteStringToFd(
             android::base::StringPrintf("%zu\n", ranges.size() / 2), mapfd.get())) {
         ALOGE("failed to write %s: %s", tmp_map_file.c_str(), strerror(errno));
-        return -1;
+        return kUncryptWriteError;
     }
     for (size_t i = 0; i < ranges.size(); i += 2) {
         if (!android::base::WriteStringToFd(
                 android::base::StringPrintf("%d %d\n", ranges[i], ranges[i+1]), mapfd.get())) {
             ALOGE("failed to write %s: %s", tmp_map_file.c_str(), strerror(errno));
-            return -1;
+            return kUncryptWriteError;
         }
     }
 
     if (fsync(mapfd.get()) == -1) {
         ALOGE("failed to fsync \"%s\": %s", tmp_map_file.c_str(), strerror(errno));
-        return -1;
+        return kUncryptFileSyncError;
     }
     if (close(mapfd.get()) == -1) {
         ALOGE("failed to close %s: %s", tmp_map_file.c_str(), strerror(errno));
-        return -1;
+        return kUncryptFileCloseError;
     }
     mapfd = -1;
 
     if (encrypted) {
         if (fsync(wfd.get()) == -1) {
             ALOGE("failed to fsync \"%s\": %s", blk_dev, strerror(errno));
-            return -1;
+            return kUncryptFileSyncError;
         }
         if (close(wfd.get()) == -1) {
             ALOGE("failed to close %s: %s", blk_dev, strerror(errno));
-            return -1;
+            return kUncryptFileCloseError;
         }
         wfd = -1;
     }
 
     if (rename(tmp_map_file.c_str(), map_file) == -1) {
         ALOGE("failed to rename %s to %s: %s", tmp_map_file.c_str(), map_file, strerror(errno));
-        return -1;
+        return kUncryptFileRenameError;
     }
     // Sync dir to make rename() result written to disk.
     std::string file_name = map_file;
@@ -400,15 +402,15 @@
     unique_fd dfd(open(dir_name.c_str(), O_RDONLY | O_DIRECTORY));
     if (!dfd) {
         ALOGE("failed to open dir %s: %s", dir_name.c_str(), strerror(errno));
-        return -1;
+        return kUncryptFileOpenError;
     }
     if (fsync(dfd.get()) == -1) {
         ALOGE("failed to fsync %s: %s", dir_name.c_str(), strerror(errno));
-        return -1;
+        return kUncryptFileSyncError;
     }
     if (close(dfd.get()) == -1) {
         ALOGE("failed to close %s: %s", dir_name.c_str(), strerror(errno));
-        return -1;
+        return kUncryptFileCloseError;
     }
     dfd = -1;
     return 0;
@@ -448,30 +450,58 @@
     // and /sdcard we leave the file alone.
     if (strncmp(path, "/data/", 6) == 0) {
         ALOGI("writing block map %s", map_file);
-        if (produce_block_map(path, map_file, blk_dev, encrypted, socket) != 0) {
-            return 1;
-        }
+        return produce_block_map(path, map_file, blk_dev, encrypted, socket);
     }
 
     return 0;
 }
 
+static void log_uncrypt_error_code(UncryptErrorCode error_code) {
+    if (!android::base::WriteStringToFile(android::base::StringPrintf(
+            "uncrypt_error: %d\n", error_code), UNCRYPT_STATUS)) {
+        ALOGW("failed to write to %s: %s", UNCRYPT_STATUS.c_str(), strerror(errno));
+    }
+}
+
 static bool uncrypt_wrapper(const char* input_path, const char* map_file, const int socket) {
+    // Initialize the uncrypt error to kUncryptErrorPlaceholder.
+    log_uncrypt_error_code(kUncryptErrorPlaceholder);
+
     std::string package;
     if (input_path == nullptr) {
         if (!find_uncrypt_package(UNCRYPT_PATH_FILE, &package)) {
             write_status_to_socket(-1, socket);
+            // Overwrite the error message.
+            log_uncrypt_error_code(kUncryptPackageMissingError);
             return false;
         }
         input_path = package.c_str();
     }
     CHECK(map_file != nullptr);
+
+    auto start = std::chrono::system_clock::now();
     int status = uncrypt(input_path, map_file, socket);
+    std::chrono::duration<double> duration = std::chrono::system_clock::now() - start;
+    int count = static_cast<int>(duration.count());
+
+    std::string uncrypt_message = android::base::StringPrintf("uncrypt_time: %d\n", count);
     if (status != 0) {
+        // Log the time cost and error code if uncrypt fails.
+        uncrypt_message += android::base::StringPrintf("uncrypt_error: %d\n", status);
+        if (!android::base::WriteStringToFile(uncrypt_message, UNCRYPT_STATUS)) {
+            ALOGW("failed to write to %s: %s", UNCRYPT_STATUS.c_str(), strerror(errno));
+        }
+
         write_status_to_socket(-1, socket);
         return false;
     }
+
+    if (!android::base::WriteStringToFile(uncrypt_message, UNCRYPT_STATUS)) {
+        ALOGW("failed to write to %s: %s", UNCRYPT_STATUS.c_str(), strerror(errno));
+    }
+
     write_status_to_socket(100, socket);
+
     return true;
 }
 
@@ -561,6 +591,7 @@
     }
 
     if ((fstab = read_fstab()) == nullptr) {
+        log_uncrypt_error_code(kUncryptFstabReadError);
         return 1;
     }
 
@@ -569,18 +600,21 @@
     unique_fd service_socket(android_get_control_socket(UNCRYPT_SOCKET.c_str()));
     if (!service_socket) {
         ALOGE("failed to open socket \"%s\": %s", UNCRYPT_SOCKET.c_str(), strerror(errno));
+        log_uncrypt_error_code(kUncryptSocketOpenError);
         return 1;
     }
     fcntl(service_socket.get(), F_SETFD, FD_CLOEXEC);
 
     if (listen(service_socket.get(), 1) == -1) {
         ALOGE("failed to listen on socket %d: %s", service_socket.get(), strerror(errno));
+        log_uncrypt_error_code(kUncryptSocketListenError);
         return 1;
     }
 
     unique_fd socket_fd(accept4(service_socket.get(), nullptr, nullptr, SOCK_CLOEXEC));
     if (!socket_fd) {
         ALOGE("failed to accept on socket %d: %s", service_socket.get(), strerror(errno));
+        log_uncrypt_error_code(kUncryptSocketAcceptError);
         return 1;
     }
 
diff --git a/updater/install.cpp b/updater/install.cpp
index d4ae64e..1c79640 100644
--- a/updater/install.cpp
+++ b/updater/install.cpp
@@ -35,8 +35,10 @@
 #include <inttypes.h>
 
 #include <memory>
+#include <string>
 #include <vector>
 
+#include <android-base/file.h>
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
 #include <android-base/stringprintf.h>
@@ -970,7 +972,6 @@
     return StringValue(strdup(value));
 }
 
-
 // file_getprop(file, key)
 //
 //   interprets 'file' as a getprop-style file (key=value pairs, one
@@ -1432,6 +1433,31 @@
     return v;
 }
 
+// write_value(value, filename)
+//   Writes 'value' to 'filename'.
+//   Example: write_value("960000", "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq")
+Value* WriteValueFn(const char* name, State* state, int argc, Expr* argv[]) {
+    if (argc != 2) {
+        return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
+    }
+
+    char* value;
+    char* filename;
+    if (ReadArgs(state, argv, 2, &value, &filename) < 0) {
+        return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)",
+                          name);
+    }
+
+    bool ret = android::base::WriteStringToFile(value, filename);
+    if (!ret) {
+        printf("%s: Failed to write to \"%s\": %s\n", name, filename, strerror(errno));
+    }
+
+    free(value);
+    free(filename);
+    return StringValue(strdup(ret ? "t" : ""));
+}
+
 // 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
@@ -1638,6 +1664,7 @@
     RegisterFunction("read_file", ReadFileFn);
     RegisterFunction("sha1_check", Sha1CheckFn);
     RegisterFunction("rename", RenameFn);
+    RegisterFunction("write_value", WriteValueFn);
 
     RegisterFunction("wipe_cache", WipeCacheFn);
 
diff --git a/verifier.cpp b/verifier.cpp
index c4cd612..229cc9d 100644
--- a/verifier.cpp
+++ b/verifier.cpp
@@ -154,6 +154,12 @@
     LOGI("comment is %zu bytes; signature %zu bytes from end\n",
          comment_size, signature_start);
 
+    if (signature_start > comment_size) {
+        LOGE("signature start: %zu is larger than comment size: %zu\n", signature_start,
+             comment_size);
+        return VERIFY_FAILURE;
+    }
+
     if (signature_start <= FOOTER_SIZE) {
         LOGE("Signature start is in the footer");
         return VERIFY_FAILURE;
diff --git a/wear_ui.cpp b/wear_ui.cpp
index b437fd0..660a078 100644
--- a/wear_ui.cpp
+++ b/wear_ui.cpp
@@ -47,32 +47,13 @@
 }
 
 WearRecoveryUI::WearRecoveryUI() :
-    progress_bar_height(3),
-    progress_bar_width(200),
     progress_bar_y(259),
     outer_height(0),
     outer_width(0),
-    menu_unusable_rows(0),
-    intro_frames(22),
-    loop_frames(60),
-    animation_fps(30),
-    currentIcon(NONE),
-    intro_done(false),
-    current_frame(0),
-    progressBarType(EMPTY),
-    progressScopeStart(0),
-    progressScopeSize(0),
-    progress(0),
-    text_cols(0),
-    text_rows(0),
-    text_col(0),
-    text_row(0),
-    text_top(0),
-    show_text(false),
-    show_text_ever(false),
-    show_menu(false),
-    menu_items(0),
-    menu_sel(0) {
+    menu_unusable_rows(0) {
+    intro_frames = 22;
+    loop_frames = 60;
+    animation_fps = 30;
 
     for (size_t i = 0; i < 5; i++)
         backgroundIcon[i] = NULL;
@@ -80,16 +61,22 @@
     self = this;
 }
 
+int WearRecoveryUI::GetProgressBaseline() {
+    return progress_bar_y;
+}
+
 // Draw background frame on the screen.  Does not flip pages.
 // Should only be called with updateMutex locked.
-void WearRecoveryUI::draw_background_locked(Icon icon)
+// TODO merge drawing routines with screen_ui
+void WearRecoveryUI::draw_background_locked()
 {
+    pagesIdentical = false;
     gr_color(0, 0, 0, 255);
     gr_fill(0, 0, gr_fb_width(), gr_fb_height());
 
-    if (icon) {
+    if (currentIcon != NONE) {
         GRSurface* surface;
-        if (icon == INSTALLING_UPDATE || icon == ERASING) {
+        if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) {
             if (!intro_done) {
                 surface = introFrames[current_frame];
             } else {
@@ -97,7 +84,7 @@
             }
         }
         else {
-            surface = backgroundIcon[icon];
+            surface = backgroundIcon[currentIcon];
         }
 
         int width = gr_get_width(surface);
@@ -110,36 +97,6 @@
     }
 }
 
-// Draw the progress bar (if any) on the screen.  Does not flip pages.
-// Should only be called with updateMutex locked.
-void WearRecoveryUI::draw_progress_locked()
-{
-    if (currentIcon == ERROR) return;
-    if (progressBarType != DETERMINATE) return;
-
-    int width = progress_bar_width;
-    int height = progress_bar_height;
-    int dx = (gr_fb_width() - width)/2;
-    int dy = progress_bar_y;
-
-    float p = progressScopeStart + progress * progressScopeSize;
-    int pos = (int) (p * width);
-
-    gr_color(0x43, 0x43, 0x43, 0xff);
-    gr_fill(dx, dy, dx + width, dy + height);
-
-    if (pos > 0) {
-        gr_color(0x02, 0xa8, 0xf3, 255);
-        if (rtl_locale) {
-            // Fill the progress bar from right to left.
-            gr_fill(dx + width - pos, dy, dx + width, dy + height);
-        } else {
-            // Fill the progress bar from left to right.
-            gr_fill(dx, dy, dx + pos, dy + height);
-        }
-    }
-}
-
 static const char* HEADERS[] = {
     "Swipe up/down to move.",
     "Swipe left/right to select.",
@@ -147,13 +104,15 @@
     NULL
 };
 
+// TODO merge drawing routines with screen_ui
 void WearRecoveryUI::draw_screen_locked()
 {
-    draw_background_locked(currentIcon);
-    draw_progress_locked();
     char cur_selection_str[50];
 
-    if (show_text) {
+    draw_background_locked();
+    if (!show_text) {
+        draw_foreground_locked();
+    } else {
         SetColor(TEXT_FILL);
         gr_fill(0, 0, gr_fb_width(), gr_fb_height());
 
@@ -177,7 +136,7 @@
             // items don't fit on the screen.
             if (menu_items > menu_end - menu_start) {
                 sprintf(cur_selection_str, "Current item: %d/%d", menu_sel + 1, menu_items);
-                gr_text(x+4, y, cur_selection_str, 1);
+                gr_text(gr_sys_font(), x+4, y, cur_selection_str, 1);
                 y += char_height_+4;
             }
 
@@ -192,10 +151,12 @@
                     gr_fill(x, y-2, gr_fb_width()-x, y+char_height_+2);
                     // white text of selected item
                     SetColor(MENU_SEL_FG);
-                    if (menu[i][0]) gr_text(x+4, y, menu[i], 1);
+                    if (menu_[i][0]) {
+                        gr_text(gr_sys_font(), x + 4, y, menu_[i], 1);
+                    }
                     SetColor(MENU);
-                } else {
-                    if (menu[i][0]) gr_text(x+4, y, menu[i], 0);
+                } else if (menu_[i][0]) {
+                    gr_text(gr_sys_font(), x + 4, y, menu_[i], 0);
                 }
                 y += char_height_+4;
             }
@@ -211,163 +172,43 @@
         // 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;
+        int row = (text_top_ + text_rows_ - 1) % text_rows_;
         size_t count = 0;
         for (int ty = gr_fb_height() - char_height_ - outer_height;
-             ty > y+2 && count < text_rows;
+             ty > y + 2 && count < text_rows_;
              ty -= char_height_, ++count) {
-            gr_text(x+4, ty, text[row], 0);
+            gr_text(gr_sys_font(), x+4, ty, text_[row], 0);
             --row;
-            if (row < 0) row = text_rows-1;
+            if (row < 0) row = text_rows_ - 1;
         }
     }
 }
 
-void WearRecoveryUI::update_screen_locked()
-{
+// TODO merge drawing routines with screen_ui
+void WearRecoveryUI::update_progress_locked() {
     draw_screen_locked();
     gr_flip();
 }
 
-// Keeps the progress bar updated, even when the process is otherwise busy.
-void* WearRecoveryUI::progress_thread(void *cookie) {
-    self->progress_loop();
-    return NULL;
-}
+void WearRecoveryUI::InitTextParams() {
+    ScreenRecoveryUI::InitTextParams();
 
-void WearRecoveryUI::progress_loop() {
-    double interval = 1.0 / animation_fps;
-    for (;;) {
-        double start = now();
-        pthread_mutex_lock(&updateMutex);
-        int redraw = 0;
+    text_cols_ = (gr_fb_width() - (outer_width * 2)) / char_width_;
 
-        if ((currentIcon == INSTALLING_UPDATE || currentIcon == ERASING)
-                                                            && !show_text) {
-            if (!intro_done) {
-                if (current_frame >= intro_frames - 1) {
-                    intro_done = true;
-                    current_frame = 0;
-                } else {
-                    current_frame++;
-                }
-            } else {
-                current_frame = (current_frame + 1) % loop_frames;
-            }
-            redraw = 1;
-        }
+    if (text_rows_ > kMaxRows) text_rows_ = kMaxRows;
+    if (text_cols_ > kMaxCols) text_cols_ = kMaxCols;
 
-        // move the progress bar forward on timed intervals, if configured
-        int duration = progressScopeDuration;
-        if (progressBarType == DETERMINATE && duration > 0) {
-            double elapsed = now() - progressScopeTime;
-            float p = 1.0 * elapsed / duration;
-            if (p > 1.0) p = 1.0;
-            if (p > progress) {
-                progress = p;
-                redraw = 1;
-            }
-        }
-
-        if (redraw)
-            update_screen_locked();
-
-        pthread_mutex_unlock(&updateMutex);
-        double end = now();
-        // minimum of 20ms delay between frames
-        double delay = interval - (end-start);
-        if (delay < 0.02) delay = 0.02;
-        usleep((long)(delay * 1000000));
-    }
-}
-
-void WearRecoveryUI::Init()
-{
-    gr_init();
-
-    gr_font_size(&char_width_, &char_height_);
-
-    text_col = text_row = 0;
-    text_rows = (gr_fb_height()) / char_height_;
     visible_text_rows = (gr_fb_height() - (outer_height * 2)) / char_height_;
-    if (text_rows > kMaxRows) text_rows = kMaxRows;
-    text_top = 1;
+}
 
-    text_cols = (gr_fb_width() - (outer_width * 2)) / char_width_;
-    if (text_cols > kMaxCols - 1) text_cols = kMaxCols - 1;
+void WearRecoveryUI::Init() {
+    ScreenRecoveryUI::Init();
 
-    LoadBitmap("icon_installing", &backgroundIcon[INSTALLING_UPDATE]);
-    backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE];
     LoadBitmap("icon_error", &backgroundIcon[ERROR]);
     backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR];
 
-    introFrames = (GRSurface**)malloc(intro_frames * sizeof(GRSurface*));
-    for (int i = 0; i < intro_frames; ++i) {
-        char filename[40];
-        sprintf(filename, "intro%02d", i);
-        LoadBitmap(filename, introFrames + i);
-    }
-
-    loopFrames = (GRSurface**)malloc(loop_frames * sizeof(GRSurface*));
-    for (int i = 0; i < loop_frames; ++i) {
-        char filename[40];
-        sprintf(filename, "loop%02d", i);
-        LoadBitmap(filename, loopFrames + i);
-    }
-
-    pthread_create(&progress_t, NULL, progress_thread, NULL);
-    RecoveryUI::Init();
-}
-
-void WearRecoveryUI::SetBackground(Icon icon)
-{
-    pthread_mutex_lock(&updateMutex);
-    currentIcon = icon;
-    update_screen_locked();
-    pthread_mutex_unlock(&updateMutex);
-}
-
-void WearRecoveryUI::SetProgressType(ProgressType type)
-{
-    pthread_mutex_lock(&updateMutex);
-    if (progressBarType != type) {
-        progressBarType = type;
-    }
-    progressScopeStart = 0;
-    progressScopeSize = 0;
-    progress = 0;
-    update_screen_locked();
-    pthread_mutex_unlock(&updateMutex);
-}
-
-void WearRecoveryUI::ShowProgress(float portion, float seconds)
-{
-    pthread_mutex_lock(&updateMutex);
-    progressBarType = DETERMINATE;
-    progressScopeStart += progressScopeSize;
-    progressScopeSize = portion;
-    progressScopeTime = now();
-    progressScopeDuration = seconds;
-    progress = 0;
-    update_screen_locked();
-    pthread_mutex_unlock(&updateMutex);
-}
-
-void WearRecoveryUI::SetProgress(float fraction)
-{
-    pthread_mutex_lock(&updateMutex);
-    if (fraction < 0.0) fraction = 0.0;
-    if (fraction > 1.0) fraction = 1.0;
-    if (progressBarType == DETERMINATE && fraction > progress) {
-        // Skip updates that aren't visibly different.
-        int width = progress_bar_width;
-        float scale = width * progressScopeSize;
-        if ((int) (progress * scale) != (int) (fraction * scale)) {
-            progress = fraction;
-            update_screen_locked();
-        }
-    }
-    pthread_mutex_unlock(&updateMutex);
+    // This leaves backgroundIcon[INSTALLING_UPDATE] and backgroundIcon[ERASING]
+    // as NULL which is fine since draw_background_locked() doesn't use them.
 }
 
 void WearRecoveryUI::SetStage(int current, int max)
@@ -386,40 +227,40 @@
 
     // This can get called before ui_init(), so be careful.
     pthread_mutex_lock(&updateMutex);
-    if (text_rows > 0 && text_cols > 0) {
+    if (text_rows_ > 0 && text_cols_ > 0) {
         char *ptr;
         for (ptr = buf; *ptr != '\0'; ++ptr) {
-            if (*ptr == '\n' || text_col >= text_cols) {
-                text[text_row][text_col] = '\0';
-                text_col = 0;
-                text_row = (text_row + 1) % text_rows;
-                if (text_row == text_top) text_top = (text_top + 1) % text_rows;
+            if (*ptr == '\n' || text_col_ >= text_cols_) {
+                text_[text_row_][text_col_] = '\0';
+                text_col_ = 0;
+                text_row_ = (text_row_ + 1) % text_rows_;
+                if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_;
             }
-            if (*ptr != '\n') text[text_row][text_col++] = *ptr;
+            if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
         }
-        text[text_row][text_col] = '\0';
+        text_[text_row_][text_col_] = '\0';
         update_screen_locked();
     }
     pthread_mutex_unlock(&updateMutex);
 }
 
 void WearRecoveryUI::StartMenu(const char* const * headers, const char* const * items,
-                                 int initial_selection) {
+                               int initial_selection) {
     pthread_mutex_lock(&updateMutex);
-    if (text_rows > 0 && text_cols > 0) {
+    if (text_rows_ > 0 && text_cols_ > 0) {
         menu_headers_ = headers;
         size_t i = 0;
-        // "i < text_rows" is removed from the loop termination condition,
+        // "i < text_rows_" is removed from the loop termination condition,
         // which is different from the one in ScreenRecoveryUI::StartMenu().
         // Because WearRecoveryUI supports scrollable menu, it's fine to have
-        // more entries than text_rows. The menu may be truncated otherwise.
+        // more entries than text_rows_. The menu may be truncated otherwise.
         // Bug: 23752519
         for (; items[i] != nullptr; i++) {
-            strncpy(menu[i], items[i], text_cols - 1);
-            menu[i][text_cols - 1] = '\0';
+            strncpy(menu_[i], items[i], text_cols_ - 1);
+            menu_[i][text_cols_ - 1] = '\0';
         }
         menu_items = i;
-        show_menu = 1;
+        show_menu = true;
         menu_sel = initial_selection;
         menu_start = 0;
         menu_end = visible_text_rows - 1 - menu_unusable_rows;
@@ -433,7 +274,7 @@
 int WearRecoveryUI::SelectMenu(int sel) {
     int old_sel;
     pthread_mutex_lock(&updateMutex);
-    if (show_menu > 0) {
+    if (show_menu) {
         old_sel = menu_sel;
         menu_sel = sel;
         if (menu_sel < 0) menu_sel = 0;
@@ -452,53 +293,6 @@
     return sel;
 }
 
-void WearRecoveryUI::EndMenu() {
-    int i;
-    pthread_mutex_lock(&updateMutex);
-    if (show_menu > 0 && text_rows > 0 && text_cols > 0) {
-        show_menu = 0;
-        update_screen_locked();
-    }
-    pthread_mutex_unlock(&updateMutex);
-}
-
-bool WearRecoveryUI::IsTextVisible()
-{
-    pthread_mutex_lock(&updateMutex);
-    int visible = show_text;
-    pthread_mutex_unlock(&updateMutex);
-    return visible;
-}
-
-bool WearRecoveryUI::WasTextEverVisible()
-{
-    pthread_mutex_lock(&updateMutex);
-    int ever_visible = show_text_ever;
-    pthread_mutex_unlock(&updateMutex);
-    return ever_visible;
-}
-
-void WearRecoveryUI::ShowText(bool visible)
-{
-    pthread_mutex_lock(&updateMutex);
-    // Don't show text during ota install or factory reset
-    if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) {
-        pthread_mutex_unlock(&updateMutex);
-        return;
-    }
-    show_text = visible;
-    if (show_text) show_text_ever = 1;
-    update_screen_locked();
-    pthread_mutex_unlock(&updateMutex);
-}
-
-void WearRecoveryUI::Redraw()
-{
-    pthread_mutex_lock(&updateMutex);
-    update_screen_locked();
-    pthread_mutex_unlock(&updateMutex);
-}
-
 void WearRecoveryUI::ShowFile(FILE* fp) {
     std::vector<long> offsets;
     offsets.push_back(ftell(fp));
@@ -538,12 +332,12 @@
 
         int ch = getc(fp);
         if (ch == EOF) {
-            text_row = text_top = text_rows - 2;
+            text_row_ = text_top_ = text_rows_ - 2;
             show_prompt = true;
         } else {
             PutChar(ch);
-            if (text_col == 0 && text_row >= text_rows - 2) {
-                text_top = text_row;
+            if (text_col_ == 0 && text_row_ >= text_rows_ - 2) {
+                text_top_ = text_row_;
                 show_prompt = true;
             }
         }
@@ -552,10 +346,10 @@
 
 void WearRecoveryUI::PutChar(char ch) {
     pthread_mutex_lock(&updateMutex);
-    if (ch != '\n') text[text_row][text_col++] = ch;
-    if (ch == '\n' || text_col >= text_cols) {
-        text_col = 0;
-        ++text_row;
+    if (ch != '\n') text_[text_row_][text_col_++] = ch;
+    if (ch == '\n' || text_col_ >= text_cols_) {
+        text_col_ = 0;
+        ++text_row_;
     }
     pthread_mutex_unlock(&updateMutex);
 }
@@ -572,11 +366,11 @@
 
 void WearRecoveryUI::ClearText() {
     pthread_mutex_lock(&updateMutex);
-    text_col = 0;
-    text_row = 0;
-    text_top = 1;
-    for (size_t i = 0; i < text_rows; ++i) {
-        memset(text[i], 0, text_cols + 1);
+    text_col_ = 0;
+    text_row_ = 0;
+    text_top_ = 1;
+    for (size_t i = 0; i < text_rows_; ++i) {
+        memset(text_[i], 0, text_cols_ + 1);
     }
     pthread_mutex_unlock(&updateMutex);
 }
@@ -597,17 +391,17 @@
     }
 
     pthread_mutex_lock(&updateMutex);
-    if (text_rows > 0 && text_cols > 0) {
+    if (text_rows_ > 0 && text_cols_ > 0) {
         for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) {
-            if (*ptr == '\n' || text_col >= text_cols) {
-                text[text_row][text_col] = '\0';
-                text_col = 0;
-                text_row = (text_row + 1) % text_rows;
-                if (text_row == text_top) text_top = (text_top + 1) % text_rows;
+            if (*ptr == '\n' || text_col_ >= text_cols_) {
+                text_[text_row_][text_col_] = '\0';
+                text_col_ = 0;
+                text_row_ = (text_row_ + 1) % text_rows_;
+                if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_;
             }
-            if (*ptr != '\n') text[text_row][text_col++] = *ptr;
+            if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
         }
-        text[text_row][text_col] = '\0';
+        text_[text_row_][text_col_] = '\0';
         update_screen_locked();
     }
     pthread_mutex_unlock(&updateMutex);
diff --git a/wear_ui.h b/wear_ui.h
index e2d6fe0..9351d41 100644
--- a/wear_ui.h
+++ b/wear_ui.h
@@ -23,39 +23,22 @@
   public:
     WearRecoveryUI();
 
-    void Init();
-    // overall recovery state ("background image")
-    void SetBackground(Icon icon);
+    void Init() override;
 
-    // progress indicator
-    void SetProgressType(ProgressType type);
-    void ShowProgress(float portion, float seconds);
-    void SetProgress(float fraction);
-
-    void SetStage(int current, int max);
-
-    // text log
-    void ShowText(bool visible);
-    bool IsTextVisible();
-    bool WasTextEverVisible();
+    void SetStage(int current, int max) override;
 
     // printing messages
-    void Print(const char* fmt, ...);
-    void PrintOnScreenOnly(const char* fmt, ...) __printflike(2, 3);
-    void ShowFile(const char* filename);
-    void ShowFile(FILE* fp);
+    void Print(const char* fmt, ...) override;
+    void PrintOnScreenOnly(const char* fmt, ...) override __printflike(2, 3);
+    void ShowFile(const char* filename) override;
+    void ShowFile(FILE* fp) override;
 
     // menu display
     void StartMenu(const char* const * headers, const char* const * items,
-                           int initial_selection);
-    int SelectMenu(int sel);
-    void EndMenu();
-
-    void Redraw();
+                   int initial_selection) override;
+    int SelectMenu(int sel) override;
 
   protected:
-    int progress_bar_height, progress_bar_width;
-
     // progress bar vertical position, it's centered horizontally
     int progress_bar_y;
 
@@ -67,59 +50,34 @@
     // that may otherwise go out of the screen.
     int menu_unusable_rows;
 
-    // number of intro frames (default: 22) and loop frames (default: 60)
-    int intro_frames;
-    int loop_frames;
+    int GetProgressBaseline() override;
 
-    // Number of frames per sec (default: 30) for both of intro and loop.
-    int animation_fps;
+    void InitTextParams() override;
+
+    void update_progress_locked() override;
+
+    void PrintV(const char*, bool, va_list) override;
 
   private:
-    Icon currentIcon;
-
-    bool intro_done;
-
-    int current_frame;
-
     GRSurface* backgroundIcon[5];
-    GRSurface* *introFrames;
-    GRSurface* *loopFrames;
-
-    ProgressType progressBarType;
-
-    float progressScopeStart, progressScopeSize, progress;
-    double progressScopeTime, progressScopeDuration;
 
     static const int kMaxCols = 96;
     static const int kMaxRows = 96;
 
-    // Log text overlay, displayed when a magic key is pressed
-    char text[kMaxRows][kMaxCols];
-    size_t text_cols, text_rows;
     // Number of text rows seen on screen
     int visible_text_rows;
-    size_t text_col, text_row, text_top;
-    bool show_text;
-    bool show_text_ever;   // has show_text ever been true?
 
-    char menu[kMaxRows][kMaxCols];
-    bool show_menu;
     const char* const* menu_headers_;
-    int menu_items, menu_sel;
     int menu_start, menu_end;
 
     pthread_t progress_t;
 
-  private:
-    void draw_background_locked(Icon icon);
+    void draw_background_locked() override;
+    void draw_screen_locked() override;
     void draw_progress_locked();
-    void draw_screen_locked();
-    void update_screen_locked();
-    static void* progress_thread(void* cookie);
-    void progress_loop();
+
     void PutChar(char);
     void ClearText();
-    void PrintV(const char*, bool, va_list);
 };
 
 #endif  // RECOVERY_WEAR_UI_H