am 1117c69f: am 7172eb7b: Merge "Mute unharmful build warning at the top of the build log:" into gingerbread

* commit '1117c69fe40dccfa6090d184094c4a6d407ab8d6':
  Mute unharmful build warning at the top of the build log:
diff --git a/Android.mk b/Android.mk
index e6c3547..508eb4c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -1,5 +1,4 @@
 ifneq ($(TARGET_SIMULATOR),true)
-ifeq ($(TARGET_ARCH),arm)
 
 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
@@ -12,8 +11,7 @@
     install.c \
     roots.c \
     ui.c \
-    verifier.c \
-    encryptedfs_provisioning.c
+    verifier.c
 
 LOCAL_MODULE := recovery
 
@@ -22,6 +20,14 @@
 RECOVERY_API_VERSION := 3
 LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
 
+LOCAL_STATIC_LIBRARIES :=
+
+ifeq ($(TARGET_USERIMAGES_USE_EXT4), true)
+LOCAL_CFLAGS += -DUSE_EXT4
+LOCAL_C_INCLUDES += system/extras/ext4_utils
+LOCAL_STATIC_LIBRARIES += libext4_utils libz
+endif
+
 # This binary is in the recovery ramdisk, which is otherwise a copy of root.
 # It gets copied there in config/Makefile.  LOCAL_MODULE_TAGS suppresses
 # a (redundant) copy of the binary in /system/bin for user builds.
@@ -29,7 +35,6 @@
 
 LOCAL_MODULE_TAGS := eng
 
-LOCAL_STATIC_LIBRARIES :=
 ifeq ($(TARGET_RECOVERY_UI_LIB),)
   LOCAL_SRC_FILES += default_recovery_ui.c
 else
@@ -61,6 +66,7 @@
 
 
 include $(commands_recovery_local_path)/minui/Android.mk
+include $(commands_recovery_local_path)/minelf/Android.mk
 include $(commands_recovery_local_path)/minzip/Android.mk
 include $(commands_recovery_local_path)/mtdutils/Android.mk
 include $(commands_recovery_local_path)/tools/Android.mk
@@ -69,6 +75,4 @@
 include $(commands_recovery_local_path)/applypatch/Android.mk
 commands_recovery_local_path :=
 
-endif   # TARGET_ARCH == arm
 endif    # !TARGET_SIMULATOR
-
diff --git a/applypatch/Android.mk b/applypatch/Android.mk
index e91e4bf..2848b51 100644
--- a/applypatch/Android.mk
+++ b/applypatch/Android.mk
@@ -14,7 +14,6 @@
 
 ifneq ($(TARGET_SIMULATOR),true)
 
-ifeq ($(TARGET_ARCH),arm)
 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
@@ -31,7 +30,7 @@
 LOCAL_SRC_FILES := main.c
 LOCAL_MODULE := applypatch
 LOCAL_C_INCLUDES += bootable/recovery
-LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz
+LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz libminelf
 LOCAL_SHARED_LIBRARIES += libz libcutils libstdc++ libc
 
 include $(BUILD_EXECUTABLE)
@@ -43,7 +42,7 @@
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 LOCAL_MODULE_TAGS := eng
 LOCAL_C_INCLUDES += bootable/recovery
-LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz
+LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz libminelf
 LOCAL_STATIC_LIBRARIES += libz libcutils libstdc++ libc
 
 include $(BUILD_EXECUTABLE)
@@ -59,5 +58,4 @@
 
 include $(BUILD_HOST_EXECUTABLE)
 
-endif   # TARGET_ARCH == arm
 endif  # !TARGET_SIMULATOR
diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c
index fd8153a..1060913 100644
--- a/applypatch/applypatch.c
+++ b/applypatch/applypatch.c
@@ -30,16 +30,21 @@
 #include "mtdutils/mtdutils.h"
 #include "edify/expr.h"
 
-static int SaveFileContents(const char* filename, FileContents file);
+int SaveFileContents(const char* filename, FileContents file);
 static int LoadPartitionContents(const char* filename, FileContents* file);
 int ParseSha1(const char* str, uint8_t* digest);
 static ssize_t FileSink(unsigned char* data, ssize_t len, void* token);
 
 static int mtd_partitions_scanned = 0;
 
-// Read a file into memory; store it and its associated metadata in
-// *file.  Return 0 on success.
-int LoadFileContents(const char* filename, FileContents* file) {
+// Read a file into memory; optionally (retouch_flag == RETOUCH_DO_MASK) mask
+// the retouched entries back to their original value (such that SHA-1 checks
+// don't fail due to randomization); store the file contents and associated
+// metadata in *file.
+//
+// Return 0 on success.
+int LoadFileContents(const char* filename, FileContents* file,
+                     int retouch_flag) {
     file->data = NULL;
 
     // A special 'filename' beginning with "MTD:" or "EMMC:" means to
@@ -75,6 +80,20 @@
     }
     fclose(f);
 
+    // apply_patch[_check] functions are blind to randomization. Randomization
+    // is taken care of in [Undo]RetouchBinariesFn. If there is a mismatch
+    // within a file, this means the file is assumed "corrupt" for simplicity.
+    if (retouch_flag) {
+        int32_t desired_offset = 0;
+        if (retouch_mask_data(file->data, file->size,
+                              &desired_offset, NULL) != RETOUCH_DATA_MATCHED) {
+            printf("error trying to mask retouch entries\n");
+            free(file->data);
+            file->data = NULL;
+            return -1;
+        }
+    }
+
     SHA(file->data, file->size, file->sha1);
     return 0;
 }
@@ -303,7 +322,7 @@
 
 // Save the contents of the given FileContents object under the given
 // filename.  Return 0 on success.
-static int SaveFileContents(const char* filename, FileContents file) {
+int SaveFileContents(const char* filename, FileContents file) {
     int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC);
     if (fd < 0) {
         printf("failed to open \"%s\" for write: %s\n",
@@ -477,7 +496,7 @@
     // LoadFileContents is successful.  (Useful for reading
     // partitions, where the filename encodes the sha1s; no need to
     // check them twice.)
-    if (LoadFileContents(filename, &file) != 0 ||
+    if (LoadFileContents(filename, &file, RETOUCH_DO_MASK) != 0 ||
         (num_patches > 0 &&
          FindMatchingPatch(file.sha1, patch_sha1_str, num_patches) < 0)) {
         printf("file \"%s\" doesn't have any of expected "
@@ -491,7 +510,7 @@
         // exists and matches the sha1 we're looking for, the check still
         // passes.
 
-        if (LoadFileContents(CACHE_TEMP_SOURCE, &file) != 0) {
+        if (LoadFileContents(CACHE_TEMP_SOURCE, &file, RETOUCH_DO_MASK) != 0) {
             printf("failed to load cache file\n");
             return 1;
         }
@@ -617,7 +636,8 @@
     int made_copy = 0;
 
     // We try to load the target file into the source_file object.
-    if (LoadFileContents(target_filename, &source_file) == 0) {
+    if (LoadFileContents(target_filename, &source_file,
+                         RETOUCH_DO_MASK) == 0) {
         if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_SIZE) == 0) {
             // The early-exit case:  the patch was already applied, this file
             // has the desired hash, nothing for us to do.
@@ -633,7 +653,8 @@
         // Need to load the source file:  either we failed to load the
         // target file, or we did but it's different from the source file.
         free(source_file.data);
-        LoadFileContents(source_filename, &source_file);
+        LoadFileContents(source_filename, &source_file,
+                         RETOUCH_DO_MASK);
     }
 
     if (source_file.data != NULL) {
@@ -648,7 +669,8 @@
         free(source_file.data);
         printf("source file is bad; trying copy\n");
 
-        if (LoadFileContents(CACHE_TEMP_SOURCE, &copy_file) < 0) {
+        if (LoadFileContents(CACHE_TEMP_SOURCE, &copy_file,
+                             RETOUCH_DO_MASK) < 0) {
             // fail.
             printf("failed to read copy file\n");
             return 1;
diff --git a/applypatch/applypatch.h b/applypatch/applypatch.h
index 10c0125..a78c89b 100644
--- a/applypatch/applypatch.h
+++ b/applypatch/applypatch.h
@@ -19,6 +19,7 @@
 
 #include <sys/stat.h>
 #include "mincrypt/sha.h"
+#include "minelf/Retouch.h"
 #include "edify/expr.h"
 
 typedef struct _Patch {
@@ -59,10 +60,12 @@
                      int num_patches,
                      char** const patch_sha1_str);
 
-// Read a file into memory; store it and its associated metadata in
-// *file.  Return 0 on success.
-int LoadFileContents(const char* filename, FileContents* file);
+int LoadFileContents(const char* filename, FileContents* file,
+                     int retouch_flag);
+int SaveFileContents(const char* filename, FileContents file);
 void FreeFileContents(FileContents* file);
+int FindMatchingPatch(uint8_t* sha1, char** const patch_sha1_str,
+                      int num_patches);
 
 // bsdiff.c
 void ShowBSDiffLicense();
diff --git a/applypatch/main.c b/applypatch/main.c
index 3917f86..7025a2e 100644
--- a/applypatch/main.c
+++ b/applypatch/main.c
@@ -74,7 +74,7 @@
             (*patches)[i] = NULL;
         } else {
             FileContents fc;
-            if (LoadFileContents(colon, &fc) != 0) {
+            if (LoadFileContents(colon, &fc, RETOUCH_DONT_MASK) != 0) {
                 goto abort;
             }
             (*patches)[i] = malloc(sizeof(Value));
diff --git a/bootloader.c b/bootloader.c
index b690c55..7096566 100644
--- a/bootloader.c
+++ b/bootloader.c
@@ -22,6 +22,8 @@
 #include <errno.h>
 #include <stdio.h>
 #include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
 
 static int get_bootloader_message_mtd(struct bootloader_message *out, const Volume* v);
 static int set_bootloader_message_mtd(const struct bootloader_message *in, const Volume* v);
@@ -132,8 +134,26 @@
 // for misc partitions on block devices
 // ------------------------------------
 
+static void wait_for_device(const char* fn) {
+    int tries = 0;
+    int ret;
+    struct stat buf;
+    do {
+        ++tries;
+        ret = stat(fn, &buf);
+        if (ret) {
+            printf("stat %s try %d: %s\n", fn, tries, strerror(errno));
+            sleep(1);
+        }
+    } while (ret && tries < 10);
+    if (ret) {
+        printf("failed to stat %s\n", fn);
+    }
+}
+
 static int get_bootloader_message_block(struct bootloader_message *out,
                                         const Volume* v) {
+    wait_for_device(v->device);
     FILE* f = fopen(v->device, "rb");
     if (f == NULL) {
         LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
@@ -155,6 +175,7 @@
 
 static int set_bootloader_message_block(const struct bootloader_message *in,
                                         const Volume* v) {
+    wait_for_device(v->device);
     FILE* f = fopen(v->device, "wb");
     if (f == NULL) {
         LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
diff --git a/common.h b/common.h
index 97e87ee..cba4c86 100644
--- a/common.h
+++ b/common.h
@@ -26,6 +26,7 @@
 int ui_wait_key();            // waits for a key/button press, returns the code
 int ui_key_pressed(int key);  // returns >0 if the code is currently pressed
 int ui_text_visible();        // returns >0 if text log is currently visible
+int ui_text_ever_visible();   // returns >0 if text log was ever visible
 void ui_show_text(int visible);
 void ui_clear_key_queue();
 
@@ -98,6 +99,31 @@
     const char* device2;      // alternative device to try if fs_type
                               // == "ext4" or "vfat" and mounting
                               // 'device' fails
+
+    long long length;         // (ext4 partition only) when
+                              // formatting, size to use for the
+                              // partition.  0 or negative number
+                              // means to format all but the last
+                              // (that much).
 } Volume;
 
+typedef struct {
+    // number of frames in indeterminate progress bar animation
+    int indeterminate_frames;
+
+    // number of frames per second to try to maintain when animating
+    int update_fps;
+
+    // number of frames in installing animation.  may be zero for a
+    // static installation icon.
+    int installing_frames;
+
+    // the install icon is animated by drawing images containing the
+    // changing part over the base icon.  These specify the
+    // coordinates of the upper-left corner.
+    int install_overlay_offset_x;
+    int install_overlay_offset_y;
+
+} UIParameters;
+
 #endif  // RECOVERY_COMMON_H
diff --git a/default_recovery_ui.c b/default_recovery_ui.c
index ce12787..7c4017e 100644
--- a/default_recovery_ui.c
+++ b/default_recovery_ui.c
@@ -24,11 +24,14 @@
                          NULL };
 
 char* MENU_ITEMS[] = { "reboot system now",
-                       "apply update from sdcard",
+                       "apply update from external storage",
                        "wipe data/factory reset",
                        "wipe cache partition",
                        NULL };
 
+void device_ui_init(UIParameters* ui_parameters) {
+}
+
 int device_recovery_start() {
     return 0;
 }
diff --git a/encryptedfs_provisioning.c b/encryptedfs_provisioning.c
deleted file mode 100644
index 601c817..0000000
--- a/encryptedfs_provisioning.c
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "encryptedfs_provisioning.h"
-#include "cutils/misc.h"
-#include "cutils/properties.h"
-#include "common.h"
-#include "mtdutils/mtdutils.h"
-#include "mtdutils/mounts.h"
-#include "roots.h"
-
-const char* encrypted_fs_enabled_property      = "persist.security.secfs.enabled";
-const char* encrypted_fs_property_dir          = "/data/property/";
-const char* encrypted_fs_system_dir            = "/data/system/";
-const char* encrypted_fs_key_file_name         = "/data/fs_key.dat";
-const char* encrypted_fs_salt_file_name        = "/data/hash_salt.dat";
-const char* encrypted_fs_hash_file_src_name    = "/data/system/password.key";
-const char* encrypted_fs_hash_file_dst_name    = "/data/hash.dat";
-const char* encrypted_fs_entropy_file_src_name = "/data/system/entropy.dat";
-const char* encrypted_fs_entropy_file_dst_name = "/data/ported_entropy.dat";
-
-void get_property_file_name(char *buffer, const char *property_name) {
-    sprintf(buffer, "%s%s", encrypted_fs_property_dir, property_name);
-}
-
-int get_binary_file_contents(char *buffer, int buf_size, const char *file_name, int *out_size) {
-    FILE *in_file;
-    int read_bytes;
-
-    in_file = fopen(file_name, "r");
-    if (in_file == NULL) {
-        LOGE("Secure FS: error accessing key file.");
-        return ENCRYPTED_FS_ERROR;
-    }
-
-    read_bytes = fread(buffer, 1, buf_size, in_file);
-    if (out_size == NULL) {
-        if (read_bytes != buf_size) {
-            // Error or unexpected data
-            fclose(in_file);
-            LOGE("Secure FS: error reading conmplete key.");
-            return ENCRYPTED_FS_ERROR;
-        }
-    } else {
-        *out_size = read_bytes;
-    }
-    fclose(in_file);
-    return ENCRYPTED_FS_OK;
-}
-
-int set_binary_file_contents(char *buffer, int buf_size, const char *file_name) {
-    FILE *out_file;
-    int write_bytes;
-
-    out_file = fopen(file_name, "w");
-    if (out_file == NULL) {
-        LOGE("Secure FS: error setting up key file.");
-        return ENCRYPTED_FS_ERROR;
-    }
-
-    write_bytes = fwrite(buffer, 1, buf_size, out_file);
-    if (write_bytes != buf_size) {
-        // Error or unexpected data
-        fclose(out_file);
-        LOGE("Secure FS: error reading conmplete key.");
-        return ENCRYPTED_FS_ERROR;
-    }
-
-    fclose(out_file);
-    return ENCRYPTED_FS_OK;
-}
-
-int get_text_file_contents(char *buffer, int buf_size, char *file_name) {
-    FILE *in_file;
-    char *read_data;
-
-    in_file = fopen(file_name, "r");
-    if (in_file == NULL) {
-        LOGE("Secure FS: error accessing properties.");
-        return ENCRYPTED_FS_ERROR;
-    }
-
-    read_data = fgets(buffer, buf_size, in_file);
-    if (read_data == NULL) {
-        // Error or unexpected data
-        fclose(in_file);
-        LOGE("Secure FS: error accessing properties.");
-        return ENCRYPTED_FS_ERROR;
-    }
-
-    fclose(in_file);
-    return ENCRYPTED_FS_OK;
-}
-
-int set_text_file_contents(char *buffer, char *file_name) {
-    FILE *out_file;
-    int result;
-
-    out_file = fopen(file_name, "w");
-    if (out_file == NULL) {
-        LOGE("Secure FS: error setting up properties.");
-        return ENCRYPTED_FS_ERROR;
-    }
-
-    result = fputs(buffer, out_file);
-    if (result != 0) {
-        // Error or unexpected data
-        fclose(out_file);
-        LOGE("Secure FS: error setting up properties.");
-        return ENCRYPTED_FS_ERROR;
-    }
-
-    fflush(out_file);
-    fclose(out_file);
-    return ENCRYPTED_FS_OK;
-}
-
-int read_encrypted_fs_boolean_property(const char *prop_name, int *value) {
-    char prop_file_name[PROPERTY_KEY_MAX + 32];
-    char prop_value[PROPERTY_VALUE_MAX];
-    int result;
-
-    get_property_file_name(prop_file_name, prop_name);
-    result = get_text_file_contents(prop_value, PROPERTY_VALUE_MAX, prop_file_name);
-
-    if (result < 0) {
-        return result;
-    }
-
-    if (strncmp(prop_value, "1", 1) == 0) {
-        *value = 1;
-    } else if (strncmp(prop_value, "0", 1) == 0) {
-        *value = 0;
-    } else {
-        LOGE("Secure FS: error accessing properties.");
-        return ENCRYPTED_FS_ERROR;
-    }
-
-    return ENCRYPTED_FS_OK;
-}
-
-int write_encrypted_fs_boolean_property(const char *prop_name, int value) {
-    char prop_file_name[PROPERTY_KEY_MAX + 32];
-    char prop_value[PROPERTY_VALUE_MAX];
-    int result;
-
-    get_property_file_name(prop_file_name, prop_name);
-
-    // Create the directory if needed
-    mkdir(encrypted_fs_property_dir, 0755);
-    if (value == 1) {
-        result = set_text_file_contents("1", prop_file_name);
-    } else if (value == 0) {
-        result = set_text_file_contents("0", prop_file_name);
-    } else {
-        return ENCRYPTED_FS_ERROR;
-    }
-    if (result < 0) {
-        return result;
-    }
-
-    return ENCRYPTED_FS_OK;
-}
-
-int read_encrypted_fs_info(encrypted_fs_info *encrypted_fs_data) {
-    int result;
-    int value;
-    result = ensure_path_mounted("/data");
-    if (result != 0) {
-        LOGE("Secure FS: error mounting userdata partition.");
-        return ENCRYPTED_FS_ERROR;
-    }
-
-    // Read the pre-generated encrypted FS key, password hash and salt.
-    result = get_binary_file_contents(encrypted_fs_data->key, ENCRYPTED_FS_KEY_SIZE,
-            encrypted_fs_key_file_name, NULL);
-    if (result != 0) {
-        LOGE("Secure FS: error reading generated file system key.");
-        return ENCRYPTED_FS_ERROR;
-    }
-
-    result = get_binary_file_contents(encrypted_fs_data->salt, ENCRYPTED_FS_SALT_SIZE,
-            encrypted_fs_salt_file_name, &(encrypted_fs_data->salt_length));
-    if (result != 0) {
-        LOGE("Secure FS: error reading file system salt.");
-        return ENCRYPTED_FS_ERROR;
-    }
-
-    result = get_binary_file_contents(encrypted_fs_data->hash, ENCRYPTED_FS_MAX_HASH_SIZE,
-            encrypted_fs_hash_file_src_name, &(encrypted_fs_data->hash_length));
-    if (result != 0) {
-        LOGE("Secure FS: error reading password hash.");
-        return ENCRYPTED_FS_ERROR;
-    }
-
-    result = get_binary_file_contents(encrypted_fs_data->entropy, ENTROPY_MAX_SIZE,
-            encrypted_fs_entropy_file_src_name, &(encrypted_fs_data->entropy_length));
-    if (result != 0) {
-        LOGE("Secure FS: error reading ported entropy.");
-        return ENCRYPTED_FS_ERROR;
-    }
-
-    result = ensure_path_unmounted("/data");
-    if (result != 0) {
-        LOGE("Secure FS: error unmounting data partition.");
-        return ENCRYPTED_FS_ERROR;
-    }
-
-    return ENCRYPTED_FS_OK;
-}
-
-int restore_encrypted_fs_info(encrypted_fs_info *encrypted_fs_data) {
-    int result;
-    result = ensure_path_mounted("/data");
-    if (result != 0) {
-        LOGE("Secure FS: error mounting userdata partition.");
-        return ENCRYPTED_FS_ERROR;
-    }
-
-    // Write the pre-generated secure FS key, password hash and salt.
-    result = set_binary_file_contents(encrypted_fs_data->key, ENCRYPTED_FS_KEY_SIZE,
-            encrypted_fs_key_file_name);
-    if (result != 0) {
-        LOGE("Secure FS: error writing generated file system key.");
-        return ENCRYPTED_FS_ERROR;
-    }
-
-    result = set_binary_file_contents(encrypted_fs_data->salt, encrypted_fs_data->salt_length,
-        encrypted_fs_salt_file_name);
-    if (result != 0) {
-        LOGE("Secure FS: error writing file system salt.");
-        return ENCRYPTED_FS_ERROR;
-    }
-
-    result = set_binary_file_contents(encrypted_fs_data->hash, encrypted_fs_data->hash_length,
-            encrypted_fs_hash_file_dst_name);
-    if (result != 0) {
-        LOGE("Secure FS: error writing password hash.");
-        return ENCRYPTED_FS_ERROR;
-    }
-
-    result = set_binary_file_contents(encrypted_fs_data->entropy, encrypted_fs_data->entropy_length,
-            encrypted_fs_entropy_file_dst_name);
-    if (result != 0) {
-        LOGE("Secure FS: error writing ported entropy.");
-        return ENCRYPTED_FS_ERROR;
-    }
-
-    // Set the secure FS properties to their respective values
-    result = write_encrypted_fs_boolean_property(encrypted_fs_enabled_property, encrypted_fs_data->mode);
-    if (result != 0) {
-        return result;
-    }
-
-    result = ensure_path_unmounted("/data");
-    if (result != 0) {
-        LOGE("Secure FS: error unmounting data partition.");
-        return ENCRYPTED_FS_ERROR;
-    }
-
-    return ENCRYPTED_FS_OK;
-}
diff --git a/encryptedfs_provisioning.h b/encryptedfs_provisioning.h
deleted file mode 100644
index 284605d..0000000
--- a/encryptedfs_provisioning.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-
-#ifndef __ENCRYPTEDFS_PROVISIONING_H__
-#define __ENCRYPTEDFS_PROVISIONING_H__
-
-#define MODE_ENCRYPTED_FS_DISABLED    0
-#define MODE_ENCRYPTED_FS_ENABLED     1
-
-#define ENCRYPTED_FS_OK               0
-#define ENCRYPTED_FS_ERROR          (-1)
-
-#define ENCRYPTED_FS_KEY_SIZE        16
-#define ENCRYPTED_FS_SALT_SIZE       16
-#define ENCRYPTED_FS_MAX_HASH_SIZE  128
-#define ENTROPY_MAX_SIZE        4096
-
-struct encrypted_fs_info {
-    int mode;
-    char key[ENCRYPTED_FS_KEY_SIZE];
-    char salt[ENCRYPTED_FS_SALT_SIZE];
-    int salt_length;
-    char hash[ENCRYPTED_FS_MAX_HASH_SIZE];
-    int hash_length;
-    char entropy[ENTROPY_MAX_SIZE];
-    int entropy_length;
-};
-
-typedef struct encrypted_fs_info encrypted_fs_info;
-
-int read_encrypted_fs_info(encrypted_fs_info *secure_fs_data);
-
-int restore_encrypted_fs_info(encrypted_fs_info *secure_data);
-
-#endif /* __ENCRYPTEDFS_PROVISIONING_H__ */
-
diff --git a/make-overlay.py b/make-overlay.py
new file mode 100644
index 0000000..7f931b3
--- /dev/null
+++ b/make-overlay.py
@@ -0,0 +1,102 @@
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Script to take a set of frames (PNG files) for a recovery
+"installing" icon animation and turn it into a base image plus a set
+of overlays, as needed by the recovery UI code.  Run with the names of
+all the input frames on the command line, in order."""
+
+import sys
+try:
+  import Image
+except ImportError:
+  print "This script requires the Python Imaging Library to be installed."
+  sys.exit(1)
+
+# Find the smallest box that contains all the pixels which change
+# between images.
+
+print "reading", sys.argv[1]
+base = Image.open(sys.argv[1])
+
+minmini = base.size[0]-1
+maxmaxi = 0
+minminj = base.size[1]-1
+maxmaxj = 0
+
+for top_name in sys.argv[2:]:
+  print "reading", top_name
+  top = Image.open(top_name)
+
+  assert base.size == top.size
+
+  mini = base.size[0]-1
+  maxi = 0
+  minj = base.size[1]-1
+  maxj = 0
+
+  h, w = base.size
+  for j in range(w):
+    for i in range(h):
+      b = base.getpixel((i,j))
+      t = top.getpixel((i,j))
+      if b != t:
+        if i < mini: mini = i
+        if i > maxi: maxi = i
+        if j < minj: minj = j
+        if j > maxj: maxj = j
+
+  minmini = min(minmini, mini)
+  maxmaxi = max(maxmaxi, maxi)
+  minminj = min(minminj, minj)
+  maxmaxj = max(maxmaxj, maxj)
+
+w = maxmaxi - minmini + 1
+h = maxmaxj - minminj + 1
+
+# Now write out an image containing just that box, for each frame.
+
+for num, top_name in enumerate(sys.argv[1:]):
+  top = Image.open(top_name)
+
+  out = Image.new("RGB", (w, h))
+  for i in range(w):
+    for j in range(h):
+      t = top.getpixel((i+minmini, j+minminj))
+      out.putpixel((i, j), t)
+
+  fn = "icon_installing_overlay%02d.png" % (num+1,)
+  out.save(fn)
+  print "saved", fn
+
+# Write out the base icon, which is the first frame with that box
+# blacked out (just to make the file smaller, since it's always
+# displayed with one of the overlays on top of it).
+
+for i in range(w):
+  for j in range(h):
+    base.putpixel((i+minmini, j+minminj), (0, 0, 0))
+fn = "icon_installing.png"
+base.save(fn)
+print "saved", fn
+
+# The device_ui_init() function needs to tell the recovery UI the
+# position of the overlay box.
+
+print
+print "add this to your device_ui_init() function:"
+print "-" * 40
+print "  ui_parameters->install_overlay_offset_x = %d;" % (minmini,)
+print "  ui_parameters->install_overlay_offset_y = %d;" % (minminj,)
+print "-" * 40
diff --git a/minelf/Android.mk b/minelf/Android.mk
new file mode 100644
index 0000000..0f41ff5
--- /dev/null
+++ b/minelf/Android.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	Retouch.c
+
+LOCAL_C_INCLUDES += bootable/recovery
+
+LOCAL_MODULE := libminelf
+
+LOCAL_CFLAGS += -Wall
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/minelf/Retouch.c b/minelf/Retouch.c
new file mode 100644
index 0000000..33809cd
--- /dev/null
+++ b/minelf/Retouch.c
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <strings.h>
+#include "Retouch.h"
+#include "applypatch/applypatch.h"
+
+typedef struct {
+    int32_t mmap_addr;
+    char tag[4]; /* 'P', 'R', 'E', ' ' */
+} prelink_info_t __attribute__((packed));
+
+#define false 0
+#define true 1
+
+static int32_t offs_prev;
+static uint32_t cont_prev;
+
+static void init_compression_state(void) {
+    offs_prev = 0;
+    cont_prev = 0;
+}
+
+// For details on the encoding used for relocation lists, please
+// refer to build/tools/retouch/retouch-prepare.c. The intent is to
+// save space by removing most of the inherent redundancy.
+
+static void decode_bytes(uint8_t *encoded_bytes, int encoded_size,
+                         int32_t *dst_offset, uint32_t *dst_contents) {
+    if (encoded_size == 2) {
+        *dst_offset = offs_prev + (((encoded_bytes[0]&0x60)>>5)+1)*4;
+
+        // if the original was negative, we need to 1-pad before applying delta
+        int32_t tmp = (((encoded_bytes[0] & 0x0000001f) << 8) |
+                       encoded_bytes[1]);
+        if (tmp & 0x1000) tmp = 0xffffe000 | tmp;
+        *dst_contents = cont_prev + tmp;
+    } else if (encoded_size == 3) {
+        *dst_offset = offs_prev + (((encoded_bytes[0]&0x30)>>4)+1)*4;
+
+        // if the original was negative, we need to 1-pad before applying delta
+        int32_t tmp = (((encoded_bytes[0] & 0x0000000f) << 16) |
+                       (encoded_bytes[1] << 8) |
+                       encoded_bytes[2]);
+        if (tmp & 0x80000) tmp = 0xfff00000 | tmp;
+        *dst_contents = cont_prev + tmp;
+    } else {
+        *dst_offset =
+          (encoded_bytes[0]<<24) |
+          (encoded_bytes[1]<<16) |
+          (encoded_bytes[2]<<8) |
+          encoded_bytes[3];
+        if (*dst_offset == 0x3fffffff) *dst_offset = -1;
+        *dst_contents =
+          (encoded_bytes[4]<<24) |
+          (encoded_bytes[5]<<16) |
+          (encoded_bytes[6]<<8) |
+          encoded_bytes[7];
+    }
+}
+
+static uint8_t *decode_in_memory(uint8_t *encoded_bytes,
+                                 int32_t *offset, uint32_t *contents) {
+    int input_size, charIx;
+    uint8_t input[8];
+
+    input[0] = *(encoded_bytes++);
+    if (input[0] & 0x80)
+        input_size = 2;
+    else if (input[0] & 0x40)
+        input_size = 3;
+    else
+        input_size = 8;
+
+    // we already read one byte..
+    charIx = 1;
+    while (charIx < input_size) {
+        input[charIx++] = *(encoded_bytes++);
+    }
+
+    // depends on the decoder state!
+    decode_bytes(input, input_size, offset, contents);
+
+    offs_prev = *offset;
+    cont_prev = *contents;
+
+    return encoded_bytes;
+}
+
+int retouch_mask_data(uint8_t *binary_object,
+                      int32_t binary_size,
+                      int32_t *desired_offset,
+                      int32_t *retouch_offset) {
+    retouch_info_t *r_info;
+    prelink_info_t *p_info;
+
+    int32_t target_offset = 0;
+    if (desired_offset) target_offset = *desired_offset;
+
+    int32_t p_offs = binary_size-sizeof(prelink_info_t); // prelink_info_t
+    int32_t r_offs = p_offs-sizeof(retouch_info_t); // retouch_info_t
+    int32_t b_offs; // retouch data blob
+
+    // If not retouched, we say it was a match. This might get invoked on
+    // non-retouched binaries, so that's why we need to do this.
+    if (retouch_offset != NULL) *retouch_offset = target_offset;
+    if (r_offs < 0) return (desired_offset == NULL) ?
+                      RETOUCH_DATA_NOTAPPLICABLE : RETOUCH_DATA_MATCHED;
+    p_info = (prelink_info_t *)(binary_object+p_offs);
+    r_info = (retouch_info_t *)(binary_object+r_offs);
+    if (strncmp(p_info->tag, "PRE ", 4) ||
+        strncmp(r_info->tag, "RETOUCH ", 8))
+        return (desired_offset == NULL) ?
+          RETOUCH_DATA_NOTAPPLICABLE : RETOUCH_DATA_MATCHED;
+
+    b_offs = r_offs-r_info->blob_size;
+    if (b_offs < 0) {
+        printf("negative binary offset: %d = %d - %d\n",
+               b_offs, r_offs, r_info->blob_size);
+        return RETOUCH_DATA_ERROR;
+    }
+    uint8_t *b_ptr = binary_object+b_offs;
+
+    // Retouched: let's go through the work then.
+    int32_t offset_candidate = target_offset;
+    bool offset_set = false, offset_mismatch = false;
+    init_compression_state();
+    while (b_ptr < (uint8_t *)r_info) {
+        int32_t retouch_entry_offset;
+        uint32_t *retouch_entry;
+        uint32_t retouch_original_value;
+
+        b_ptr = decode_in_memory(b_ptr,
+                                 &retouch_entry_offset,
+                                 &retouch_original_value);
+        if (retouch_entry_offset < (-1) ||
+            retouch_entry_offset >= b_offs) {
+            printf("bad retouch_entry_offset: %d", retouch_entry_offset);
+            return RETOUCH_DATA_ERROR;
+        }
+
+        // "-1" means this is the value in prelink_info_t, which also gets
+        // randomized.
+        if (retouch_entry_offset == -1)
+            retouch_entry = (uint32_t *)&(p_info->mmap_addr);
+        else
+            retouch_entry = (uint32_t *)(binary_object+retouch_entry_offset);
+
+        if (desired_offset)
+            *retouch_entry = retouch_original_value + target_offset;
+
+        // Infer the randomization shift, compare to previously inferred.
+        int32_t offset_of_this_entry = (int32_t)(*retouch_entry-
+                                                 retouch_original_value);
+        if (!offset_set) {
+            offset_candidate = offset_of_this_entry;
+            offset_set = true;
+        } else {
+            if (offset_candidate != offset_of_this_entry) {
+                offset_mismatch = true;
+                printf("offset is mismatched: %d, this entry is %d,"
+                       " original 0x%x @ 0x%x",
+                       offset_candidate, offset_of_this_entry,
+                       retouch_original_value, retouch_entry_offset);
+            }
+        }
+    }
+    if (b_ptr > (uint8_t *)r_info) {
+        printf("b_ptr went too far: %p, while r_info is %p",
+               b_ptr, r_info);
+        return RETOUCH_DATA_ERROR;
+    }
+
+    if (offset_mismatch) return RETOUCH_DATA_MISMATCHED;
+    if (retouch_offset != NULL) *retouch_offset = offset_candidate;
+    return RETOUCH_DATA_MATCHED;
+}
+
+// On success, _override is set to the offset that was actually applied.
+// This implies that once we randomize to an offset we stick with it.
+// This in turn is necessary in order to guarantee recovery after crash.
+bool retouch_one_library(const char *binary_name,
+                         const char *binary_sha1,
+                         int32_t retouch_offset,
+                         int32_t *retouch_offset_override) {
+    bool success = true;
+    int result;
+
+    FileContents file;
+    file.data = NULL;
+
+    char binary_name_atomic[strlen(binary_name)+10];
+    strcpy(binary_name_atomic, binary_name);
+    strcat(binary_name_atomic, ".atomic");
+
+    // We need a path that exists for calling statfs() later.
+    //
+    // Assume that binary_name (eg "/system/app/Foo.apk") is located
+    // on the same filesystem as its top-level directory ("/system").
+    char target_fs[strlen(binary_name)+1];
+    char* slash = strchr(binary_name+1, '/');
+    if (slash != NULL) {
+        int count = slash - binary_name;
+        strncpy(target_fs, binary_name, count);
+        target_fs[count] = '\0';
+    } else {
+        strcpy(target_fs, binary_name);
+    }
+
+    result = LoadFileContents(binary_name, &file, RETOUCH_DONT_MASK);
+
+    if (result == 0) {
+        // Figure out the *apparent* offset to which this file has been
+        // retouched. If it looks good, we will skip processing (we might
+        // have crashed and during this recovery pass we don't want to
+        // overwrite a valuable saved file in /cache---which would happen
+        // if we blindly retouch everything again). NOTE: This implies
+        // that we might have to override the supplied retouch offset. We
+        // can do the override only once though: everything should match
+        // afterward.
+
+        int32_t inferred_offset;
+        int retouch_probe_result = retouch_mask_data(file.data,
+                                                     file.size,
+                                                     NULL,
+                                                     &inferred_offset);
+
+        if (retouch_probe_result == RETOUCH_DATA_MATCHED) {
+            if ((retouch_offset == inferred_offset) ||
+                ((retouch_offset != 0 && inferred_offset != 0) &&
+                 (retouch_offset_override != NULL))) {
+                // This file is OK already and we are allowed to override.
+                // Let's just return the offset override value. It is critical
+                // to skip regardless of override: a broken file might need
+                // recovery down the list and we should not mess up the saved
+                // copy by doing unnecessary retouching.
+                //
+                // NOTE: If retouching was already started with a different
+                // value, we will not be allowed to override. This happens
+                // if on the retouch list there is a patched binary (which is
+                // masked in apply_patch()) before there is a non-patched
+                // binary.
+                if (retouch_offset_override != NULL)
+                    *retouch_offset_override = inferred_offset;
+                success = true;
+                goto out;
+            } else {
+                // Retouch to zero (mask the retouching), to make sure that
+                // the SHA-1 check will pass below.
+                int32_t zero = 0;
+                retouch_mask_data(file.data, file.size, &zero, NULL);
+                SHA(file.data, file.size, file.sha1);
+            }
+        }
+
+        if (retouch_probe_result == RETOUCH_DATA_NOTAPPLICABLE) {
+            // In the case of not retouchable, fake it. We do not want
+            // to do the normal processing and overwrite the backup file:
+            // we might be recovering!
+            //
+            // We return a zero override, which tells the caller that we
+            // simply skipped the file.
+            if (retouch_offset_override != NULL)
+                *retouch_offset_override = 0;
+            success = true;
+            goto out;
+        }
+
+        // If we get here, either there was a mismatch in the offset, or
+        // the file has not been processed yet. Continue with normal
+        // processing.
+    }
+
+    if (result != 0 || FindMatchingPatch(file.sha1, &binary_sha1, 1) < 0) {
+        free(file.data);
+        printf("Attempting to recover source from '%s' ...\n",
+               CACHE_TEMP_SOURCE);
+        result = LoadFileContents(CACHE_TEMP_SOURCE, &file, RETOUCH_DO_MASK);
+        if (result != 0 || FindMatchingPatch(file.sha1, &binary_sha1, 1) < 0) {
+            printf(" failed.\n");
+            success = false;
+            goto out;
+        }
+        printf(" succeeded.\n");
+    }
+
+    // Retouch in-memory before worrying about backing up the original.
+    //
+    // Recovery steps will be oblivious to the actual retouch offset used,
+    // so might as well write out the already-retouched copy. Then, in the
+    // usual case, we will just swap the file locally, with no more writes
+    // needed. In the no-free-space case, we will then write the same to the
+    // original location.
+
+    result = retouch_mask_data(file.data, file.size, &retouch_offset, NULL);
+    if (result != RETOUCH_DATA_MATCHED) {
+        success = false;
+        goto out;
+    }
+    if (retouch_offset_override != NULL)
+        *retouch_offset_override = retouch_offset;
+
+    // How much free space do we need?
+    bool enough_space = false;
+    size_t free_space = FreeSpaceForFile(target_fs);
+    // 50% margin when estimating the space needed.
+    enough_space = (free_space > (file.size * 3 / 2));
+
+    // The experts say we have to allow for a retry of the
+    // whole process to avoid filesystem weirdness.
+    int retry = 1;
+    bool made_copy = false;
+    do {
+        // First figure out where to store a copy of the original.
+        // Ideally leave the original itself intact until the
+        // atomic swap. If no room on the same partition, fall back
+        // to the cache partition and remove the original.
+
+        if (!enough_space) {
+            printf("Target is %ldB; free space is %ldB: not enough.\n",
+                   (long)file.size, (long)free_space);
+
+            retry = 0;
+            if (MakeFreeSpaceOnCache(file.size) < 0) {
+                printf("Not enough free space on '/cache'.\n");
+                success = false;
+                goto out;
+            }
+            if (SaveFileContents(CACHE_TEMP_SOURCE, file) < 0) {
+                printf("Failed to back up source file.\n");
+                success = false;
+                goto out;
+            }
+            made_copy = true;
+            unlink(binary_name);
+
+            size_t free_space = FreeSpaceForFile(target_fs);
+            printf("(now %ld bytes free for target)\n", (long)free_space);
+        }
+
+        result = SaveFileContents(binary_name_atomic, file);
+        if (result != 0) {
+            // Maybe the filesystem was optimistic: retry.
+            enough_space = false;
+            unlink(binary_name_atomic);
+            printf("Saving the retouched contents failed; retrying.\n");
+            continue;
+        }
+
+        // Succeeded; no need to retry.
+        break;
+    } while (retry-- > 0);
+
+    // Give the .atomic file the same owner, group, and mode of the
+    // original source file.
+    if (chmod(binary_name_atomic, file.st.st_mode) != 0) {
+        printf("chmod of \"%s\" failed: %s\n",
+               binary_name_atomic, strerror(errno));
+        success = false;
+        goto out;
+    }
+    if (chown(binary_name_atomic, file.st.st_uid, file.st.st_gid) != 0) {
+        printf("chown of \"%s\" failed: %s\n",
+               binary_name_atomic,
+               strerror(errno));
+        success = false;
+        goto out;
+    }
+
+    // Finally, rename the .atomic file to replace the target file.
+    if (rename(binary_name_atomic, binary_name) != 0) {
+        printf("rename of .atomic to \"%s\" failed: %s\n",
+               binary_name, strerror(errno));
+        success = false;
+        goto out;
+    }
+
+    // If this run created a copy, and we're here, we can delete it.
+    if (made_copy) unlink(CACHE_TEMP_SOURCE);
+
+  out:
+    // clean up
+    free(file.data);
+    unlink(binary_name_atomic);
+
+    return success;
+}
diff --git a/minelf/Retouch.h b/minelf/Retouch.h
new file mode 100644
index 0000000..048d78e
--- /dev/null
+++ b/minelf/Retouch.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MINELF_RETOUCH
+#define _MINELF_RETOUCH
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+typedef struct {
+  char tag[8];        /* "RETOUCH ", not zero-terminated */
+  uint32_t blob_size; /* in bytes, located right before this struct */
+} retouch_info_t __attribute__((packed));
+
+// Retouch a file. Use CACHED_SOURCE_TEMP to store a copy.
+bool retouch_one_library(const char *binary_name,
+                         const char *binary_sha1,
+                         int32_t retouch_offset,
+                         int32_t *retouch_offset_override);
+
+#define RETOUCH_DONT_MASK           0
+#define RETOUCH_DO_MASK             1
+
+#define RETOUCH_DATA_ERROR          0 // This is bad. Should not happen.
+#define RETOUCH_DATA_MATCHED        1 // Up to an uniform random offset.
+#define RETOUCH_DATA_MISMATCHED     2 // Partially randomized, or total mess.
+#define RETOUCH_DATA_NOTAPPLICABLE  3 // Not retouched. Only when inferring.
+
+// Mask retouching in-memory. Used before apply_patch[_check].
+// Also used to determine status of retouching after a crash.
+//
+// If desired_offset is not NULL, then apply retouching instead,
+// and return that in retouch_offset.
+int retouch_mask_data(uint8_t *binary_object,
+                      int32_t binary_size,
+                      int32_t *desired_offset,
+                      int32_t *retouch_offset);
+#endif
diff --git a/minui/Android.mk b/minui/Android.mk
index 91dd939..7ded5d3 100644
--- a/minui/Android.mk
+++ b/minui/Android.mk
@@ -9,4 +9,8 @@
 
 LOCAL_MODULE := libminui
 
+ifneq ($(RECOVERY_24_BIT),)
+  LOCAL_CFLAGS += -DRECOVERY_24_BIT
+endif
+
 include $(BUILD_STATIC_LIBRARY)
diff --git a/minui/graphics.c b/minui/graphics.c
index 4127c40..42c85e7 100644
--- a/minui/graphics.c
+++ b/minui/graphics.c
@@ -32,6 +32,14 @@
 #include "font_10x18.h"
 #include "minui.h"
 
+#ifdef RECOVERY_24_BIT
+#define PIXEL_FORMAT GGL_PIXEL_FORMAT_RGBX_8888
+#define PIXEL_SIZE   4
+#else
+#define PIXEL_FORMAT GGL_PIXEL_FORMAT_RGB_565
+#define PIXEL_SIZE   2
+#endif
+
 typedef struct {
     GGLSurface texture;
     unsigned cwidth;
@@ -87,8 +95,8 @@
     fb->height = vi.yres;
     fb->stride = vi.xres;
     fb->data = bits;
-    fb->format = GGL_PIXEL_FORMAT_RGB_565;
-    memset(fb->data, 0, vi.yres * vi.xres * 2);
+    fb->format = PIXEL_FORMAT;
+    memset(fb->data, 0, vi.yres * vi.xres * PIXEL_SIZE);
 
     fb++;
 
@@ -96,9 +104,9 @@
     fb->width = vi.xres;
     fb->height = vi.yres;
     fb->stride = vi.xres;
-    fb->data = (void*) (((unsigned) bits) + vi.yres * vi.xres * 2);
-    fb->format = GGL_PIXEL_FORMAT_RGB_565;
-    memset(fb->data, 0, vi.yres * vi.xres * 2);
+    fb->data = (void*) (((unsigned) bits) + vi.yres * vi.xres * PIXEL_SIZE);
+    fb->format = PIXEL_FORMAT;
+    memset(fb->data, 0, vi.yres * vi.xres * PIXEL_SIZE);
 
     return fd;
 }
@@ -108,16 +116,16 @@
   ms->width = vi.xres;
   ms->height = vi.yres;
   ms->stride = vi.xres;
-  ms->data = malloc(vi.xres * vi.yres * 2);
-  ms->format = GGL_PIXEL_FORMAT_RGB_565;
+  ms->data = malloc(vi.xres * vi.yres * PIXEL_SIZE);
+  ms->format = PIXEL_FORMAT;
 }
 
 static void set_active_framebuffer(unsigned n)
 {
     if (n > 1) return;
-    vi.yres_virtual = vi.yres * 2;
+    vi.yres_virtual = vi.yres * PIXEL_SIZE;
     vi.yoffset = n * vi.yres;
-    vi.bits_per_pixel = 16;
+    vi.bits_per_pixel = PIXEL_SIZE * 8;
     if (ioctl(gr_fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) {
         perror("active fb swap failed");
     }
@@ -133,7 +141,7 @@
     /* copy data from the in-memory surface to the buffer we're about
      * to make active. */
     memcpy(gr_framebuffer[gr_active_fb].data, gr_mem_surface.data,
-           vi.xres * vi.yres * 2);
+           vi.xres * vi.yres * PIXEL_SIZE);
 
     /* inform the display driver */
     set_active_framebuffer(gr_active_fb);
diff --git a/minui/resources.c b/minui/resources.c
index 3d2c727..b437a87 100644
--- a/minui/resources.c
+++ b/minui/resources.c
@@ -49,6 +49,8 @@
     png_structp png_ptr = NULL;
     png_infop info_ptr = NULL;
 
+    *pSurface = NULL;
+
     snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);
     resPath[sizeof(resPath)-1] = '\0';
     FILE* fp = fopen(resPath, "rb");
@@ -119,12 +121,17 @@
     surface->format = (channels == 3) ?
             GGL_PIXEL_FORMAT_RGBX_8888 : GGL_PIXEL_FORMAT_RGBA_8888;
 
+    int alpha = 0;
     if (color_type == PNG_COLOR_TYPE_PALETTE) {
-      png_set_palette_to_rgb(png_ptr);
+        png_set_palette_to_rgb(png_ptr);
+    }
+    if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+        png_set_tRNS_to_alpha(png_ptr);
+        alpha = 1;
     }
 
     int y;
-    if (channels == 3) {
+    if (channels == 3 || (channels == 1 && !alpha)) {
         for (y = 0; y < height; ++y) {
             unsigned char* pRow = pData + y * stride;
             png_read_row(png_ptr, pRow, NULL);
diff --git a/mtdutils/Android.mk b/mtdutils/Android.mk
index 57ab579..4166536 100644
--- a/mtdutils/Android.mk
+++ b/mtdutils/Android.mk
@@ -1,5 +1,4 @@
 ifneq ($(TARGET_SIMULATOR),true)
-ifeq ($(TARGET_ARCH),arm)
 
 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
@@ -20,5 +19,4 @@
 LOCAL_SHARED_LIBRARIES := libcutils libc
 include $(BUILD_EXECUTABLE)
 
-endif	# TARGET_ARCH == arm
 endif	# !TARGET_SIMULATOR
diff --git a/recovery.c b/recovery.c
index 9ad075d..fd7ce42 100644
--- a/recovery.c
+++ b/recovery.c
@@ -23,7 +23,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/reboot.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <time.h>
@@ -33,19 +32,18 @@
 #include "bootloader.h"
 #include "common.h"
 #include "cutils/properties.h"
+#include "cutils/android_reboot.h"
 #include "install.h"
 #include "minui/minui.h"
 #include "minzip/DirUtil.h"
 #include "roots.h"
 #include "recovery_ui.h"
-#include "encryptedfs_provisioning.h"
 
 static const struct option OPTIONS[] = {
   { "send_intent", required_argument, NULL, 's' },
   { "update_package", required_argument, NULL, 'u' },
   { "wipe_data", no_argument, NULL, 'w' },
   { "wipe_cache", no_argument, NULL, 'c' },
-  { "set_encrypted_filesystems", required_argument, NULL, 'e' },
   { "show_text", no_argument, NULL, 't' },
   { NULL, 0, NULL, 0 },
 };
@@ -58,6 +56,8 @@
 static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
 static const char *SIDELOAD_TEMP_DIR = "/tmp/sideload";
 
+extern UIParameters ui_parameters;    // from ui.c
+
 /*
  * The recovery tool communicates with the main system through /cache files.
  *   /cache/recovery/command - INPUT - command line for tool, one arg per line
@@ -114,26 +114,6 @@
  *    8g. finish_recovery() erases BCB
  *        -- after this, rebooting will (try to) restart the main system --
  * 9. main() calls reboot() to boot main system
- *
- * SECURE FILE SYSTEMS ENABLE/DISABLE
- * 1. user selects "enable encrypted file systems"
- * 2. main system writes "--set_encrypted_filesystems=on|off" to
- *    /cache/recovery/command
- * 3. main system reboots into recovery
- * 4. get_args() writes BCB with "boot-recovery" and
- *    "--set_encrypted_filesystems=on|off"
- *    -- after this, rebooting will restart the transition --
- * 5. read_encrypted_fs_info() retrieves encrypted file systems settings from /data
- *    Settings include: property to specify the Encrypted FS istatus and
- *    FS encryption key if enabled (not yet implemented)
- * 6. erase_volume() reformats /data
- * 7. erase_volume() reformats /cache
- * 8. restore_encrypted_fs_info() writes required encrypted file systems settings to /data
- *    Settings include: property to specify the Encrypted FS status and
- *    FS encryption key if enabled (not yet implemented)
- * 9. finish_recovery() erases BCB
- *    -- after this, rebooting will restart the main system --
- * 10. main() calls reboot() to boot main system
  */
 
 static const int MAX_ARG_LENGTH = 4096;
@@ -446,6 +426,16 @@
         int key = ui_wait_key();
         int visible = ui_text_visible();
 
+        if (key == -1) {   // ui_wait_key() timed out
+            if (ui_text_ever_visible()) {
+                continue;
+            } else {
+                LOGI("timed out waiting for key input; rebooting.\n");
+                ui_end_menu();
+                return ITEM_REBOOT;
+            }
+        }
+
         int action = device_handle_key(key, visible);
 
         if (action < 0) {
@@ -700,6 +690,7 @@
     freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL);
     printf("Starting recovery on %s", ctime(&start));
 
+    device_ui_init(&ui_parameters);
     ui_init();
     ui_set_background(BACKGROUND_ICON_INSTALLING);
     load_volume_table();
@@ -708,10 +699,7 @@
     int previous_runs = 0;
     const char *send_intent = NULL;
     const char *update_package = NULL;
-    const char *encrypted_fs_mode = NULL;
     int wipe_data = 0, wipe_cache = 0;
-    int toggle_secure_fs = 0;
-    encrypted_fs_info encrypted_fs_data;
 
     int arg;
     while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {
@@ -721,7 +709,6 @@
         case 'u': update_package = optarg; break;
         case 'w': wipe_data = wipe_cache = 1; break;
         case 'c': wipe_cache = 1; break;
-        case 'e': encrypted_fs_mode = optarg; toggle_secure_fs = 1; break;
         case 't': ui_show_text(1); break;
         case '?':
             LOGE("Invalid command argument\n");
@@ -758,43 +745,7 @@
 
     int status = INSTALL_SUCCESS;
 
-    if (toggle_secure_fs) {
-        if (strcmp(encrypted_fs_mode,"on") == 0) {
-            encrypted_fs_data.mode = MODE_ENCRYPTED_FS_ENABLED;
-            ui_print("Enabling Encrypted FS.\n");
-        } else if (strcmp(encrypted_fs_mode,"off") == 0) {
-            encrypted_fs_data.mode = MODE_ENCRYPTED_FS_DISABLED;
-            ui_print("Disabling Encrypted FS.\n");
-        } else {
-            ui_print("Error: invalid Encrypted FS setting.\n");
-            status = INSTALL_ERROR;
-        }
-
-        // Recovery strategy: if the data partition is damaged, disable encrypted file systems.
-        // This preventsthe device recycling endlessly in recovery mode.
-        if ((encrypted_fs_data.mode == MODE_ENCRYPTED_FS_ENABLED) &&
-                (read_encrypted_fs_info(&encrypted_fs_data))) {
-            ui_print("Encrypted FS change aborted, resetting to disabled state.\n");
-            encrypted_fs_data.mode = MODE_ENCRYPTED_FS_DISABLED;
-        }
-
-        if (status != INSTALL_ERROR) {
-            if (erase_volume("/data")) {
-                ui_print("Data wipe failed.\n");
-                status = INSTALL_ERROR;
-            } else if (erase_volume("/cache")) {
-                ui_print("Cache wipe failed.\n");
-                status = INSTALL_ERROR;
-            } else if ((encrypted_fs_data.mode == MODE_ENCRYPTED_FS_ENABLED) &&
-                      (restore_encrypted_fs_info(&encrypted_fs_data))) {
-                ui_print("Encrypted FS change aborted.\n");
-                status = INSTALL_ERROR;
-            } else {
-                ui_print("Successfully updated Encrypted FS.\n");
-                status = INSTALL_SUCCESS;
-            }
-        }
-    } else if (update_package != NULL) {
+    if (update_package != NULL) {
         status = install_package(update_package);
         if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n");
     } else if (wipe_data) {
@@ -817,7 +768,6 @@
     // Otherwise, get ready to boot the main system...
     finish_recovery(send_intent);
     ui_print("Rebooting...\n");
-    sync();
-    reboot(RB_AUTOBOOT);
+    android_reboot(ANDROID_RB_RESTART, 0, 0);
     return EXIT_SUCCESS;
 }
diff --git a/recovery_ui.h b/recovery_ui.h
index e451bdf..e56a24b 100644
--- a/recovery_ui.h
+++ b/recovery_ui.h
@@ -17,6 +17,12 @@
 #ifndef _RECOVERY_UI_H
 #define _RECOVERY_UI_H
 
+#include "common.h"
+
+// Called before UI library is initialized.  Can change things like
+// how many frames are included in various animations, etc.
+extern void device_ui_init(UIParameters* ui_parameters);
+
 // Called when recovery starts up.  Returns 0.
 extern int device_recovery_start();
 
@@ -66,7 +72,8 @@
 #define SELECT_ITEM         -4
 
 #define ITEM_REBOOT          0
-#define ITEM_APPLY_SDCARD    1
+#define ITEM_APPLY_EXT       1
+#define ITEM_APPLY_SDCARD    1  // historical synonym for ITEM_APPLY_EXT
 #define ITEM_WIPE_DATA       2
 #define ITEM_WIPE_CACHE      3
 
diff --git a/res/images/icon_error.png b/res/images/icon_error.png
old mode 100755
new mode 100644
index 90c8d87..446c3fb
--- a/res/images/icon_error.png
+++ b/res/images/icon_error.png
Binary files differ
diff --git a/res/images/icon_installing.png b/res/images/icon_installing.png
old mode 100755
new mode 100644
index d428f57..7e42628
--- a/res/images/icon_installing.png
+++ b/res/images/icon_installing.png
Binary files differ
diff --git a/res/images/icon_installing_overlay01.png b/res/images/icon_installing_overlay01.png
new file mode 100644
index 0000000..2c4c89a
--- /dev/null
+++ b/res/images/icon_installing_overlay01.png
Binary files differ
diff --git a/res/images/icon_installing_overlay02.png b/res/images/icon_installing_overlay02.png
new file mode 100644
index 0000000..b63552b
--- /dev/null
+++ b/res/images/icon_installing_overlay02.png
Binary files differ
diff --git a/res/images/icon_installing_overlay03.png b/res/images/icon_installing_overlay03.png
new file mode 100644
index 0000000..6acb5ef
--- /dev/null
+++ b/res/images/icon_installing_overlay03.png
Binary files differ
diff --git a/res/images/icon_installing_overlay04.png b/res/images/icon_installing_overlay04.png
new file mode 100644
index 0000000..0d4608a
--- /dev/null
+++ b/res/images/icon_installing_overlay04.png
Binary files differ
diff --git a/res/images/icon_installing_overlay05.png b/res/images/icon_installing_overlay05.png
new file mode 100644
index 0000000..4bfacb2
--- /dev/null
+++ b/res/images/icon_installing_overlay05.png
Binary files differ
diff --git a/res/images/icon_installing_overlay06.png b/res/images/icon_installing_overlay06.png
new file mode 100644
index 0000000..5fd2528
--- /dev/null
+++ b/res/images/icon_installing_overlay06.png
Binary files differ
diff --git a/res/images/icon_installing_overlay07.png b/res/images/icon_installing_overlay07.png
new file mode 100644
index 0000000..350b381
--- /dev/null
+++ b/res/images/icon_installing_overlay07.png
Binary files differ
diff --git a/res/images/indeterminate01.png b/res/images/indeterminate01.png
new file mode 100644
index 0000000..84f04b0
--- /dev/null
+++ b/res/images/indeterminate01.png
Binary files differ
diff --git a/res/images/indeterminate02.png b/res/images/indeterminate02.png
new file mode 100644
index 0000000..21d0121
--- /dev/null
+++ b/res/images/indeterminate02.png
Binary files differ
diff --git a/res/images/indeterminate03.png b/res/images/indeterminate03.png
new file mode 100644
index 0000000..8a190a8
--- /dev/null
+++ b/res/images/indeterminate03.png
Binary files differ
diff --git a/res/images/indeterminate04.png b/res/images/indeterminate04.png
new file mode 100644
index 0000000..baf8d63
--- /dev/null
+++ b/res/images/indeterminate04.png
Binary files differ
diff --git a/res/images/indeterminate05.png b/res/images/indeterminate05.png
new file mode 100644
index 0000000..5653c7b
--- /dev/null
+++ b/res/images/indeterminate05.png
Binary files differ
diff --git a/res/images/indeterminate06.png b/res/images/indeterminate06.png
new file mode 100644
index 0000000..858de85
--- /dev/null
+++ b/res/images/indeterminate06.png
Binary files differ
diff --git a/res/images/indeterminate1.png b/res/images/indeterminate1.png
deleted file mode 100644
index 90cb9fb..0000000
--- a/res/images/indeterminate1.png
+++ /dev/null
Binary files differ
diff --git a/res/images/indeterminate2.png b/res/images/indeterminate2.png
deleted file mode 100644
index f7fb289..0000000
--- a/res/images/indeterminate2.png
+++ /dev/null
Binary files differ
diff --git a/res/images/indeterminate3.png b/res/images/indeterminate3.png
deleted file mode 100644
index ba10dfa..0000000
--- a/res/images/indeterminate3.png
+++ /dev/null
Binary files differ
diff --git a/res/images/indeterminate4.png b/res/images/indeterminate4.png
deleted file mode 100644
index ad5d9a5..0000000
--- a/res/images/indeterminate4.png
+++ /dev/null
Binary files differ
diff --git a/res/images/indeterminate5.png b/res/images/indeterminate5.png
deleted file mode 100644
index 8c19c8d..0000000
--- a/res/images/indeterminate5.png
+++ /dev/null
Binary files differ
diff --git a/res/images/indeterminate6.png b/res/images/indeterminate6.png
deleted file mode 100644
index c0c6638..0000000
--- a/res/images/indeterminate6.png
+++ /dev/null
Binary files differ
diff --git a/res/images/progress_empty.png b/res/images/progress_empty.png
index 4cb4998..c682015 100644
--- a/res/images/progress_empty.png
+++ b/res/images/progress_empty.png
Binary files differ
diff --git a/res/images/progress_fill.png b/res/images/progress_fill.png
index eb71754..9748435 100644
--- a/res/images/progress_fill.png
+++ b/res/images/progress_fill.png
Binary files differ
diff --git a/roots.c b/roots.c
index 90b82d7..cb7e067 100644
--- a/roots.c
+++ b/roots.c
@@ -31,6 +31,21 @@
 static int num_volumes = 0;
 static Volume* device_volumes = NULL;
 
+static int parse_options(char* options, Volume* volume) {
+    char* option;
+    while (option = strtok(options, ",")) {
+        options = NULL;
+
+        if (strncmp(option, "length=", 7) == 0) {
+            volume->length = strtoll(option+7, NULL, 10);
+        } else {
+            LOGE("bad option \"%s\"\n", option);
+            return -1;
+        }
+    }
+    return 0;
+}
+
 void load_volume_table() {
     int alloc = 2;
     device_volumes = malloc(alloc * sizeof(Volume));
@@ -40,6 +55,7 @@
     device_volumes[0].fs_type = "ramdisk";
     device_volumes[0].device = NULL;
     device_volumes[0].device2 = NULL;
+    device_volumes[0].length = 0;
     num_volumes = 1;
 
     FILE* fstab = fopen("/etc/recovery.fstab", "r");
@@ -61,7 +77,16 @@
         char* device = strtok(NULL, " \t\n");
         // lines may optionally have a second device, to use if
         // mounting the first one fails.
+        char* options = NULL;
         char* device2 = strtok(NULL, " \t\n");
+        if (device2) {
+            if (device2[0] == '/') {
+                options = strtok(NULL, " \t\n");
+            } else {
+                options = device2;
+                device2 = NULL;
+            }
+        }
 
         if (mount_point && fs_type && device) {
             while (num_volumes >= alloc) {
@@ -73,7 +98,13 @@
             device_volumes[num_volumes].device = strdup(device);
             device_volumes[num_volumes].device2 =
                 device2 ? strdup(device2) : NULL;
-            ++num_volumes;
+
+            device_volumes[num_volumes].length = 0;
+            if (parse_options(options, device_volumes + num_volumes) != 0) {
+                LOGE("skipping malformed recovery.fstab line: %s\n", original);
+            } else {
+                ++num_volumes;
+            }
         } else {
             LOGE("skipping malformed recovery.fstab line: %s\n", original);
         }
@@ -86,8 +117,8 @@
     printf("=========================\n");
     for (i = 0; i < num_volumes; ++i) {
         Volume* v = &device_volumes[i];
-        printf("  %d %s %s %s %s\n", i, v->mount_point, v->fs_type,
-               v->device, v->device2);
+        printf("  %d %s %s %s %s %lld\n", i, v->mount_point, v->fs_type,
+               v->device, v->device2, v->length);
     }
     printf("\n");
 }
@@ -238,8 +269,7 @@
     }
 
     if (strcmp(v->fs_type, "ext4") == 0) {
-        reset_ext4fs_info();
-        int result = make_ext4fs(v->device, NULL, NULL, 0, 0, 0);
+        int result = make_ext4fs(v->device, v->length);
         if (result != 0) {
             LOGE("format_volume: make_extf4fs failed on %s\n", v->device);
             return -1;
diff --git a/ui.c b/ui.c
index 84a9b85..0744da4 100644
--- a/ui.c
+++ b/ui.c
@@ -14,18 +14,22 @@
  * limitations under the License.
  */
 
+#include <errno.h>
+#include <fcntl.h>
 #include <linux/input.h>
 #include <pthread.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/reboot.h>
+#include <sys/stat.h>
 #include <sys/time.h>
+#include <sys/types.h>
 #include <time.h>
 #include <unistd.h>
 
 #include "common.h"
+#include <cutils/android_reboot.h>
 #include "minui/minui.h"
 #include "recovery_ui.h"
 
@@ -35,30 +39,32 @@
 #define CHAR_WIDTH 10
 #define CHAR_HEIGHT 18
 
-#define PROGRESSBAR_INDETERMINATE_STATES 6
-#define PROGRESSBAR_INDETERMINATE_FPS 15
+#define UI_WAIT_KEY_TIMEOUT_SEC    120
+
+UIParameters ui_parameters = {
+    6,       // indeterminate progress bar frames
+    20,      // fps
+    7,       // installation icon frames (0 == static image)
+    23, 83,  // installation icon overlay offset
+};
 
 static pthread_mutex_t gUpdateMutex = PTHREAD_MUTEX_INITIALIZER;
 static gr_surface gBackgroundIcon[NUM_BACKGROUND_ICONS];
-static gr_surface gProgressBarIndeterminate[PROGRESSBAR_INDETERMINATE_STATES];
+static gr_surface *gInstallationOverlay;
+static gr_surface *gProgressBarIndeterminate;
 static gr_surface gProgressBarEmpty;
 static gr_surface gProgressBarFill;
 
 static const struct { gr_surface* surface; const char *name; } BITMAPS[] = {
     { &gBackgroundIcon[BACKGROUND_ICON_INSTALLING], "icon_installing" },
     { &gBackgroundIcon[BACKGROUND_ICON_ERROR],      "icon_error" },
-    { &gProgressBarIndeterminate[0],    "indeterminate1" },
-    { &gProgressBarIndeterminate[1],    "indeterminate2" },
-    { &gProgressBarIndeterminate[2],    "indeterminate3" },
-    { &gProgressBarIndeterminate[3],    "indeterminate4" },
-    { &gProgressBarIndeterminate[4],    "indeterminate5" },
-    { &gProgressBarIndeterminate[5],    "indeterminate6" },
     { &gProgressBarEmpty,               "progress_empty" },
     { &gProgressBarFill,                "progress_fill" },
     { NULL,                             NULL },
 };
 
-static gr_surface gCurrentIcon = NULL;
+static int gCurrentIcon = 0;
+static int gInstallingFrame = 0;
 
 static enum ProgressBarType {
     PROGRESSBAR_TYPE_NONE,
@@ -68,7 +74,7 @@
 
 // Progress bar scope of current operation
 static float gProgressScopeStart = 0, gProgressScopeSize = 0, gProgress = 0;
-static time_t gProgressScopeTime, gProgressScopeDuration;
+static double gProgressScopeTime, gProgressScopeDuration;
 
 // Set to 1 when both graphics pages are the same (except for the progress bar)
 static int gPagesIdentical = 0;
@@ -78,6 +84,7 @@
 static int text_cols = 0, text_rows = 0;
 static int text_col = 0, text_row = 0, text_top = 0;
 static int show_text = 0;
+static int show_text_ever = 0;   // has show_text ever been 1?
 
 static char menu[MAX_ROWS][MAX_COLS];
 static int show_menu = 0;
@@ -89,20 +96,46 @@
 static int key_queue[256], key_queue_len = 0;
 static volatile char key_pressed[KEY_MAX + 1];
 
+// Return the current time as a double (including fractions of a second).
+static double now() {
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    return tv.tv_sec + tv.tv_usec / 1000000.0;
+}
+
+// Draw the given frame over the installation overlay animation.  The
+// background is not cleared or draw with the base icon first; we
+// assume that the frame already contains some other frame of the
+// animation.  Does nothing if no overlay animation is defined.
+// Should only be called with gUpdateMutex locked.
+static void draw_install_overlay_locked(int frame) {
+    if (gInstallationOverlay == NULL) return;
+    gr_surface surface = gInstallationOverlay[frame];
+    int iconWidth = gr_get_width(surface);
+    int iconHeight = gr_get_height(surface);
+    gr_blit(surface, 0, 0, iconWidth, iconHeight,
+            ui_parameters.install_overlay_offset_x,
+            ui_parameters.install_overlay_offset_y);
+}
+
 // Clear the screen and draw the currently selected background icon (if any).
 // Should only be called with gUpdateMutex locked.
-static void draw_background_locked(gr_surface icon)
+static void draw_background_locked(int icon)
 {
     gPagesIdentical = 0;
     gr_color(0, 0, 0, 255);
     gr_fill(0, 0, gr_fb_width(), gr_fb_height());
 
     if (icon) {
-        int iconWidth = gr_get_width(icon);
-        int iconHeight = gr_get_height(icon);
+        gr_surface surface = gBackgroundIcon[icon];
+        int iconWidth = gr_get_width(surface);
+        int iconHeight = gr_get_height(surface);
         int iconX = (gr_fb_width() - iconWidth) / 2;
         int iconY = (gr_fb_height() - iconHeight) / 2;
-        gr_blit(icon, 0, 0, iconWidth, iconHeight, iconX, iconY);
+        gr_blit(surface, 0, 0, iconWidth, iconHeight, iconX, iconY);
+        if (icon == BACKGROUND_ICON_INSTALLING) {
+            draw_install_overlay_locked(gInstallingFrame);
+        }
     }
 }
 
@@ -110,35 +143,39 @@
 // Should only be called with gUpdateMutex locked.
 static void draw_progress_locked()
 {
-    if (gProgressBarType == PROGRESSBAR_TYPE_NONE) return;
-
-    int iconHeight = gr_get_height(gBackgroundIcon[BACKGROUND_ICON_INSTALLING]);
-    int width = gr_get_width(gProgressBarEmpty);
-    int height = gr_get_height(gProgressBarEmpty);
-
-    int dx = (gr_fb_width() - width)/2;
-    int dy = (3*gr_fb_height() + iconHeight - 2*height)/4;
-
-    // Erase behind the progress bar (in case this was a progress-only update)
-    gr_color(0, 0, 0, 255);
-    gr_fill(dx, dy, width, height);
-
-    if (gProgressBarType == PROGRESSBAR_TYPE_NORMAL) {
-        float progress = gProgressScopeStart + gProgress * gProgressScopeSize;
-        int pos = (int) (progress * width);
-
-        if (pos > 0) {
-          gr_blit(gProgressBarFill, 0, 0, pos, height, dx, dy);
-        }
-        if (pos < width-1) {
-          gr_blit(gProgressBarEmpty, pos, 0, width-pos, height, dx+pos, dy);
-        }
+    if (gCurrentIcon == BACKGROUND_ICON_INSTALLING) {
+        draw_install_overlay_locked(gInstallingFrame);
     }
 
-    if (gProgressBarType == PROGRESSBAR_TYPE_INDETERMINATE) {
-        static int frame = 0;
-        gr_blit(gProgressBarIndeterminate[frame], 0, 0, width, height, dx, dy);
-        frame = (frame + 1) % PROGRESSBAR_INDETERMINATE_STATES;
+    if (gProgressBarType != PROGRESSBAR_TYPE_NONE) {
+        int iconHeight = gr_get_height(gBackgroundIcon[BACKGROUND_ICON_INSTALLING]);
+        int width = gr_get_width(gProgressBarEmpty);
+        int height = gr_get_height(gProgressBarEmpty);
+
+        int dx = (gr_fb_width() - width)/2;
+        int dy = (3*gr_fb_height() + iconHeight - 2*height)/4;
+
+        // Erase behind the progress bar (in case this was a progress-only update)
+        gr_color(0, 0, 0, 255);
+        gr_fill(dx, dy, width, height);
+
+        if (gProgressBarType == PROGRESSBAR_TYPE_NORMAL) {
+            float progress = gProgressScopeStart + gProgress * gProgressScopeSize;
+            int pos = (int) (progress * width);
+
+            if (pos > 0) {
+                gr_blit(gProgressBarFill, 0, 0, pos, height, dx, dy);
+            }
+            if (pos < width-1) {
+                gr_blit(gProgressBarEmpty, pos, 0, width-pos, height, dx+pos, dy);
+            }
+        }
+
+        if (gProgressBarType == PROGRESSBAR_TYPE_INDETERMINATE) {
+            static int frame = 0;
+            gr_blit(gProgressBarIndeterminate[frame], 0, 0, width, height, dx, dy);
+            frame = (frame + 1) % ui_parameters.indeterminate_frames;
+        }
     }
 }
 
@@ -203,7 +240,7 @@
         draw_screen_locked();    // Must redraw the whole screen
         gPagesIdentical = 1;
     } else {
-        draw_progress_locked();  // Draw only the progress bar
+        draw_progress_locked();  // Draw only the progress bar and overlays
     }
     gr_flip();
 }
@@ -211,29 +248,49 @@
 // Keeps the progress bar updated, even when the process is otherwise busy.
 static void *progress_thread(void *cookie)
 {
+    double interval = 1.0 / ui_parameters.update_fps;
     for (;;) {
-        usleep(1000000 / PROGRESSBAR_INDETERMINATE_FPS);
+        double start = now();
         pthread_mutex_lock(&gUpdateMutex);
 
+        int redraw = 0;
+
+        // update the installation animation, if active
+        // skip this if we have a text overlay (too expensive to update)
+        if (gCurrentIcon == BACKGROUND_ICON_INSTALLING &&
+            ui_parameters.installing_frames > 0 &&
+            !show_text) {
+            gInstallingFrame =
+                (gInstallingFrame + 1) % ui_parameters.installing_frames;
+            redraw = 1;
+        }
+
         // update the progress bar animation, if active
         // skip this if we have a text overlay (too expensive to update)
         if (gProgressBarType == PROGRESSBAR_TYPE_INDETERMINATE && !show_text) {
-            update_progress_locked();
+            redraw = 1;
         }
 
         // move the progress bar forward on timed intervals, if configured
         int duration = gProgressScopeDuration;
         if (gProgressBarType == PROGRESSBAR_TYPE_NORMAL && duration > 0) {
-            int elapsed = time(NULL) - gProgressScopeTime;
+            double elapsed = now() - gProgressScopeTime;
             float progress = 1.0 * elapsed / duration;
             if (progress > 1.0) progress = 1.0;
             if (progress > gProgress) {
                 gProgress = progress;
-                update_progress_locked();
+                redraw = 1;
             }
         }
 
+        if (redraw) update_progress_locked();
+
         pthread_mutex_unlock(&gUpdateMutex);
+        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));
     }
     return NULL;
 }
@@ -295,12 +352,13 @@
         if (ev.value > 0 && device_toggle_display(key_pressed, ev.code)) {
             pthread_mutex_lock(&gUpdateMutex);
             show_text = !show_text;
+            if (show_text) show_text_ever = 1;
             update_screen_locked();
             pthread_mutex_unlock(&gUpdateMutex);
         }
 
         if (ev.value > 0 && device_reboot_now(key_pressed, ev.code)) {
-            reboot(RB_AUTOBOOT);
+            android_reboot(ANDROID_RB_RESTART, 0, 0);
         }
     }
     return NULL;
@@ -323,15 +381,49 @@
     for (i = 0; BITMAPS[i].name != NULL; ++i) {
         int result = res_create_surface(BITMAPS[i].name, BITMAPS[i].surface);
         if (result < 0) {
-            if (result == -2) {
-                LOGI("Bitmap %s missing header\n", BITMAPS[i].name);
-            } else {
-                LOGE("Missing bitmap %s\n(Code %d)\n", BITMAPS[i].name, result);
-            }
-            *BITMAPS[i].surface = NULL;
+            LOGE("Missing bitmap %s\n(Code %d)\n", BITMAPS[i].name, result);
         }
     }
 
+    gProgressBarIndeterminate = malloc(ui_parameters.indeterminate_frames *
+                                       sizeof(gr_surface));
+    for (i = 0; i < ui_parameters.indeterminate_frames; ++i) {
+        char filename[40];
+        // "indeterminate01.png", "indeterminate02.png", ...
+        sprintf(filename, "indeterminate%02d", i+1);
+        int result = res_create_surface(filename, gProgressBarIndeterminate+i);
+        if (result < 0) {
+            LOGE("Missing bitmap %s\n(Code %d)\n", filename, result);
+        }
+    }
+
+    if (ui_parameters.installing_frames > 0) {
+        gInstallationOverlay = malloc(ui_parameters.installing_frames *
+                                      sizeof(gr_surface));
+        for (i = 0; i < ui_parameters.installing_frames; ++i) {
+            char filename[40];
+            // "icon_installing_overlay01.png",
+            // "icon_installing_overlay02.png", ...
+            sprintf(filename, "icon_installing_overlay%02d", i+1);
+            int result = res_create_surface(filename, gInstallationOverlay+i);
+            if (result < 0) {
+                LOGE("Missing bitmap %s\n(Code %d)\n", filename, result);
+            }
+        }
+
+        // Adjust the offset to account for the positioning of the
+        // base image on the screen.
+        if (gBackgroundIcon[BACKGROUND_ICON_INSTALLING] != NULL) {
+            gr_surface bg = gBackgroundIcon[BACKGROUND_ICON_INSTALLING];
+            ui_parameters.install_overlay_offset_x +=
+                (gr_fb_width() - gr_get_width(bg)) / 2;
+            ui_parameters.install_overlay_offset_y +=
+                (gr_fb_height() - gr_get_height(bg)) / 2;
+        }
+    } else {
+        gInstallationOverlay = NULL;
+    }
+
     pthread_t t;
     pthread_create(&t, NULL, progress_thread, NULL);
     pthread_create(&t, NULL, input_thread, NULL);
@@ -340,7 +432,7 @@
 void ui_set_background(int icon)
 {
     pthread_mutex_lock(&gUpdateMutex);
-    gCurrentIcon = gBackgroundIcon[icon];
+    gCurrentIcon = icon;
     update_screen_locked();
     pthread_mutex_unlock(&gUpdateMutex);
 }
@@ -361,7 +453,7 @@
     gProgressBarType = PROGRESSBAR_TYPE_NORMAL;
     gProgressScopeStart += gProgressScopeSize;
     gProgressScopeSize = portion;
-    gProgressScopeTime = time(NULL);
+    gProgressScopeTime = now();
     gProgressScopeDuration = seconds;
     gProgress = 0;
     update_progress_locked();
@@ -481,23 +573,67 @@
     return visible;
 }
 
+int ui_text_ever_visible()
+{
+    pthread_mutex_lock(&gUpdateMutex);
+    int ever_visible = show_text_ever;
+    pthread_mutex_unlock(&gUpdateMutex);
+    return ever_visible;
+}
+
 void ui_show_text(int visible)
 {
     pthread_mutex_lock(&gUpdateMutex);
     show_text = visible;
+    if (show_text) show_text_ever = 1;
     update_screen_locked();
     pthread_mutex_unlock(&gUpdateMutex);
 }
 
+// Return true if USB is connected.
+static int usb_connected() {
+    int fd = open("/sys/class/switch/usb_connected/state", O_RDONLY);
+    if (fd < 0) {
+        printf("failed to open /sys/class/switch/usb_connected/state: %s\n",
+               strerror(errno));
+        return 0;
+    }
+
+    char buf;
+    int connected = (read(fd, &buf, 1) == 1) && (buf == '1');
+    if (close(fd) < 0) {
+        printf("failed to close /sys/class/switch/usb_connected/state: %s\n",
+               strerror(errno));
+    }
+    return connected;
+}
+
 int ui_wait_key()
 {
     pthread_mutex_lock(&key_queue_mutex);
-    while (key_queue_len == 0) {
-        pthread_cond_wait(&key_queue_cond, &key_queue_mutex);
-    }
 
-    int key = key_queue[0];
-    memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len);
+    // Time out after UI_WAIT_KEY_TIMEOUT_SEC, unless a USB cable is
+    // plugged in.
+    do {
+        struct timeval now;
+        struct timespec timeout;
+        gettimeofday(&now, NULL);
+        timeout.tv_sec = now.tv_sec;
+        timeout.tv_nsec = now.tv_usec * 1000;
+        timeout.tv_sec += UI_WAIT_KEY_TIMEOUT_SEC;
+
+        int rc = 0;
+        while (key_queue_len == 0 && rc != ETIMEDOUT) {
+            rc = pthread_cond_timedwait(&key_queue_cond, &key_queue_mutex,
+                                        &timeout);
+        }
+    } while (usb_connected() && key_queue_len == 0);
+
+    int key = -1;
+    if (key_queue_len > 0) {
+        key = key_queue[0];
+        memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len);
+    }
     pthread_mutex_unlock(&key_queue_mutex);
     return key;
 }
diff --git a/updater/Android.mk b/updater/Android.mk
index c8f61f4..e3d62bc 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -27,6 +27,7 @@
 LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UPDATER_LIBS) $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS)
 LOCAL_STATIC_LIBRARIES += libapplypatch libedify libmtdutils libminzip libz
 LOCAL_STATIC_LIBRARIES += libmincrypt libbz
+LOCAL_STATIC_LIBRARIES += libminelf
 LOCAL_STATIC_LIBRARIES += libcutils libstdc++ libc
 LOCAL_C_INCLUDES += $(LOCAL_PATH)/..
 
diff --git a/updater/install.c b/updater/install.c
index a596dc6..6a79964 100644
--- a/updater/install.c
+++ b/updater/install.c
@@ -25,12 +25,15 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
 
 #include "cutils/misc.h"
 #include "cutils/properties.h"
 #include "edify/expr.h"
 #include "mincrypt/sha.h"
 #include "minzip/DirUtil.h"
+#include "minelf/Retouch.h"
 #include "mtdutils/mounts.h"
 #include "mtdutils/mtdutils.h"
 #include "updater.h"
@@ -174,19 +177,23 @@
 }
 
 
-// format(fs_type, partition_type, location)
+// format(fs_type, partition_type, location, fs_size)
 //
-//    fs_type="yaffs2" partition_type="MTD"     location=partition
-//    fs_type="ext4"   partition_type="EMMC"    location=device
+//    fs_type="yaffs2" partition_type="MTD"     location=partition fs_size=<bytes>
+//    fs_type="ext4"   partition_type="EMMC"    location=device    fs_size=<bytes>
+//    if fs_size == 0, then make_ext4fs uses the entire partition.
+//    if fs_size > 0, that is the size to use
+//    if fs_size < 0, then reserve that many bytes at the end of the partition
 Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
     char* result = NULL;
-    if (argc != 3) {
-        return ErrorAbort(state, "%s() expects 3 args, got %d", name, argc);
+    if (argc != 4) {
+        return ErrorAbort(state, "%s() expects 4 args, got %d", name, argc);
     }
     char* fs_type;
     char* partition_type;
     char* location;
-    if (ReadArgs(state, argv, 3, &fs_type, &partition_type, &location) < 0) {
+    char* fs_size;
+    if (ReadArgs(state, argv, 4, &fs_type, &partition_type, &location, &fs_size) < 0) {
         return NULL;
     }
 
@@ -233,8 +240,7 @@
         result = location;
 #ifdef USE_EXT4
     } else if (strcmp(fs_type, "ext4") == 0) {
-        reset_ext4fs_info();
-        int status = make_ext4fs(location, NULL, NULL, 0, 0, 0);
+        int status = make_ext4fs(location, atoll(fs_size));
         if (status != 0) {
             fprintf(stderr, "%s: make_ext4fs failed (%d) on %s",
                     name, status, location);
@@ -429,6 +435,121 @@
 }
 
 
+// retouch_binaries(lib1, lib2, ...)
+Value* RetouchBinariesFn(const char* name, State* state,
+                         int argc, Expr* argv[]) {
+    UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
+
+    char **retouch_entries  = ReadVarArgs(state, argc, argv);
+    if (retouch_entries == NULL) {
+        return StringValue(strdup("t"));
+    }
+
+    // some randomness from the clock
+    int32_t override_base;
+    bool override_set = false;
+    int32_t random_base = time(NULL) % 1024;
+    // some more randomness from /dev/random
+    FILE *f_random = fopen("/dev/random", "rb");
+    uint16_t random_bits = 0;
+    if (f_random != NULL) {
+        fread(&random_bits, 2, 1, f_random);
+        random_bits = random_bits % 1024;
+        fclose(f_random);
+    }
+    random_base = (random_base + random_bits) % 1024;
+    fprintf(ui->cmd_pipe, "ui_print Random offset: 0x%x\n", random_base);
+    fprintf(ui->cmd_pipe, "ui_print\n");
+
+    // make sure we never randomize to zero; this let's us look at a file
+    // and know for sure whether it has been processed; important in the
+    // crash recovery process
+    if (random_base == 0) random_base = 1;
+    // make sure our randomization is page-aligned
+    random_base *= -0x1000;
+    override_base = random_base;
+
+    int i = 0;
+    bool success = true;
+    while (i < (argc - 1)) {
+        success = success && retouch_one_library(retouch_entries[i],
+                                                 retouch_entries[i+1],
+                                                 random_base,
+                                                 override_set ?
+                                                   NULL :
+                                                   &override_base);
+        if (!success)
+            ErrorAbort(state, "Failed to retouch '%s'.", retouch_entries[i]);
+
+        free(retouch_entries[i]);
+        free(retouch_entries[i+1]);
+        i += 2;
+
+        if (success && override_base != 0) {
+            random_base = override_base;
+            override_set = true;
+        }
+    }
+    if (i < argc) {
+        free(retouch_entries[i]);
+        success = false;
+    }
+    free(retouch_entries);
+
+    if (!success) {
+      Value* v = malloc(sizeof(Value));
+      v->type = VAL_STRING;
+      v->data = NULL;
+      v->size = -1;
+      return v;
+    }
+    return StringValue(strdup("t"));
+}
+
+
+// undo_retouch_binaries(lib1, lib2, ...)
+Value* UndoRetouchBinariesFn(const char* name, State* state,
+                             int argc, Expr* argv[]) {
+    UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
+
+    char **retouch_entries  = ReadVarArgs(state, argc, argv);
+    if (retouch_entries == NULL) {
+        return StringValue(strdup("t"));
+    }
+
+    int i = 0;
+    bool success = true;
+    int32_t override_base;
+    while (i < (argc-1)) {
+        success = success && retouch_one_library(retouch_entries[i],
+                                                 retouch_entries[i+1],
+                                                 0 /* undo => offset==0 */,
+                                                 NULL);
+        if (!success)
+            ErrorAbort(state, "Failed to unretouch '%s'.",
+                       retouch_entries[i]);
+
+        free(retouch_entries[i]);
+        free(retouch_entries[i+1]);
+        i += 2;
+    }
+    if (i < argc) {
+        free(retouch_entries[i]);
+        success = false;
+    }
+    free(retouch_entries);
+
+    if (!success) {
+      Value* v = malloc(sizeof(Value));
+      v->type = VAL_STRING;
+      v->data = NULL;
+      v->size = -1;
+      return v;
+    }
+    return StringValue(strdup("t"));
+}
+
+
 // symlink target src1 src2 ...
 //    unlinks any previously existing src1, src2, etc before creating symlinks.
 Value* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) {
@@ -1007,7 +1128,7 @@
     return args[i];
 }
 
-// Read a local file and return its contents (the char* returned
+// Read a local file and return its contents (the Value* returned
 // is actually a FileContents*).
 Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) {
     if (argc != 1) {
@@ -1020,7 +1141,7 @@
     v->type = VAL_BLOB;
 
     FileContents fc;
-    if (LoadFileContents(filename, &fc) != 0) {
+    if (LoadFileContents(filename, &fc, RETOUCH_DONT_MASK) != 0) {
         ErrorAbort(state, "%s() loading \"%s\" failed: %s",
                    name, filename, strerror(errno));
         free(filename);
@@ -1047,6 +1168,8 @@
     RegisterFunction("delete_recursive", DeleteFn);
     RegisterFunction("package_extract_dir", PackageExtractDirFn);
     RegisterFunction("package_extract_file", PackageExtractFileFn);
+    RegisterFunction("retouch_binaries", RetouchBinariesFn);
+    RegisterFunction("undo_retouch_binaries", UndoRetouchBinariesFn);
     RegisterFunction("symlink", SymlinkFn);
     RegisterFunction("set_perm", SetPermFn);
     RegisterFunction("set_perm_recursive", SetPermFn);
diff --git a/verifier.c b/verifier.c
index 9d39fd1..729e085 100644
--- a/verifier.c
+++ b/verifier.c
@@ -173,7 +173,7 @@
         // the signing tool appends after the signature itself.
         if (RSA_verify(pKeys+i, eocd + eocd_size - 6 - RSANUMBYTES,
                        RSANUMBYTES, sha1)) {
-            LOGI("whole-file signature verified\n");
+            LOGI("whole-file signature verified against key %d\n", i);
             free(eocd);
             return VERIFY_SUCCESS;
         }