summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk16
-rw-r--r--applypatch/Android.mk6
-rw-r--r--applypatch/applypatch.c42
-rw-r--r--applypatch/applypatch.h9
-rw-r--r--applypatch/main.c2
-rw-r--r--bootloader.c21
-rw-r--r--common.h26
-rw-r--r--default_recovery_ui.c5
-rw-r--r--encryptedfs_provisioning.c283
-rw-r--r--encryptedfs_provisioning.h51
-rw-r--r--make-overlay.py102
-rw-r--r--minelf/Android.mk27
-rw-r--r--minelf/Retouch.c406
-rw-r--r--minelf/Retouch.h51
-rw-r--r--minui/Android.mk4
-rw-r--r--minui/graphics.c28
-rw-r--r--minui/resources.c11
-rw-r--r--mtdutils/Android.mk2
-rw-r--r--recovery.c82
-rw-r--r--recovery_ui.h9
-rw-r--r--[-rwxr-xr-x]res/images/icon_error.pngbin10398 -> 14566 bytes
-rw-r--r--[-rwxr-xr-x]res/images/icon_installing.pngbin11493 -> 8046 bytes
-rw-r--r--res/images/icon_installing_overlay01.pngbin0 -> 15591 bytes
-rw-r--r--res/images/icon_installing_overlay02.pngbin0 -> 15487 bytes
-rw-r--r--res/images/icon_installing_overlay03.pngbin0 -> 15467 bytes
-rw-r--r--res/images/icon_installing_overlay04.pngbin0 -> 15561 bytes
-rw-r--r--res/images/icon_installing_overlay05.pngbin0 -> 15506 bytes
-rw-r--r--res/images/icon_installing_overlay06.pngbin0 -> 15471 bytes
-rw-r--r--res/images/icon_installing_overlay07.pngbin0 -> 15645 bytes
-rw-r--r--res/images/indeterminate01.pngbin0 -> 929 bytes
-rw-r--r--res/images/indeterminate02.pngbin0 -> 972 bytes
-rw-r--r--res/images/indeterminate03.pngbin0 -> 976 bytes
-rw-r--r--res/images/indeterminate04.pngbin0 -> 977 bytes
-rw-r--r--res/images/indeterminate05.pngbin0 -> 1024 bytes
-rw-r--r--res/images/indeterminate06.pngbin0 -> 968 bytes
-rw-r--r--res/images/indeterminate1.pngbin1919 -> 0 bytes
-rw-r--r--res/images/indeterminate2.pngbin1912 -> 0 bytes
-rw-r--r--res/images/indeterminate3.pngbin1917 -> 0 bytes
-rw-r--r--res/images/indeterminate4.pngbin1912 -> 0 bytes
-rw-r--r--res/images/indeterminate5.pngbin1902 -> 0 bytes
-rw-r--r--res/images/indeterminate6.pngbin1914 -> 0 bytes
-rw-r--r--res/images/progress_empty.pngbin361 -> 1117 bytes
-rw-r--r--res/images/progress_fill.pngbin286 -> 2885 bytes
-rw-r--r--roots.c40
-rw-r--r--ui.c248
-rw-r--r--updater/Android.mk1
-rw-r--r--updater/install.c143
-rw-r--r--verifier.c2
48 files changed, 1105 insertions, 512 deletions
diff --git a/Android.mk b/Android.mk
index e6c354759..508eb4c64 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 @@ LOCAL_SRC_FILES := \
install.c \
roots.c \
ui.c \
- verifier.c \
- encryptedfs_provisioning.c
+ verifier.c
LOCAL_MODULE := recovery
@@ -22,6 +20,14 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true
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_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
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 $(BUILD_EXECUTABLE)
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)/updater/Android.mk
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 e91e4bf88..2848b517e 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 @@ include $(CLEAR_VARS)
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_MODULE := applypatch_static
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 @@ LOCAL_STATIC_LIBRARIES += libz libbz
include $(BUILD_HOST_EXECUTABLE)
-endif # TARGET_ARCH == arm
endif # !TARGET_SIMULATOR
diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c
index fd8153aec..106091386 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 @@ int LoadFileContents(const char* filename, FileContents* file) {
}
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 @@ static int LoadPartitionContents(const char* filename, FileContents* file) {
// 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 @@ int applypatch_check(const char* filename,
// 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 @@ int applypatch_check(const char* filename,
// 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 applypatch(const char* source_filename,
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 @@ int applypatch(const char* source_filename,
// 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 @@ int applypatch(const char* source_filename,
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 10c01259a..a78c89bfe 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 applypatch_check(const char* filename,
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 3917f86ed..7025a2e2e 100644
--- a/applypatch/main.c
+++ b/applypatch/main.c
@@ -74,7 +74,7 @@ static int ParsePatchArgs(int argc, char** argv,
(*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 b690c5582..709656602 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 @@ static int set_bootloader_message_mtd(const struct bootloader_message *in,
// 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 get_bootloader_message_block(struct bootloader_message *out,
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 97e87eec5..cba4c861d 100644
--- a/common.h
+++ b/common.h
@@ -26,6 +26,7 @@ void ui_init();
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 @@ typedef struct {
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 ce12787fe..7c4017e7e 100644
--- a/default_recovery_ui.c
+++ b/default_recovery_ui.c
@@ -24,11 +24,14 @@ char* MENU_HEADERS[] = { "Android system recovery utility",
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 601c817de..000000000
--- 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 284605d20..000000000
--- 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 000000000..7f931b373
--- /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 000000000..0f41ff528
--- /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 000000000..33809cd6d
--- /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 000000000..048d78e44
--- /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 91dd939f6..7ded5d3d0 100644
--- a/minui/Android.mk
+++ b/minui/Android.mk
@@ -9,4 +9,8 @@ LOCAL_C_INCLUDES +=\
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 4127c400a..42c85e777 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 @@ static int get_framebuffer(GGLSurface *fb)
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 @@ static int get_framebuffer(GGLSurface *fb)
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 @@ static void get_memory_surface(GGLSurface* ms) {
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 @@ void gr_flip(void)
/* 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 3d2c727fb..b437a87cb 100644
--- a/minui/resources.c
+++ b/minui/resources.c
@@ -49,6 +49,8 @@ int res_create_surface(const char* name, gr_surface* pSurface) {
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 @@ int res_create_surface(const char* name, gr_surface* pSurface) {
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 57ab579b2..416653698 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_STATIC_LIBRARIES := libmtdutils
LOCAL_SHARED_LIBRARIES := libcutils libc
include $(BUILD_EXECUTABLE)
-endif # TARGET_ARCH == arm
endif # !TARGET_SIMULATOR
diff --git a/recovery.c b/recovery.c
index 9ad075d74..fd7ce42f1 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 *SDCARD_ROOT = "/sdcard";
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 @@ static const char *SIDELOAD_TEMP_DIR = "/tmp/sideload";
* 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 @@ get_menu_selection(char** headers, char** items, int menu_only,
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 @@ main(int argc, char **argv) {
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 @@ main(int argc, char **argv) {
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 @@ main(int argc, char **argv) {
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 @@ main(int argc, char **argv) {
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 @@ main(int argc, char **argv) {
// 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 e451bdfa2..e56a24b29 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 @@ int device_wipe_data();
#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
index 90c8d878a..446c3fb19 100755..100644
--- 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
index d428f57d1..7e42628e7 100755..100644
--- 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 000000000..2c4c89a8b
--- /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 000000000..b63552bca
--- /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 000000000..6acb5ef63
--- /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 000000000..0d4608aa4
--- /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 000000000..4bfacb269
--- /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 000000000..5fd252862
--- /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 000000000..350b3814d
--- /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 000000000..84f04b0d2
--- /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 000000000..21d012168
--- /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 000000000..8a190a8c6
--- /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 000000000..baf8d63e4
--- /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 000000000..5653c7b06
--- /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 000000000..858de85a5
--- /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 90cb9fba9..000000000
--- 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 f7fb28989..000000000
--- 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 ba10dfa53..000000000
--- 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 ad5d9a542..000000000
--- 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 8c19c8d57..000000000
--- 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 c0c66386a..000000000
--- 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 4cb4998dd..c6820159b 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 eb71754db..9748435f5 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 90b82d7ed..cb7e067a1 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 @@ void load_volume_table() {
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 @@ void load_volume_table() {
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 @@ void load_volume_table() {
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 @@ void load_volume_table() {
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 @@ int format_volume(const char* volume) {
}
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 84a9b85c2..0744da4d9 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 @@ static enum ProgressBarType {
// 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 char text[MAX_ROWS][MAX_COLS];
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 pthread_cond_t key_queue_cond = PTHREAD_COND_INITIALIZER;
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 @@ static void draw_background_locked(gr_surface icon)
// Should only be called with gUpdateMutex locked.
static void draw_progress_locked()
{
- if (gProgressBarType == PROGRESSBAR_TYPE_NONE) return;
+ if (gCurrentIcon == BACKGROUND_ICON_INSTALLING) {
+ draw_install_overlay_locked(gInstallingFrame);
+ }
- int iconHeight = gr_get_height(gBackgroundIcon[BACKGROUND_ICON_INSTALLING]);
- int width = gr_get_width(gProgressBarEmpty);
- int height = gr_get_height(gProgressBarEmpty);
+ 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;
+ 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);
+ // 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 (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 (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) % PROGRESSBAR_INDETERMINATE_STATES;
+ 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 @@ static void update_progress_locked(void)
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 @@ static void update_progress_locked(void)
// 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 @@ static void *input_thread(void *cookie)
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,13 +381,47 @@ void ui_init(void)
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);
+ 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);
}
- *BITMAPS[i].surface = NULL;
}
+
+ // 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;
@@ -340,7 +432,7 @@ void ui_init(void)
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 @@ void ui_show_progress(float portion, int seconds)
gProgressBarType = PROGRESSBAR_TYPE_NORMAL;
gProgressScopeStart += gProgressScopeSize;
gProgressScopeSize = portion;
- gProgressScopeTime = time(NULL);
+ gProgressScopeTime = now();
gProgressScopeDuration = seconds;
gProgress = 0;
update_progress_locked();
@@ -481,23 +573,67 @@ int ui_text_visible()
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 c8f61f41d..e3d62bcbe 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -27,6 +27,7 @@ endif
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 a596dc654..6a7996467 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 @@ done:
}
-// 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 @@ Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
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 @@ Value* PackageExtractFileFn(const char* name, State* state,
}
+// 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 @@ Value* Sha1CheckFn(const char* name, State* state, int argc, Expr* argv[]) {
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 @@ Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) {
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 @@ void RegisterInstallFunctions() {
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 9d39fd139..729e085cf 100644
--- a/verifier.c
+++ b/verifier.c
@@ -173,7 +173,7 @@ int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKey
// 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;
}