Update to latest AOSP master
Merge in latest commits from AOSP master and fix merge conflicts
diff --git a/Android.mk b/Android.mk
index 4a2238c..8d285c3 100644
--- a/Android.mk
+++ b/Android.mk
@@ -40,15 +40,27 @@
#LOCAL_FORCE_STATIC_EXECUTABLE := true
RECOVERY_API_VERSION := 3
+RECOVERY_FSTAB_VERSION := 2
LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
#LOCAL_STATIC_LIBRARIES := \
-# libext4_utils \
+# libext4_utils_static \
+# libsparse_static \
# libminzip \
+# libz \
# libmtdutils \
# libmincrypt \
# libminadbd \
-# libpixelflinger_static
+# libminui \
+# libpixelflinger_static \
+# libpng \
+# libfs_mgr \
+# libcutils \
+# liblog \
+# libselinux \
+# libstdc++ \
+# libm \
+# libc
LOCAL_C_INCLUDES += bionic external/stlport/stlport
@@ -67,7 +79,7 @@
LOCAL_C_INCLUDES += system/extras/ext4_utils
LOCAL_SHARED_LIBRARIES += libext4_utils
endif
-LOCAL_C_INCLUDES += external/libselinux/include
+
ifeq ($(HAVE_SELINUX), true)
#LOCAL_C_INCLUDES += external/libselinux/include
#LOCAL_STATIC_LIBRARIES += libselinux
@@ -90,11 +102,11 @@
# TODO: Build the ramdisk image in a more principled way.
LOCAL_MODULE_TAGS := eng
-ifeq ($(TARGET_RECOVERY_UI_LIB),)
+#ifeq ($(TARGET_RECOVERY_UI_LIB),)
LOCAL_SRC_FILES += default_device.cpp
-else
- LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB)
-endif
+#else
+# LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB)
+#endif
LOCAL_C_INCLUDES += system/extras/ext4_utils
@@ -289,6 +301,9 @@
LOCAL_MODULE_TAGS := eng
LOCAL_MODULES_TAGS = optional
LOCAL_CFLAGS =
+ifneq ($(wildcard system/core/libmincrypt/rsa_e_3.c),)
+ LOCAL_CFLAGS += -DHAS_EXPONENT
+endif
LOCAL_SRC_FILES = adb_install.cpp bootloader.cpp verifier.cpp mtdutils/mtdutils.c
LOCAL_SHARED_LIBRARIES += libc liblog libcutils libmtdutils
LOCAL_STATIC_LIBRARIES += libmincrypt
diff --git a/CleanSpec.mk b/CleanSpec.mk
index b84e1b6..ecf89ae 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -47,3 +47,4 @@
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/recovery_intermediates)
diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c
index 488fd8c..0dcdce0 100644
--- a/applypatch/applypatch.c
+++ b/applypatch/applypatch.c
@@ -39,7 +39,8 @@
const char* source_filename,
const char* target_filename,
const uint8_t target_sha1[SHA_DIGEST_SIZE],
- size_t target_size);
+ size_t target_size,
+ const Value* bonus_data);
static int mtd_partitions_scanned = 0;
@@ -420,18 +421,111 @@
break;
case EMMC:
- ;
- FILE* f = fopen(partition, "wb");
- if (fwrite(data, 1, len, f) != len) {
- printf("short write writing to %s (%s)\n",
- partition, strerror(errno));
+ {
+ size_t start = 0;
+ int success = 0;
+ int fd = open(partition, O_RDWR | O_SYNC);
+ if (fd < 0) {
+ printf("failed to open %s: %s\n", partition, strerror(errno));
return -1;
}
- if (fclose(f) != 0) {
+ int attempt;
+
+ for (attempt = 0; attempt < 10; ++attempt) {
+ size_t next_sync = start + (1<<20);
+ printf("raw O_SYNC write %s attempt %d start at %d\n", partition, attempt+1, start);
+ lseek(fd, start, SEEK_SET);
+ while (start < len) {
+ size_t to_write = len - start;
+ if (to_write > 4096) to_write = 4096;
+
+ ssize_t written = write(fd, data+start, to_write);
+ if (written < 0) {
+ if (errno == EINTR) {
+ written = 0;
+ } else {
+ printf("failed write writing to %s (%s)\n",
+ partition, strerror(errno));
+ return -1;
+ }
+ }
+ start += written;
+ if (start >= next_sync) {
+ fsync(fd);
+ next_sync = start + (1<<20);
+ }
+ }
+ fsync(fd);
+
+ // drop caches so our subsequent verification read
+ // won't just be reading the cache.
+ sync();
+ int dc = open("/proc/sys/vm/drop_caches", O_WRONLY);
+ write(dc, "3\n", 2);
+ close(dc);
+ sleep(1);
+ printf(" caches dropped\n");
+
+ // verify
+ lseek(fd, 0, SEEK_SET);
+ unsigned char buffer[4096];
+ start = len;
+ size_t p;
+ for (p = 0; p < len; p += sizeof(buffer)) {
+ size_t to_read = len - p;
+ if (to_read > sizeof(buffer)) to_read = sizeof(buffer);
+
+ size_t so_far = 0;
+ while (so_far < to_read) {
+ ssize_t read_count = read(fd, buffer+so_far, to_read-so_far);
+ if (read_count < 0) {
+ if (errno == EINTR) {
+ read_count = 0;
+ } else {
+ printf("verify read error %s at %d: %s\n",
+ partition, p, strerror(errno));
+ return -1;
+ }
+ }
+ if ((size_t)read_count < to_read) {
+ printf("short verify read %s at %d: %d %d %s\n",
+ partition, p, read_count, to_read, strerror(errno));
+ }
+ so_far += read_count;
+ }
+
+ if (memcmp(buffer, data+p, to_read)) {
+ printf("verification failed starting at %d\n", p);
+ start = p;
+ break;
+ }
+ }
+
+ if (start == len) {
+ printf("verification read succeeded (attempt %d)\n", attempt+1);
+ success = true;
+ break;
+ }
+
+ sleep(2);
+ }
+
+ if (!success) {
+ printf("failed to verify after all attempts\n");
+ return -1;
+ }
+
+ if (close(fd) != 0) {
printf("error closing %s (%s)\n", partition, strerror(errno));
return -1;
}
+ // hack: sync and sleep after closing in hopes of getting
+ // the data actually onto flash.
+ printf("sleeping after close\n");
+ sync();
+ sleep(5);
break;
+ }
}
free(copy);
@@ -472,7 +566,7 @@
// Search an array of sha1 strings for one matching the given sha1.
// Return the index of the match on success, or -1 if no match is
// found.
-int FindMatchingPatch(uint8_t* sha1, const char** patch_sha1_str,
+int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str,
int num_patches) {
int i;
uint8_t patch_sha1[SHA_DIGEST_SIZE];
@@ -584,6 +678,14 @@
}
}
+static void print_short_sha1(const uint8_t sha1[SHA_DIGEST_SIZE]) {
+ int i;
+ const char* hex = "0123456789abcdef";
+ for (i = 0; i < 4; ++i) {
+ putchar(hex[(sha1[i]>>4) & 0xf]);
+ putchar(hex[sha1[i] & 0xf]);
+ }
+}
// This function applies binary patches to files in a way that is safe
// (the original file is not touched until we have the desired
@@ -617,8 +719,9 @@
size_t target_size,
int num_patches,
char** const patch_sha1_str,
- Value** patch_data) {
- printf("\napplying patch to %s\n", source_filename);
+ Value** patch_data,
+ Value* bonus_data) {
+ printf("patch %s: ", source_filename);
if (target_filename[0] == '-' &&
target_filename[1] == '\0') {
@@ -644,8 +747,9 @@
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.
- printf("\"%s\" is already target; no patch needed\n",
- target_filename);
+ printf("already ");
+ print_short_sha1(target_sha1);
+ putchar('\n');
free(source_file.data);
return 0;
}
@@ -699,7 +803,7 @@
int result = GenerateTarget(&source_file, source_patch_value,
©_file, copy_patch_value,
source_filename, target_filename,
- target_sha1, target_size);
+ target_sha1, target_size, bonus_data);
free(source_file.data);
free(copy_file.data);
@@ -713,7 +817,8 @@
const char* source_filename,
const char* target_filename,
const uint8_t target_sha1[SHA_DIGEST_SIZE],
- size_t target_size) {
+ size_t target_size,
+ const Value* bonus_data) {
int retry = 1;
SHA_CTX ctx;
int output;
@@ -766,8 +871,10 @@
enough_space =
(free_space > (256 << 10)) && // 256k (two-block) minimum
(free_space > (target_size * 3 / 2)); // 50% margin of error
- printf("target %ld bytes; free space %ld bytes; retry %d; enough %d\n",
- (long)target_size, (long)free_space, retry, enough_space);
+ if (!enough_space) {
+ printf("target %ld bytes; free space %ld bytes; retry %d; enough %d\n",
+ (long)target_size, (long)free_space, retry, enough_space);
+ }
}
if (!enough_space) {
@@ -802,7 +909,7 @@
unlink(source_filename);
size_t free_space = FreeSpaceForFile(target_fs);
- printf("(now %ld bytes free for target)\n", (long)free_space);
+ printf("(now %ld bytes free for target) ", (long)free_space);
}
}
@@ -867,7 +974,7 @@
} else if (header_bytes_read >= 8 &&
memcmp(header, "IMGDIFF2", 8) == 0) {
result = ApplyImagePatch(source_to_use->data, source_to_use->size,
- patch, sink, token, &ctx);
+ patch, sink, token, &ctx, bonus_data);
} else {
printf("Unknown patch file format\n");
return 1;
@@ -898,6 +1005,10 @@
if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_SIZE) != 0) {
printf("patch did not produce expected sha1\n");
return 1;
+ } else {
+ printf("now ");
+ print_short_sha1(target_sha1);
+ putchar('\n');
}
if (output < 0) {
diff --git a/applypatch/applypatch.h b/applypatch/applypatch.h
index fb58843..f1f13a1 100644
--- a/applypatch/applypatch.h
+++ b/applypatch/applypatch.h
@@ -55,7 +55,8 @@
size_t target_size,
int num_patches,
char** const patch_sha1_str,
- Value** patch_data);
+ Value** patch_data,
+ Value* bonus_data);
int applypatch_check(const char* filename,
int num_patches,
char** const patch_sha1_str);
@@ -64,7 +65,7 @@
int retouch_flag);
int SaveFileContents(const char* filename, const FileContents* file);
void FreeFileContents(FileContents* file);
-int FindMatchingPatch(uint8_t* sha1, const char** patch_sha1_str,
+int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str,
int num_patches);
// bsdiff.c
@@ -79,7 +80,8 @@
// imgpatch.c
int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
const Value* patch,
- SinkFn sink, void* token, SHA_CTX* ctx);
+ SinkFn sink, void* token, SHA_CTX* ctx,
+ const Value* bonus_data);
// freecache.c
int MakeFreeSpaceOnCache(size_t bytes_needed);
diff --git a/applypatch/imgdiff.c b/applypatch/imgdiff.c
index 6b9ebee..05c4f25 100644
--- a/applypatch/imgdiff.c
+++ b/applypatch/imgdiff.c
@@ -111,6 +111,14 @@
*
* After the header there are 'chunk count' bsdiff patches; the offset
* of each from the beginning of the file is specified in the header.
+ *
+ * This tool can take an optional file of "bonus data". This is an
+ * extra file of data that is appended to chunk #1 after it is
+ * compressed (it must be a CHUNK_DEFLATE chunk). The same file must
+ * be available (and passed to applypatch with -b) when applying the
+ * patch. This is used to reduce the size of recovery-from-boot
+ * patches by combining the boot image with recovery ramdisk
+ * information that is stored on the system partition.
*/
#include <errno.h>
@@ -772,21 +780,45 @@
}
int main(int argc, char** argv) {
- if (argc != 4 && argc != 5) {
- usage:
- printf("usage: %s [-z] <src-img> <tgt-img> <patch-file>\n",
- argv[0]);
- return 2;
- }
-
int zip_mode = 0;
- if (strcmp(argv[1], "-z") == 0) {
+ if (argc >= 2 && strcmp(argv[1], "-z") == 0) {
zip_mode = 1;
--argc;
++argv;
}
+ size_t bonus_size = 0;
+ unsigned char* bonus_data = NULL;
+ if (argc >= 3 && strcmp(argv[1], "-b") == 0) {
+ struct stat st;
+ if (stat(argv[2], &st) != 0) {
+ printf("failed to stat bonus file %s: %s\n", argv[2], strerror(errno));
+ return 1;
+ }
+ bonus_size = st.st_size;
+ bonus_data = malloc(bonus_size);
+ FILE* f = fopen(argv[2], "rb");
+ if (f == NULL) {
+ printf("failed to open bonus file %s: %s\n", argv[2], strerror(errno));
+ return 1;
+ }
+ if (fread(bonus_data, 1, bonus_size, f) != bonus_size) {
+ printf("failed to read bonus file %s: %s\n", argv[2], strerror(errno));
+ return 1;
+ }
+ fclose(f);
+
+ argc -= 2;
+ argv += 2;
+ }
+
+ if (argc != 4) {
+ usage:
+ printf("usage: %s [-z] [-b <bonus-file>] <src-img> <tgt-img> <patch-file>\n",
+ argv[0]);
+ return 2;
+ }
int num_src_chunks;
ImageChunk* src_chunks;
@@ -909,6 +941,8 @@
// Compute bsdiff patches for each chunk's data (the uncompressed
// data, in the case of deflate chunks).
+ DumpChunks(src_chunks, num_src_chunks);
+
printf("Construct patches for %d chunks...\n", num_tgt_chunks);
unsigned char** patch_data = malloc(num_tgt_chunks * sizeof(unsigned char*));
size_t* patch_size = malloc(num_tgt_chunks * sizeof(size_t));
@@ -923,6 +957,13 @@
patch_data[i] = MakePatch(src_chunks, tgt_chunks+i, patch_size+i);
}
} else {
+ if (i == 1 && bonus_data) {
+ printf(" using %d bytes of bonus data for chunk %d\n", bonus_size, i);
+ src_chunks[i].data = realloc(src_chunks[i].data, src_chunks[i].len + bonus_size);
+ memcpy(src_chunks[i].data+src_chunks[i].len, bonus_data, bonus_size);
+ src_chunks[i].len += bonus_size;
+ }
+
patch_data[i] = MakePatch(src_chunks+i, tgt_chunks+i, patch_size+i);
}
printf("patch %3d is %d bytes (of %d)\n",
diff --git a/applypatch/imgpatch.c b/applypatch/imgpatch.c
index e3ee80a..3a1df38 100644
--- a/applypatch/imgpatch.c
+++ b/applypatch/imgpatch.c
@@ -37,7 +37,8 @@
*/
int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
const Value* patch,
- SinkFn sink, void* token, SHA_CTX* ctx) {
+ SinkFn sink, void* token, SHA_CTX* ctx,
+ const Value* bonus_data) {
ssize_t pos = 12;
char* header = patch->data;
if (patch->size < 12) {
@@ -123,6 +124,12 @@
// Decompress the source data; the chunk header tells us exactly
// how big we expect it to be when decompressed.
+ // Note: expanded_len will include the bonus data size if
+ // the patch was constructed with bonus data. The
+ // deflation will come up 'bonus_size' bytes short; these
+ // must be appended from the bonus_data value.
+ size_t bonus_size = (i == 1 && bonus_data != NULL) ? bonus_data->size : 0;
+
unsigned char* expanded_source = malloc(expanded_len);
if (expanded_source == NULL) {
printf("failed to allocate %d bytes for expanded_source\n",
@@ -153,13 +160,19 @@
printf("source inflation returned %d\n", ret);
return -1;
}
- // We should have filled the output buffer exactly.
- if (strm.avail_out != 0) {
- printf("source inflation short by %d bytes\n", strm.avail_out);
+ // We should have filled the output buffer exactly, except
+ // for the bonus_size.
+ if (strm.avail_out != bonus_size) {
+ printf("source inflation short by %d bytes\n", strm.avail_out-bonus_size);
return -1;
}
inflateEnd(&strm);
+ if (bonus_size) {
+ memcpy(expanded_source + (expanded_len - bonus_size),
+ bonus_data->data, bonus_size);
+ }
+
// Next, apply the bsdiff patch (in memory) to the uncompressed
// data.
unsigned char* uncompressed_target_data;
diff --git a/applypatch/main.c b/applypatch/main.c
index 7025a2e..f61db5d 100644
--- a/applypatch/main.c
+++ b/applypatch/main.c
@@ -100,6 +100,21 @@
}
int PatchMode(int argc, char** argv) {
+ Value* bonus = NULL;
+ if (argc >= 3 && strcmp(argv[1], "-b") == 0) {
+ FileContents fc;
+ if (LoadFileContents(argv[2], &fc, RETOUCH_DONT_MASK) != 0) {
+ printf("failed to load bonus file %s\n", argv[2]);
+ return 1;
+ }
+ bonus = malloc(sizeof(Value));
+ bonus->type = VAL_BLOB;
+ bonus->size = fc.size;
+ bonus->data = (char*)fc.data;
+ argc -= 2;
+ argv += 2;
+ }
+
if (argc < 6) {
return 2;
}
@@ -120,7 +135,7 @@
}
int result = applypatch(argv[1], argv[2], argv[3], target_size,
- num_patches, sha1s, patches);
+ num_patches, sha1s, patches, bonus);
int i;
for (i = 0; i < num_patches; ++i) {
@@ -130,6 +145,10 @@
free(p);
}
}
+ if (bonus) {
+ free(bonus->data);
+ free(bonus);
+ }
free(sha1s);
free(patches);
@@ -163,7 +182,7 @@
if (argc < 2) {
usage:
printf(
- "usage: %s <src-file> <tgt-file> <tgt-sha1> <tgt-size> "
+ "usage: %s [-b <bonus-file>] <src-file> <tgt-file> <tgt-sha1> <tgt-size> "
"[<src-sha1>:<patch> ...]\n"
" or %s -c <file> [<sha1> ...]\n"
" or %s -s <bytes>\n"
diff --git a/bootloader.cpp b/bootloader.cpp
index ff0dc68..fbb31e0 100644
--- a/bootloader.cpp
+++ b/bootloader.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+/*
+#include <fs_mgr.h>
+*/
#include "bootloader.h"
#include "common.h"
extern "C" {
@@ -27,38 +30,42 @@
#include <sys/stat.h>
#include <unistd.h>
+static char device_type = 'e'; // e for emmc or m for mtd, default is emmc
+static char device_name[256];
+
+/*
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);
static int get_bootloader_message_block(struct bootloader_message *out, const Volume* v);
static int set_bootloader_message_block(const struct bootloader_message *in, const Volume* v);
-
+*/
int get_bootloader_message(struct bootloader_message *out) {
- Volume* v = NULL;//volume_for_path("/misc");
- if (v == NULL) {
+ //volume_for_path("/misc");
+ if (device_name[0] == 0) {
LOGE("Cannot load volume /misc!\n");
return -1;
}
- if (strcmp(v->fs_type, "mtd") == 0) {
- return get_bootloader_message_mtd(out, v);
- } else if (strcmp(v->fs_type, "emmc") == 0) {
- return get_bootloader_message_block(out, v);
+ if (device_type == 'm') {
+ return get_bootloader_message_mtd_name(out);
+ } else if (device_type == 'e') {
+ return get_bootloader_message_block_name(out);
}
- LOGE("unknown misc partition fs_type \"%s\"\n", v->fs_type);
+ LOGE("unknown misc partition fs_type \"%c\"\n", device_type);
return -1;
}
int set_bootloader_message(const struct bootloader_message *in) {
- Volume* v = NULL;//volume_for_path("/misc");
- if (v == NULL) {
+ //volume_for_path("/misc");
+ if (device_name[0] == 0) {
LOGE("Cannot load volume /misc!\n");
return -1;
}
- if (strcmp(v->fs_type, "mtd") == 0) {
- return set_bootloader_message_mtd(in, v);
- } else if (strcmp(v->fs_type, "emmc") == 0) {
- return set_bootloader_message_block(in, v);
+ if (device_type == 'm') {
+ return set_bootloader_message_mtd_name(in, device_name);
+ } else if (device_type == 'e') {
+ return set_bootloader_message_block_name(in, device_name);
}
- LOGE("unknown misc partition fs_type \"%s\"\n", v->fs_type);
+ LOGE("unknown misc partition type \"%c\"\n", device_type);
return -1;
}
@@ -68,27 +75,27 @@
static const int MISC_PAGES = 3; // number of pages to save
static const int MISC_COMMAND_PAGE = 1; // bootloader command is this page
-
+/*
static int get_bootloader_message_mtd(struct bootloader_message *out,
const Volume* v) {
size_t write_size;
mtd_scan_partitions();
- const MtdPartition *part = mtd_find_partition_by_name(v->device);
+ const MtdPartition *part = mtd_find_partition_by_name(v->blk_device);
if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) {
- LOGE("Can't find %s\n", v->device);
+ LOGE("Can't find %s\n", v->blk_device);
return -1;
}
MtdReadContext *read = mtd_read_partition(part);
if (read == NULL) {
- LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
+ LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno));
return -1;
}
const ssize_t size = write_size * MISC_PAGES;
char data[size];
ssize_t r = mtd_read_data(read, data, size);
- if (r != size) LOGE("Can't read %s\n(%s)\n", v->device, strerror(errno));
+ if (r != size) LOGE("Can't read %s\n(%s)\n", v->blk_device, strerror(errno));
mtd_read_close(read);
if (r != size) return -1;
@@ -99,22 +106,22 @@
const Volume* v) {
size_t write_size;
mtd_scan_partitions();
- const MtdPartition *part = mtd_find_partition_by_name(v->device);
+ const MtdPartition *part = mtd_find_partition_by_name(v->blk_device);
if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) {
- LOGE("Can't find %s\n", v->device);
+ LOGE("Can't find %s\n", v->blk_device);
return -1;
}
MtdReadContext *read = mtd_read_partition(part);
if (read == NULL) {
- LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
+ LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno));
return -1;
}
ssize_t size = write_size * MISC_PAGES;
char data[size];
ssize_t r = mtd_read_data(read, data, size);
- if (r != size) LOGE("Can't read %s\n(%s)\n", v->device, strerror(errno));
+ if (r != size) LOGE("Can't read %s\n(%s)\n", v->blk_device, strerror(errno));
mtd_read_close(read);
if (r != size) return -1;
@@ -122,22 +129,61 @@
MtdWriteContext *write = mtd_write_partition(part);
if (write == NULL) {
- LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
+ LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno));
return -1;
}
if (mtd_write_data(write, data, size) != size) {
- LOGE("Can't write %s\n(%s)\n", v->device, strerror(errno));
+ LOGE("Can't write %s\n(%s)\n", v->blk_device, strerror(errno));
mtd_write_close(write);
return -1;
}
if (mtd_write_close(write)) {
- LOGE("Can't finish %s\n(%s)\n", v->device, strerror(errno));
+ LOGE("Can't finish %s\n(%s)\n", v->blk_device, strerror(errno));
return -1;
}
LOGI("Set boot command \"%s\"\n", in->command[0] != 255 ? in->command : "");
return 0;
}
+*/
+
+void set_device_type(char new_type) {
+ device_type = new_type;
+}
+
+void set_device_name(const char* new_name) {
+ if (strlen(new_name) >= sizeof(device_name)) {
+ LOGE("New device name of '%s' is too large for bootloader.cpp\n", new_name);
+ } else {
+ strcpy(device_name, new_name);
+ }
+}
+
+int get_bootloader_message_mtd_name(struct bootloader_message *out) {
+ size_t write_size;
+ mtd_scan_partitions();
+ const MtdPartition *part = mtd_find_partition_by_name(device_name);
+ if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) {
+ LOGE("Can't find %s\n", device_name);
+ return -1;
+ }
+
+ MtdReadContext *read = mtd_read_partition(part);
+ if (read == NULL) {
+ LOGE("Can't open %s\n(%s)\n", device_name, strerror(errno));
+ return -1;
+ }
+
+ const ssize_t size = write_size * MISC_PAGES;
+ char data[size];
+ ssize_t r = mtd_read_data(read, data, size);
+ if (r != size) LOGE("Can't read %s\n(%s)\n", device_name, strerror(errno));
+ mtd_read_close(read);
+ if (r != size) return -1;
+
+ memcpy(out, &data[write_size * MISC_COMMAND_PAGE], sizeof(*out));
+ return 0;
+}
int set_bootloader_message_mtd_name(const struct bootloader_message *in,
const char* mtd_name) {
@@ -203,23 +249,23 @@
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");
+ wait_for_device(v->blk_device);
+ FILE* f = fopen(v->blk_device, "rb");
if (f == NULL) {
- LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
+ LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno));
return -1;
}
struct bootloader_message temp;
int count = fread(&temp, sizeof(temp), 1, f);
if (count != 1) {
- LOGE("Failed reading %s\n(%s)\n", v->device, strerror(errno));
+ LOGE("Failed reading %s\n(%s)\n", v->blk_device, strerror(errno));
return -1;
}
if (fclose(f) != 0) {
- LOGE("Failed closing %s\n(%s)\n", v->device, strerror(errno));
+ LOGE("Failed closing %s\n(%s)\n", v->blk_device, strerror(errno));
return -1;
}
memcpy(out, &temp, sizeof(temp));
@@ -228,23 +274,45 @@
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");
+ wait_for_device(v->blk_device);
+ FILE* f = fopen(v->blk_device, "wb");
if (f == NULL) {
- LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
+ LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno));
return -1;
}
int count = fwrite(in, sizeof(*in), 1, f);
if (count != 1) {
- LOGE("Failed writing %s\n(%s)\n", v->device, strerror(errno));
+ LOGE("Failed writing %s\n(%s)\n", v->blk_device, strerror(errno));
return -1;
}
if (fclose(f) != 0) {
- LOGE("Failed closing %s\n(%s)\n", v->device, strerror(errno));
+ LOGE("Failed closing %s\n(%s)\n", v->blk_device, strerror(errno));
return -1;
}
return 0;
}
+*/
+
+int get_bootloader_message_block_name(struct bootloader_message *out) {
+ wait_for_device(device_name);
+ FILE* f = fopen(device_name, "rb");
+ if (f == NULL) {
+ LOGE("Can't open %s\n(%s)\n", device_name, strerror(errno));
+ return -1;
+ }
+ struct bootloader_message temp;
+ int count = fread(&temp, sizeof(temp), 1, f);
+ if (count != 1) {
+ LOGE("Failed reading %s\n(%s)\n", device_name, strerror(errno));
+ return -1;
+ }
+ if (fclose(f) != 0) {
+ LOGE("Failed closing %s\n(%s)\n", device_name, strerror(errno));
+ return -1;
+ }
+ memcpy(out, &temp, sizeof(temp));
+ return 0;
+}
int set_bootloader_message_block_name(const struct bootloader_message *in,
const char* block_name) {
diff --git a/bootloader.h b/bootloader.h
index ead1d0b..0a682b2 100644
--- a/bootloader.h
+++ b/bootloader.h
@@ -48,10 +48,17 @@
/* Read and write the bootloader command from the "misc" partition.
* These return zero on success.
*/
+/*
int get_bootloader_message(struct bootloader_message *out);
int set_bootloader_message(const struct bootloader_message *in);
+*/
+void set_device_type(char new_type);
+void set_device_name(const char* new_name);
+
+int get_bootloader_message_mtd_name(struct bootloader_message *out);
int set_bootloader_message_mtd_name(const struct bootloader_message *in, const char* mtd_name);
+int get_bootloader_message_block_name(struct bootloader_message *out);
int set_bootloader_message_block_name(const struct bootloader_message *in, const char* block_name);
void get_args(int *argc, char ***argv);
diff --git a/common.h b/common.h
index 4344298..18b0304 100644
--- a/common.h
+++ b/common.h
@@ -18,6 +18,7 @@
#define RECOVERY_COMMON_H
#include <stdio.h>
+#include <stdarg.h>
#ifdef __cplusplus
extern "C" {
@@ -44,28 +45,13 @@
#define STRINGIFY(x) #x
#define EXPAND(x) STRINGIFY(x)
-typedef struct {
- const char* mount_point; // eg. "/cache". must live in the root directory.
-
- const char* fs_type; // "yaffs2" or "ext4" or "vfat"
-
- const char* device; // MTD partition name if fs_type == "yaffs"
- // block device if fs_type == "ext4" or "vfat"
-
- 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 fstab_rec Volume;
// fopen a file, mounting volumes and making parent dirs as necessary.
FILE* fopen_path(const char *path, const char *mode);
+//void ui_print(const char* format, ...);
+
#ifdef __cplusplus
}
#endif
diff --git a/fonts/12x22.png b/fonts/12x22.png
new file mode 100644
index 0000000..ae826be
--- /dev/null
+++ b/fonts/12x22.png
Binary files differ
diff --git a/fonts/18x32.png b/fonts/18x32.png
new file mode 100644
index 0000000..d95408a
--- /dev/null
+++ b/fonts/18x32.png
Binary files differ
diff --git a/fonts/OFL.txt b/fonts/OFL.txt
new file mode 100644
index 0000000..b14edde
--- /dev/null
+++ b/fonts/OFL.txt
@@ -0,0 +1,93 @@
+Copyright (c) 2011, Raph Levien (firstname.lastname@gmail.com), Copyright (c) 2012, Cyreal (cyreal.org)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/fonts/README b/fonts/README
new file mode 100644
index 0000000..d0748d2
--- /dev/null
+++ b/fonts/README
@@ -0,0 +1,6 @@
+The images in this directory were generated using the font
+Inconsolata, which is released under the OFL license and was obtained
+from:
+
+ https://code.google.com/p/googlefontdirectory/source/browse/ofl/inconsolata/
+
diff --git a/install.cpp b/install.cpp
index 92388e8..0f3298f 100644
--- a/install.cpp
+++ b/install.cpp
@@ -174,86 +174,10 @@
return INSTALL_SUCCESS;
}
-// Reads a file containing one or more public keys as produced by
-// DumpPublicKey: this is an RSAPublicKey struct as it would appear
-// as a C source literal, eg:
-//
-// "{64,0xc926ad21,{1795090719,...,-695002876},{-857949815,...,1175080310}}"
-//
-// (Note that the braces and commas in this example are actual
-// characters the parser expects to find in the file; the ellipses
-// indicate more numbers omitted from this example.)
-//
-// The file may contain multiple keys in this format, separated by
-// commas. The last key must not be followed by a comma.
-//
-// Returns NULL if the file failed to parse, or if it contain zero keys.
-RSAPublicKey*
-load_keys(const char* filename, int* numKeys) {
- RSAPublicKey* out = NULL;
- *numKeys = 0;
-
- FILE* f = fopen(filename, "r");
- if (f == NULL) {
- LOGE("opening %s: %s\n", filename, strerror(errno));
- goto exit;
- }
-
- {
- int i;
- bool done = false;
- while (!done) {
- ++*numKeys;
- out = (RSAPublicKey*)realloc(out, *numKeys * sizeof(RSAPublicKey));
- RSAPublicKey* key = out + (*numKeys - 1);
- if (fscanf(f, " { %i , 0x%x , { %u",
- &(key->len), &(key->n0inv), &(key->n[0])) != 3) {
- goto exit;
- }
- if (key->len != RSANUMWORDS) {
- LOGE("key length (%d) does not match expected size\n", key->len);
- goto exit;
- }
- for (i = 1; i < key->len; ++i) {
- if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit;
- }
- if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit;
- for (i = 1; i < key->len; ++i) {
- if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit;
- }
- fscanf(f, " } } ");
-
- // if the line ends in a comma, this file has more keys.
- switch (fgetc(f)) {
- case ',':
- // more keys to come.
- break;
-
- case EOF:
- done = true;
- break;
-
- default:
- LOGE("unexpected character between keys\n");
- goto exit;
- }
- }
- }
-
- fclose(f);
- return out;
-
-exit:
- if (f) fclose(f);
- free(out);
- *numKeys = 0;
- return NULL;
-}
-
static int
really_install_package(const char *path, int* wipe_cache)
{
- ui->SetBackground(RecoveryUI::INSTALLING);
+ ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
ui->Print("Finding update package...\n");
ui->SetProgressType(RecoveryUI::INDETERMINATE);
LOGI("Update location: %s\n", path);
diff --git a/install.h b/install.h
index 5829c65..043ce13 100644
--- a/install.h
+++ b/install.h
@@ -24,7 +24,7 @@
extern "C" {
#endif
-enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT };
+enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_NONE };
// Install the package specified by root_path. If INSTALL_SUCCESS is
// returned and *wipe_cache is true on exit, caller should wipe the
// cache partition.
diff --git a/minadbd/adb.c b/minadbd/adb.c
index 54adba0..4cd05ed 100644
--- a/minadbd/adb.c
+++ b/minadbd/adb.c
@@ -29,8 +29,6 @@
#include "adb.h"
#include <private/android_filesystem_config.h>
-#include <linux/capability.h>
-#include <linux/prctl.h>
#if ADB_TRACE
ADB_MUTEX_DEFINE( D_lock );
diff --git a/minui/Android.mk b/minui/Android.mk
index f9afd6b..232ebb2 100644
--- a/minui/Android.mk
+++ b/minui/Android.mk
@@ -9,11 +9,21 @@
LOCAL_STATIC_LIBRARY := libpng
LOCAL_MODULE := libminui
-ifeq ($(TARGET_RECOVERY_PIXEL_FORMAT),"RGBX_8888")
+# This used to compare against values in double-quotes (which are just
+# ordinary characters in this context). Strip double-quotes from the
+# value so that either will work.
+
+ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),RGBX_8888)
LOCAL_CFLAGS += -DRECOVERY_RGBX
endif
-ifeq ($(TARGET_RECOVERY_PIXEL_FORMAT),"BGRA_8888")
+ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),BGRA_8888)
LOCAL_CFLAGS += -DRECOVERY_BGRA
endif
+ifneq ($(TARGET_RECOVERY_OVERSCAN_PERCENT),)
+ LOCAL_CFLAGS += -DOVERSCAN_PERCENT=$(TARGET_RECOVERY_OVERSCAN_PERCENT)
+else
+ LOCAL_CFLAGS += -DOVERSCAN_PERCENT=0
+endif
+
include $(BUILD_STATIC_LIBRARY)
diff --git a/minui/graphics.c b/minui/graphics.c
index 296e2e0..4968eac 100644
--- a/minui/graphics.c
+++ b/minui/graphics.c
@@ -44,20 +44,24 @@
#define PIXEL_SIZE 2
#endif
+#define NUM_BUFFERS 2
+
typedef struct {
- GGLSurface texture;
+ GGLSurface* texture;
unsigned cwidth;
unsigned cheight;
- unsigned ascent;
} GRFont;
static GRFont *gr_font = 0;
static GGLContext *gr_context = 0;
static GGLSurface gr_font_texture;
-static GGLSurface gr_framebuffer[2];
+static GGLSurface gr_framebuffer[NUM_BUFFERS];
static GGLSurface gr_mem_surface;
static unsigned gr_active_fb = 0;
static unsigned double_buffering = 0;
+static int overscan_percent = OVERSCAN_PERCENT;
+static int overscan_offset_x = 0;
+static int overscan_offset_y = 0;
static int gr_fb_fd = -1;
static int gr_vt_fd = -1;
@@ -130,6 +134,9 @@
return -1;
}
+ overscan_offset_x = vi.xres * overscan_percent / 100;
+ overscan_offset_y = vi.yres * overscan_percent / 100;
+
fb->version = sizeof(*fb);
fb->width = vi.xres;
fb->height = vi.yres;
@@ -169,7 +176,7 @@
static void set_active_framebuffer(unsigned n)
{
if (n > 1 || !double_buffering) return;
- vi.yres_virtual = vi.yres * PIXEL_SIZE;
+ vi.yres_virtual = vi.yres * NUM_BUFFERS;
vi.yoffset = n * vi.yres;
vi.bits_per_pixel = PIXEL_SIZE * 8;
if (ioctl(gr_fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) {
@@ -216,15 +223,20 @@
*y = gr_font->cheight;
}
-int gr_text(int x, int y, const char *s)
+int gr_text(int x, int y, const char *s, int bold)
{
GGLContext *gl = gr_context;
GRFont *font = gr_font;
unsigned off;
- y -= font->ascent;
+ if (!font->texture) return x;
- gl->bindTexture(gl, &font->texture);
+ bold = bold && (font->texture->height != font->cheight);
+
+ x += overscan_offset_x;
+ y += overscan_offset_y;
+
+ gl->bindTexture(gl, font->texture);
gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
@@ -233,7 +245,8 @@
while((off = *s++)) {
off -= 32;
if (off < 96) {
- gl->texCoord2i(gl, (off * font->cwidth) - x, 0 - y);
+ gl->texCoord2i(gl, (off * font->cwidth) - x,
+ (bold ? font->cheight : 0) - y);
gl->recti(gl, x, y, x + font->cwidth, y + font->cheight);
}
x += font->cwidth;
@@ -242,19 +255,50 @@
return x;
}
-void gr_fill(int x, int y, int w, int h)
+void gr_texticon(int x, int y, gr_surface icon) {
+ if (gr_context == NULL || icon == NULL) {
+ return;
+ }
+ GGLContext* gl = gr_context;
+
+ x += overscan_offset_x;
+ y += overscan_offset_y;
+
+ gl->bindTexture(gl, (GGLSurface*) icon);
+ gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
+ gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
+ gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
+ gl->enable(gl, GGL_TEXTURE_2D);
+
+ int w = gr_get_width(icon);
+ int h = gr_get_height(icon);
+
+ gl->texCoord2i(gl, -x, -y);
+ gl->recti(gl, x, y, x+gr_get_width(icon), y+gr_get_height(icon));
+}
+
+void gr_fill(int x1, int y1, int x2, int y2)
{
+ x1 += overscan_offset_x;
+ y1 += overscan_offset_y;
+
+ x2 += overscan_offset_x;
+ y2 += overscan_offset_y;
+
GGLContext *gl = gr_context;
gl->disable(gl, GGL_TEXTURE_2D);
- gl->recti(gl, x, y, w, h);
+ gl->recti(gl, x1, y1, x2, y2);
}
void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy) {
- if (gr_context == NULL) {
+ if (gr_context == NULL || source == NULL) {
return;
}
GGLContext *gl = gr_context;
+ dx += overscan_offset_x;
+ dy += overscan_offset_y;
+
gl->bindTexture(gl, (GGLSurface*) source);
gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
@@ -280,31 +324,40 @@
static void gr_init_font(void)
{
- GGLSurface *ftex;
- unsigned char *bits, *rle;
- unsigned char *in, data;
-
gr_font = calloc(sizeof(*gr_font), 1);
- ftex = &gr_font->texture;
- bits = malloc(font.width * font.height);
+ int res = res_create_surface("font", (void**)&(gr_font->texture));
+ if (res == 0) {
+ // The font image should be a 96x2 array of character images. The
+ // columns are the printable ASCII characters 0x20 - 0x7f. The
+ // top row is regular text; the bottom row is bold.
+ gr_font->cwidth = gr_font->texture->width / 96;
+ gr_font->cheight = gr_font->texture->height / 2;
+ } else {
+ printf("failed to read font: res=%d\n", res);
- ftex->version = sizeof(*ftex);
- ftex->width = font.width;
- ftex->height = font.height;
- ftex->stride = font.width;
- ftex->data = (void*) bits;
- ftex->format = GGL_PIXEL_FORMAT_A_8;
+ // fall back to the compiled-in font.
+ gr_font->texture = malloc(sizeof(*gr_font->texture));
+ gr_font->texture->width = font.width;
+ gr_font->texture->height = font.height;
+ gr_font->texture->stride = font.width;
- in = font.rundata;
- while((data = *in++)) {
- memset(bits, (data & 0x80) ? 255 : 0, data & 0x7f);
- bits += (data & 0x7f);
+ unsigned char* bits = malloc(font.width * font.height);
+ gr_font->texture->data = (void*) bits;
+
+ unsigned char data;
+ unsigned char* in = font.rundata;
+ while((data = *in++)) {
+ memset(bits, (data & 0x80) ? 255 : 0, data & 0x7f);
+ bits += (data & 0x7f);
+ }
+
+ gr_font->cwidth = font.cwidth;
+ gr_font->cheight = font.cheight;
}
- gr_font->cwidth = font.cwidth;
- gr_font->cheight = font.cheight;
- gr_font->ascent = font.cheight - 2;
+ // interpret the grayscale as alpha
+ gr_font->texture->format = GGL_PIXEL_FORMAT_A_8;
}
int gr_init(void)
@@ -364,12 +417,12 @@
int gr_fb_width(void)
{
- return gr_framebuffer[0].width;
+ return gr_framebuffer[0].width - 2*overscan_offset_x;
}
int gr_fb_height(void)
{
- return gr_framebuffer[0].height;
+ return gr_framebuffer[0].height - 2*overscan_offset_y;
}
gr_pixel *gr_fb_data(void)
diff --git a/minui/minui.h b/minui/minui.h
index 74da4e9..1b8dd05 100644
--- a/minui/minui.h
+++ b/minui/minui.h
@@ -36,8 +36,9 @@
void gr_fb_blank(bool blank);
void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
-void gr_fill(int x, int y, int w, int h);
-int gr_text(int x, int y, const char *s);
+void gr_fill(int x1, int y1, int x2, int y2);
+int gr_text(int x, int y, const char *s, int bold);
+ void gr_texticon(int x, int y, gr_surface icon);
int gr_measure(const char *s);
void gr_font_size(int *x, int *y);
@@ -71,6 +72,7 @@
// Returns 0 if no error, else negative.
int res_create_surface(const char* name, gr_surface* pSurface);
+int res_create_localized_surface(const char* name, gr_surface* pSurface);
void res_free_surface(gr_surface surface);
#ifdef __cplusplus
diff --git a/minui/resources.c b/minui/resources.c
index 5e20621..c0a9cca 100644
--- a/minui/resources.c
+++ b/minui/resources.c
@@ -33,6 +33,8 @@
#include "minui.h"
+extern char* locale;
+
// libpng gives "undefined reference to 'pow'" errors, and I have no
// idea how to convince the build system to link with -lm. We don't
// need this functionality (it's used for gamma adjustment) so provide
@@ -91,22 +93,25 @@
png_set_sig_bytes(png_ptr, sizeof(header));
png_read_info(png_ptr, info_ptr);
- size_t width = info_ptr->width;
- size_t height = info_ptr->height;
- size_t stride = 4 * width;
- size_t pixelSize = stride * height;
+ int color_type, bit_depth;
+ size_t width, height;
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
+ &color_type, NULL, NULL, NULL);
- int color_type = info_ptr->color_type;
- int bit_depth = info_ptr->bit_depth;
- int channels = info_ptr->channels;
+ int channels = png_get_channels(png_ptr, info_ptr);
+
if (!(bit_depth == 8 &&
((channels == 3 && color_type == PNG_COLOR_TYPE_RGB) ||
(channels == 4 && color_type == PNG_COLOR_TYPE_RGBA) ||
- (channels == 1 && color_type == PNG_COLOR_TYPE_PALETTE)))) {
+ (channels == 1 && (color_type == PNG_COLOR_TYPE_PALETTE ||
+ color_type == PNG_COLOR_TYPE_GRAY))))) {
return -7;
goto exit;
}
+ size_t stride = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4) * width;
+ size_t pixelSize = stride * height;
+
surface = malloc(sizeof(GGLSurface) + pixelSize);
if (surface == NULL) {
result = -8;
@@ -118,8 +123,8 @@
surface->height = height;
surface->stride = width; /* Yes, pixels, not bytes */
surface->data = pData;
- surface->format = (channels == 3) ?
- GGL_PIXEL_FORMAT_RGBX_8888 : GGL_PIXEL_FORMAT_RGBA_8888;
+ surface->format = (channels == 3) ? GGL_PIXEL_FORMAT_RGBX_8888 :
+ ((color_type == PNG_COLOR_TYPE_PALETTE ? GGL_PIXEL_FORMAT_RGBA_8888 : GGL_PIXEL_FORMAT_L_8));
int alpha = 0;
if (color_type == PNG_COLOR_TYPE_PALETTE) {
@@ -129,6 +134,9 @@
png_set_tRNS_to_alpha(png_ptr);
alpha = 1;
}
+ if (color_type == PNG_COLOR_TYPE_GRAY) {
+ alpha = 1;
+ }
unsigned int y;
if (channels == 3 || (channels == 1 && !alpha)) {
@@ -173,6 +181,141 @@
return result;
}
+static int matches_locale(const char* loc) {
+ if (locale == NULL) return 0;
+
+ if (strcmp(loc, locale) == 0) return 1;
+
+ // if loc does *not* have an underscore, and it matches the start
+ // of locale, and the next character in locale *is* an underscore,
+ // that's a match. For instance, loc == "en" matches locale ==
+ // "en_US".
+
+ int i;
+ for (i = 0; loc[i] != 0 && loc[i] != '_'; ++i);
+ if (loc[i] == '_') return 0;
+
+ return (strncmp(locale, loc, i) == 0 && locale[i] == '_');
+}
+
+int res_create_localized_surface(const char* name, gr_surface* pSurface) {
+ char resPath[256];
+ GGLSurface* surface = NULL;
+ int result = 0;
+ unsigned char header[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");
+ if (fp == NULL) {
+ result = -1;
+ goto exit;
+ }
+
+ size_t bytesRead = fread(header, 1, sizeof(header), fp);
+ if (bytesRead != sizeof(header)) {
+ result = -2;
+ goto exit;
+ }
+
+ if (png_sig_cmp(header, 0, sizeof(header))) {
+ result = -3;
+ goto exit;
+ }
+
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr) {
+ result = -4;
+ goto exit;
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ result = -5;
+ goto exit;
+ }
+
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ result = -6;
+ goto exit;
+ }
+
+ png_init_io(png_ptr, fp);
+ png_set_sig_bytes(png_ptr, sizeof(header));
+ png_read_info(png_ptr, info_ptr);
+
+ int color_type, bit_depth;
+ size_t width, height;
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
+ &color_type, NULL, NULL, NULL);
+ int channels = png_get_channels(png_ptr, info_ptr);
+
+ if (!(bit_depth == 8 &&
+ (channels == 1 && color_type == PNG_COLOR_TYPE_GRAY))) {
+ return -7;
+ goto exit;
+ }
+
+ unsigned char* row = malloc(width);
+ int y;
+ for (y = 0; y < height; ++y) {
+ png_read_row(png_ptr, row, NULL);
+ int w = (row[1] << 8) | row[0];
+ int h = (row[3] << 8) | row[2];
+ int len = row[4];
+ char* loc = row+5;
+
+ if (y+1+h >= height || matches_locale(loc)) {
+ printf(" %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y);
+
+ surface = malloc(sizeof(GGLSurface));
+ if (surface == NULL) {
+ result = -8;
+ goto exit;
+ }
+ unsigned char* pData = malloc(w*h);
+
+ surface->version = sizeof(GGLSurface);
+ surface->width = w;
+ surface->height = h;
+ surface->stride = w; /* Yes, pixels, not bytes */
+ surface->data = pData;
+ surface->format = GGL_PIXEL_FORMAT_A_8;
+
+ int i;
+ for (i = 0; i < h; ++i, ++y) {
+ png_read_row(png_ptr, row, NULL);
+ memcpy(pData + i*w, row, w);
+ }
+
+ *pSurface = (gr_surface) surface;
+ break;
+ } else {
+ int i;
+ for (i = 0; i < h; ++i, ++y) {
+ png_read_row(png_ptr, row, NULL);
+ }
+ }
+ }
+
+exit:
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+
+ if (fp != NULL) {
+ fclose(fp);
+ }
+ if (result < 0) {
+ if (surface) {
+ free(surface);
+ }
+ }
+ return result;
+}
+
void res_free_surface(gr_surface surface) {
GGLSurface* pSurface = (GGLSurface*) surface;
if (pSurface) {
diff --git a/minzip/Android.mk b/minzip/Android.mk
index 0435c6a..3dd97ff 100644
--- a/minzip/Android.mk
+++ b/minzip/Android.mk
@@ -8,15 +8,11 @@
Inlines.c \
Zip.c
-LOCAL_C_INCLUDES += \
+LOCAL_C_INCLUDES := \
external/zlib \
external/safe-iop/include
-ifeq ($(HAVE_SELINUX),true)
-LOCAL_C_INCLUDES += external/libselinux/include
-LOCAL_STATIC_LIBRARIES += libselinux
-LOCAL_CFLAGS += -DHAVE_SELINUX
-endif
+LOCAL_STATIC_LIBRARIES := libselinux
LOCAL_MODULE := libminzip
diff --git a/minzip/DirUtil.c b/minzip/DirUtil.c
index 0d49b57..8dd5da1 100644
--- a/minzip/DirUtil.c
+++ b/minzip/DirUtil.c
@@ -145,24 +145,19 @@
} else if (ds == DMISSING) {
int err;
-#ifdef HAVE_SELINUX
char *secontext = NULL;
if (sehnd) {
selabel_lookup(sehnd, &secontext, cpath, mode);
setfscreatecon(secontext);
}
-#endif
err = mkdir(cpath, mode);
-#ifdef HAVE_SELINUX
-
if (secontext) {
freecon(secontext);
setfscreatecon(NULL);
}
-#endif
if (err != 0) {
free(cpath);
diff --git a/minzip/DirUtil.h b/minzip/DirUtil.h
index f8be640..a5cfa76 100644
--- a/minzip/DirUtil.h
+++ b/minzip/DirUtil.h
@@ -24,12 +24,8 @@
extern "C" {
#endif
-#ifdef HAVE_SELINUX
#include <selinux/selinux.h>
#include <selinux/label.h>
-#else
-struct selabel_handle;
-#endif
/* Like "mkdir -p", try to guarantee that all directories
* specified in path are present, creating as many directories
diff --git a/minzip/SysUtil.c b/minzip/SysUtil.c
index 49a2522..31c76d6 100644
--- a/minzip/SysUtil.c
+++ b/minzip/SysUtil.c
@@ -95,16 +95,16 @@
if (memPtr == NULL)
return -1;
- actual = read(fd, memPtr, length);
+ pMap->baseAddr = pMap->addr = memPtr;
+ pMap->baseLength = pMap->length = length;
+
+ actual = TEMP_FAILURE_RETRY(read(fd, memPtr, length));
if (actual != length) {
LOGE("only read %d of %d bytes\n", (int) actual, (int) length);
sysReleaseShmem(pMap);
return -1;
}
- pMap->baseAddr = pMap->addr = memPtr;
- pMap->baseLength = pMap->length = length;
-
return 0;
}
diff --git a/minzip/Zip.c b/minzip/Zip.c
index 54d5d55..439e5d9 100644
--- a/minzip/Zip.c
+++ b/minzip/Zip.c
@@ -985,6 +985,7 @@
unsigned int i;
bool seenMatch = false;
int ok = true;
+ int extractCount = 0;
for (i = 0; i < pArchive->numEntries; i++) {
ZipEntry *pEntry = pArchive->pEntries + i;
if (pEntry->fileNameLen < zipDirLen) {
@@ -1115,23 +1116,19 @@
* Open the target for writing.
*/
-#ifdef HAVE_SELINUX
char *secontext = NULL;
if (sehnd) {
selabel_lookup(sehnd, &secontext, targetFile, UNZIP_FILEMODE);
setfscreatecon(secontext);
}
-#endif
int fd = creat(targetFile, UNZIP_FILEMODE);
-#ifdef HAVE_SELINUX
if (secontext) {
freecon(secontext);
setfscreatecon(NULL);
}
-#endif
if (fd < 0) {
LOGE("Can't create target file \"%s\": %s\n",
@@ -1154,13 +1151,16 @@
break;
}
- LOGD("Extracted file \"%s\"\n", targetFile);
+ LOGV("Extracted file \"%s\"\n", targetFile);
+ ++extractCount;
}
}
if (callback != NULL) callback(targetFile, cookie);
}
+ LOGD("Extracted %d file(s)\n", extractCount);
+
free(helper.buf);
free(zpath);
diff --git a/minzip/Zip.h b/minzip/Zip.h
index 4bb9ef6..c942828 100644
--- a/minzip/Zip.h
+++ b/minzip/Zip.h
@@ -18,12 +18,8 @@
extern "C" {
#endif
-#ifdef HAVE_SELINUX
#include <selinux/selinux.h>
#include <selinux/label.h>
-#else
-struct selabel_handle;
-#endif
/*
* One entry in the Zip archive. Treat this as opaque -- use accessors below.
diff --git a/mtdutils/Android.mk b/mtdutils/Android.mk
index 8e1bdca..c1b0e5b 100644
--- a/mtdutils/Android.mk
+++ b/mtdutils/Android.mk
@@ -50,8 +50,8 @@
LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities
LOCAL_MODULE_STEM := bml_over_mtd
LOCAL_C_INCLUDES += bootable/recovery/mtdutils
-LOCAL_STATIC_LIBRARIES := libmtdutils libcutils libc
-LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_STATIC_LIBRARIES := libmtdutils
+LOCAL_SHARED_LIBRARIES := libcutils liblog libc
include $(BUILD_EXECUTABLE)
endif
diff --git a/recovery.cpp b/recovery.cpp
index 4f07fd4..9308eec 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -15,11 +15,13 @@
*/
#include <ctype.h>
+#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <limits.h>
#include <linux/input.h>
+#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -27,7 +29,6 @@
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
-#include <dirent.h>
#include "bootloader.h"
#include "common.h"
@@ -70,15 +71,17 @@
{ "wipe_cache", no_argument, NULL, 'c' },
{ "show_text", no_argument, NULL, 't' },
{ "just_exit", no_argument, NULL, 'x' },
- { "nandroid", no_argument, NULL, 'n' },
+ { "locale", required_argument, NULL, 'l' },
{ NULL, 0, NULL, 0 },
};
+#define LAST_LOG_FILE "/cache/recovery/last_log"
+
static const char *COMMAND_FILE = "/cache/recovery/command";
static const char *INTENT_FILE = "/cache/recovery/intent";
static const char *LOG_FILE = "/cache/recovery/log";
-static const char *LAST_LOG_FILE = "/cache/recovery/last_log";
static const char *LAST_INSTALL_FILE = "/cache/recovery/last_install";
+static const char *LOCALE_FILE = "/cache/recovery/last_locale";
static const char *CACHE_ROOT = "/cache";
static const char *SDCARD_ROOT = "/sdcard";
static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
@@ -86,6 +89,7 @@
static const char *SIDELOAD_TEMP_DIR = "/tmp/sideload";
RecoveryUI* ui = NULL;
+char* locale = NULL;
/*
* The recovery tool communicates with the main system through /cache files.
@@ -212,6 +216,7 @@
if (*argc <= 1) {
FILE *fp = fopen_path(COMMAND_FILE, "r");
if (fp != NULL) {
+ char *token;
char *argv0 = (*argv)[0];
*argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
(*argv)[0] = argv0; // use the same program name
@@ -219,7 +224,12 @@
char buf[MAX_ARG_LENGTH];
for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
if (!fgets(buf, sizeof(buf), fp)) break;
- (*argv)[*argc] = strdup(strtok(buf, "\r\n")); // Strip newline.
+ token = strtok(buf, "\r\n");
+ if (token != NULL) {
+ (*argv)[*argc] = strdup(token); // Strip newline.
+ } else {
+ --*argc;
+ }
}
check_and_fclose(fp, COMMAND_FILE);
@@ -273,6 +283,21 @@
}
}
+// Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max
+// Overwrites any existing last_log.$max.
+static void
+rotate_last_logs(int max) {
+ char oldfn[256];
+ char newfn[256];
+
+ int i;
+ for (i = max-1; i >= 0; --i) {
+ snprintf(oldfn, sizeof(oldfn), (i==0) ? LAST_LOG_FILE : (LAST_LOG_FILE ".%d"), i);
+ snprintf(newfn, sizeof(newfn), LAST_LOG_FILE ".%d", i+1);
+ // ignore errors
+ rename(oldfn, newfn);
+ }
+}
// clear the recovery command and prepare to boot a (hopefully working) system,
// copy our log file to cache as well (for the system to read), and
@@ -291,6 +316,18 @@
}
}
+ // Save the locale to cache, so if recovery is next started up
+ // without a --locale argument (eg, directly from the bootloader)
+ // it will use the last-known locale.
+ if (locale != NULL) {
+ LOGI("Saving locale \"%s\"\n", locale);
+ FILE* fp = fopen_path(LOCALE_FILE, "w");
+ fwrite(locale, 1, strlen(locale), fp);
+ fflush(fp);
+ fsync(fileno(fp));
+ check_and_fclose(fp, LOCALE_FILE);
+ }
+
// Copy logs to cache so the system can find out what happened.
copy_log_file(TEMPORARY_LOG_FILE, LOG_FILE, true);
copy_log_file(TEMPORARY_LOG_FILE, LAST_LOG_FILE, false);
@@ -317,8 +354,7 @@
static int
erase_volume(const char *volume) {
- return !PartitionManager.Wipe_By_Path(volume);
- ui->SetBackground(RecoveryUI::INSTALLING);
+ ui->SetBackground(RecoveryUI::ERASING);
ui->SetProgressType(RecoveryUI::INDETERMINATE);
ui->Print("Formatting %s...\n", volume);
@@ -671,11 +707,22 @@
}
static void
-prompt_and_wait(Device* device) {
+prompt_and_wait(Device* device, int status) {
const char* const* headers = prepend_title(device->GetMenuHeaders());
for (;;) {
finish_recovery(NULL);
+ switch (status) {
+ case INSTALL_SUCCESS:
+ case INSTALL_NONE:
+ ui->SetBackground(RecoveryUI::NO_COMMAND);
+ break;
+
+ case INSTALL_ERROR:
+ case INSTALL_CORRUPT:
+ ui->SetBackground(RecoveryUI::ERROR);
+ break;
+ }
ui->SetProgressType(RecoveryUI::EMPTY);
int chosen_item = get_menu_selection(headers, device->GetMenuItems(), 0, 0, device);
@@ -685,7 +732,6 @@
// statement below.
chosen_item = device->InvokeMenuItem(chosen_item);
- int status;
int wipe_cache;
switch (chosen_item) {
case Device::REBOOT:
@@ -775,6 +821,43 @@
printf("%s=%s\n", key, name);
}
+static void
+load_locale_from_cache() {
+ FILE* fp = fopen_path(LOCALE_FILE, "r");
+ char buffer[80];
+ if (fp != NULL) {
+ fgets(buffer, sizeof(buffer), fp);
+ int j = 0;
+ unsigned int i;
+ for (i = 0; i < sizeof(buffer) && buffer[i]; ++i) {
+ if (!isspace(buffer[i])) {
+ buffer[j++] = buffer[i];
+ }
+ }
+ buffer[j] = 0;
+ locale = strdup(buffer);
+ check_and_fclose(fp, LOCALE_FILE);
+ }
+}
+
+static RecoveryUI* gCurrentUI = NULL;
+
+void
+ui_print(const char* format, ...) {
+ char buffer[256];
+
+ va_list ap;
+ va_start(ap, format);
+ vsnprintf(buffer, sizeof(buffer), format, ap);
+ va_end(ap);
+
+ if (gCurrentUI != NULL) {
+ gCurrentUI->Print("%s", buffer);
+ } else {
+ fputs(buffer, stdout);
+ }
+}
+
int
main(int argc, char **argv) {
// Recovery needs to install world-readable files, so clear umask
@@ -824,12 +907,17 @@
gui_loadResources();
PartitionManager.Mount_By_Path("/cache", true);
+
+ load_volume_table();
+ ensure_path_mounted(LAST_LOG_FILE);
+ rotate_last_logs(5);
+
get_args(&argc, &argv);
int previous_runs = 0;
const char *send_intent = NULL;
const char *update_package = NULL;
- int wipe_data = 0, wipe_cache = 0;
+ int wipe_data = 0, wipe_cache = 0, show_text = 0;
bool just_exit = false;
bool perform_backup = false;
@@ -841,16 +929,29 @@
case 'u': update_package = optarg; break;
case 'w': wipe_data = wipe_cache = 1; break;
case 'c': wipe_cache = 1; break;
- case 't': ui->ShowText(true); break;
+ case 't': show_text = 1; break;
case 'x': just_exit = true; break;
- case 'n': perform_backup = true; LOGI("nandroid\n"); break;
+ case 'l': locale = optarg; break;
case '?':
LOGE("Invalid command argument\n");
continue;
}
}
-#ifdef HAVE_SELINUX
+ if (locale == NULL) {
+ load_locale_from_cache();
+ }
+ printf("locale is [%s]\n", locale);
+
+ Device* device = make_device();
+ ui = device->GetUI();
+ gCurrentUI = ui;
+
+ ui->Init();
+ ui->SetLocale(locale);
+ ui->SetBackground(RecoveryUI::NONE);
+ if (show_text) ui->ShowText(true);
+
struct selinux_opt seopts[] = {
{ SELABEL_OPT_PATH, "/file_contexts" }
};
@@ -861,7 +962,6 @@
fprintf(stderr, "Warning: No file_contexts\n");
ui->Print("Warning: No file_contexts\n");
}
-#endif
//device->StartRecovery();
@@ -936,8 +1036,19 @@
LOGE("Cache wipe (requested by package) failed.");
}
}
- if (status != INSTALL_SUCCESS) ui->Print("Installation aborted.\n");
- */
+
+ if (status != INSTALL_SUCCESS) {
+ ui->Print("Installation aborted.\n");
+
+ // If this is an eng or userdebug build, then automatically
+ // turn the text display on if the script fails so the error
+ // message is visible.
+ char buffer[PROPERTY_VALUE_MAX+1];
+ property_get("ro.build.fingerprint", buffer, "");
+ if (strstr(buffer, ":userdebug/") || strstr(buffer, ":eng/")) {
+ ui->ShowText(true);
+ }
+ }
} else if (wipe_data) {
if (!OpenRecoveryScript::Insert_ORS_Command("wipe data\n"))
status = INSTALL_ERROR;
@@ -952,7 +1063,8 @@
status = INSTALL_ERROR;
if (status != INSTALL_SUCCESS) ui->Print("Cache wipe failed.\n");
} else if (!just_exit) {
- status = INSTALL_ERROR; // No command specified
+ status = INSTALL_NONE; // No command specified
+ ui->SetBackground(RecoveryUI::NO_COMMAND);
}
}
@@ -998,6 +1110,13 @@
PartitionManager.UnMount_By_Path("/system", false);
}
+ if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) {
+ ui->SetBackground(RecoveryUI::ERROR);
+ }
+ if (status != INSTALL_SUCCESS || ui->IsTextVisible()) {
+ prompt_and_wait(device, status);
+ }
+
// Otherwise, get ready to boot the main system...
finish_recovery(send_intent);
ui->Print("Rebooting...\n");
diff --git a/res/images/erasing_text.png b/res/images/erasing_text.png
new file mode 100644
index 0000000..441768a
--- /dev/null
+++ b/res/images/erasing_text.png
Binary files differ
diff --git a/res/images/error_text.png b/res/images/error_text.png
new file mode 100644
index 0000000..4ac6391
--- /dev/null
+++ b/res/images/error_text.png
Binary files differ
diff --git a/res/images/installing_text.png b/res/images/installing_text.png
new file mode 100644
index 0000000..e1ac819
--- /dev/null
+++ b/res/images/installing_text.png
Binary files differ
diff --git a/res/images/no_command_text.png b/res/images/no_command_text.png
new file mode 100644
index 0000000..a688f09
--- /dev/null
+++ b/res/images/no_command_text.png
Binary files differ
diff --git a/roots.cpp b/roots.cpp
index d7ffb31..c52cf59 100644
--- a/roots.cpp
+++ b/roots.cpp
@@ -23,6 +23,7 @@
#include <ctype.h>
extern "C" {
+#include <fs_mgr.h>
#include "mtdutils/mtdutils.h"
#include "mtdutils/mounts.h"
}
@@ -31,115 +32,41 @@
#include "make_ext4fs.h"
#include "partitions.hpp"
-static int num_volumes = 0;
-static Volume* device_volumes = NULL;
+static struct fstab *fstab = NULL;
extern struct selabel_handle *sehandle;
-static int parse_options(char* options, Volume* volume) {
- char* option;
- while ((option = strtok(options, ","))) {
- options = NULL;
+void load_volume_table()
+{
+ int i;
+ int ret;
- if (strncmp(option, "flags=", 6) == 0) continue;
- 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 = (Volume*)malloc(alloc * sizeof(Volume));
-
- // Insert an entry for /tmp, which is the ramdisk and is always mounted.
- device_volumes[0].mount_point = "/tmp";
- 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");
- if (fstab == NULL) {
- LOGE("failed to open /etc/recovery.fstab (%s)\n", strerror(errno));
+ fstab = fs_mgr_read_fstab("/etc/recovery.fstab");
+ if (!fstab) {
+ LOGE("failed to read /etc/recovery.fstab\n");
return;
}
- char buffer[1024];
- int i;
- while (fgets(buffer, sizeof(buffer)-1, fstab)) {
- for (i = 0; buffer[i] && isspace(buffer[i]); ++i);
- if (buffer[i] == '\0' || buffer[i] == '#') continue;
-
- char* original = strdup(buffer);
-
- char* mount_point = strtok(buffer+i, " \t\n");
- char* fs_type = strtok(NULL, " \t\n");
- 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) {
- alloc *= 2;
- device_volumes = (Volume*)realloc(device_volumes, alloc*sizeof(Volume));
- }
- device_volumes[num_volumes].mount_point = strdup(mount_point);
- device_volumes[num_volumes].fs_type = strdup(fs_type);
- device_volumes[num_volumes].device = strdup(device);
- device_volumes[num_volumes].device2 =
- device2 ? strdup(device2) : NULL;
-
- 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);
- }
- free(original);
+ ret = fs_mgr_add_entry(fstab, "/tmp", "ramdisk", "ramdisk", 0);
+ if (ret < 0 ) {
+ LOGE("failed to add /tmp entry to fstab\n");
+ fs_mgr_free_fstab(fstab);
+ fstab = NULL;
+ return;
}
- fclose(fstab);
-
printf("recovery filesystem table\n");
printf("=========================\n");
- for (i = 0; i < num_volumes; ++i) {
- Volume* v = &device_volumes[i];
- printf(" %d %s %s %s %s %lld\n", i, v->mount_point, v->fs_type,
- v->device, v->device2, v->length);
+ for (i = 0; i < fstab->num_entries; ++i) {
+ Volume* v = &fstab->recs[i];
+ printf(" %d %s %s %s %lld\n", i, v->mount_point, v->fs_type,
+ v->blk_device, v->length);
}
printf("\n");
}
Volume* volume_for_path(const char* path) {
- int i;
- for (i = 0; i < num_volumes; ++i) {
- Volume* v = device_volumes+i;
- int len = strlen(v->mount_point);
- if (strncmp(path, v->mount_point, len) == 0 &&
- (path[len] == '\0' || path[len] == '/')) {
- return v;
- }
- }
- return NULL;
+ return fs_mgr_get_entry_for_mount_point(fstab, path);
}
int ensure_path_mounted(const char* path) {
@@ -177,27 +104,19 @@
// mount an MTD partition as a YAFFS2 filesystem.
mtd_scan_partitions();
const MtdPartition* partition;
- partition = mtd_find_partition_by_name(v->device);
+ partition = mtd_find_partition_by_name(v->blk_device);
if (partition == NULL) {
LOGE("failed to find \"%s\" partition to mount at \"%s\"\n",
- v->device, v->mount_point);
+ v->blk_device, v->mount_point);
return -1;
}
return mtd_mount_partition(partition, v->mount_point, v->fs_type, 0);
} else if (strcmp(v->fs_type, "ext4") == 0 ||
strcmp(v->fs_type, "vfat") == 0) {
- result = mount(v->device, v->mount_point, v->fs_type,
+ result = mount(v->blk_device, v->mount_point, v->fs_type,
MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");
if (result == 0) return 0;
- if (v->device2) {
- LOGW("failed to mount %s (%s); trying %s\n",
- v->device, strerror(errno), v->device2);
- result = mount(v->device2, v->mount_point, v->fs_type,
- MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");
- if (result == 0) return 0;
- }
-
LOGE("failed to mount %s (%s)\n", v->mount_point, strerror(errno));
return -1;
}
@@ -265,38 +184,31 @@
if (strcmp(v->fs_type, "yaffs2") == 0 || strcmp(v->fs_type, "mtd") == 0) {
mtd_scan_partitions();
- const MtdPartition* partition = mtd_find_partition_by_name(v->device);
+ const MtdPartition* partition = mtd_find_partition_by_name(v->blk_device);
if (partition == NULL) {
- LOGE("format_volume: no MTD partition \"%s\"\n", v->device);
+ LOGE("format_volume: no MTD partition \"%s\"\n", v->blk_device);
return -1;
}
MtdWriteContext *write = mtd_write_partition(partition);
if (write == NULL) {
- LOGW("format_volume: can't open MTD \"%s\"\n", v->device);
+ LOGW("format_volume: can't open MTD \"%s\"\n", v->blk_device);
return -1;
} else if (mtd_erase_blocks(write, -1) == (off_t) -1) {
- LOGW("format_volume: can't erase MTD \"%s\"\n", v->device);
+ LOGW("format_volume: can't erase MTD \"%s\"\n", v->blk_device);
mtd_write_close(write);
return -1;
} else if (mtd_write_close(write)) {
- LOGW("format_volume: can't close MTD \"%s\"\n", v->device);
+ LOGW("format_volume: can't close MTD \"%s\"\n", v->blk_device);
return -1;
}
return 0;
}
if (strcmp(v->fs_type, "ext4") == 0) {
-#ifdef USE_EXT4
-/*
- int result = make_ext4fs(v->device, v->length, volume, sehandle);
-*/
- int result = 0;
-#else
- int result = 0;
-#endif
+ int result = make_ext4fs(v->blk_device, v->length, volume, sehandle);
if (result != 0) {
- LOGE("format_volume: make_extf4fs failed on %s\n", v->device);
+ LOGE("format_volume: make_extf4fs failed on %s\n", v->blk_device);
return -1;
}
return 0;
diff --git a/screen_ui.cpp b/screen_ui.cpp
index 4441f7a..4a83a5c 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -40,8 +40,8 @@
}
#include "data.hpp"
-#define CHAR_WIDTH 10
-#define CHAR_HEIGHT 18
+static int char_width;
+static int char_height;
// There's only (at most) one of these objects, and global callbacks
// (for pthread_create, and the input event system) need to find it,
@@ -58,6 +58,7 @@
ScreenRecoveryUI::ScreenRecoveryUI() :
currentIcon(NONE),
installingFrame(0),
+ rtl_locale(false),
progressBarType(EMPTY),
progressScopeStart(0),
progressScopeSize(0),
@@ -84,7 +85,13 @@
indeterminate_frames(6),
installing_frames(7),
install_overlay_offset_x(13),
- install_overlay_offset_y(190) {
+ install_overlay_offset_y(190),
+ overlay_offset_x(-1),
+ overlay_offset_y(-1) {
+
+ for (int i = 0; i < 5; i++)
+ backgroundIcon[i] = NULL;
+
pthread_mutex_init(&updateMutex, NULL);
self = this;
}
@@ -95,12 +102,12 @@
// animation. Does nothing if no overlay animation is defined.
// Should only be called with updateMutex locked.
void ScreenRecoveryUI::draw_install_overlay_locked(int frame) {
- if (installationOverlay == NULL) return;
+ if (installationOverlay == NULL || overlay_offset_x < 0) return;
gr_surface surface = installationOverlay[frame];
int iconWidth = gr_get_width(surface);
int iconHeight = gr_get_height(surface);
gr_blit(surface, 0, 0, iconWidth, iconHeight,
- install_overlay_offset_x, install_overlay_offset_y);
+ overlay_offset_x, overlay_offset_y);
}
// Clear the screen and draw the currently selected background icon (if any).
@@ -113,14 +120,26 @@
if (icon) {
gr_surface surface = backgroundIcon[icon];
+ gr_surface text_surface = backgroundText[icon];
+
int iconWidth = gr_get_width(surface);
int iconHeight = gr_get_height(surface);
+ int textWidth = gr_get_width(text_surface);
+ int textHeight = gr_get_height(text_surface);
+
int iconX = (gr_fb_width() - iconWidth) / 2;
- int iconY = (gr_fb_height() - iconHeight) / 2;
+ int iconY = (gr_fb_height() - (iconHeight+textHeight+40)) / 2;
+
+ int textX = (gr_fb_width() - textWidth) / 2;
+ int textY = ((gr_fb_height() - (iconHeight+textHeight+40)) / 2) + iconHeight + 40;
+
gr_blit(surface, 0, 0, iconWidth, iconHeight, iconX, iconY);
- if (icon == INSTALLING) {
+ if (icon == INSTALLING_UPDATE || icon == ERASING) {
draw_install_overlay_locked(installingFrame);
}
+
+ gr_color(255, 255, 255, 255);
+ gr_texticon(textX, textY, text_surface);
}
}
@@ -130,12 +149,12 @@
{
if (currentIcon == ERROR) return;
- if (currentIcon == INSTALLING) {
+ if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) {
draw_install_overlay_locked(installingFrame);
}
if (progressBarType != EMPTY) {
- int iconHeight = gr_get_height(backgroundIcon[INSTALLING]);
+ int iconHeight = gr_get_height(backgroundIcon[INSTALLING_UPDATE]);
int width = gr_get_width(progressBarEmpty);
int height = gr_get_height(progressBarEmpty);
@@ -150,27 +169,42 @@
float p = progressScopeStart + progress * progressScopeSize;
int pos = (int) (p * width);
- if (pos > 0) {
- gr_blit(progressBarFill, 0, 0, pos, height, dx, dy);
- }
- if (pos < width-1) {
- gr_blit(progressBarEmpty, pos, 0, width-pos, height, dx+pos, dy);
+ if (rtl_locale) {
+ // Fill the progress bar from right to left.
+ if (pos > 0) {
+ gr_blit(progressBarFill, width-pos, 0, pos, height, dx+width-pos, dy);
+ }
+ if (pos < width-1) {
+ gr_blit(progressBarEmpty, 0, 0, width-pos, height, dx, dy);
+ }
+ } else {
+ // Fill the progress bar from left to right.
+ if (pos > 0) {
+ gr_blit(progressBarFill, 0, 0, pos, height, dx, dy);
+ }
+ if (pos < width-1) {
+ gr_blit(progressBarEmpty, pos, 0, width-pos, height, dx+pos, dy);
+ }
}
}
if (progressBarType == INDETERMINATE) {
static int frame = 0;
gr_blit(progressBarIndeterminate[frame], 0, 0, width, height, dx, dy);
- frame = (frame + 1) % indeterminate_frames;
+ // in RTL locales, we run the animation backwards, which
+ // makes the spinner spin the other way.
+ if (rtl_locale) {
+ frame = (frame + indeterminate_frames - 1) % indeterminate_frames;
+ } else {
+ frame = (frame + 1) % indeterminate_frames;
+ }
}
}
}
-void ScreenRecoveryUI::draw_text_line(int row, const char* t) {
- if (t[0] != '\0') {
- twgr_text(0, (row+1)*CHAR_HEIGHT-1, t);
- }
-}
+#define C_HEADER 247,0,6
+#define C_MENU 0,106,157
+#define C_LOG 249,194,0
// Redraw everything on the screen. Does not flip pages.
// Should only be called with updateMutex locked.
@@ -183,30 +217,46 @@
gr_color(0, 0, 0, 160);
gr_fill(0, 0, gr_fb_width(), gr_fb_height());
+ int y = 0;
int i = 0;
if (show_menu) {
- gr_color(64, 96, 255, 255);
- gr_fill(0, (menu_top+menu_sel) * CHAR_HEIGHT,
- gr_fb_width(), (menu_top+menu_sel+1)*CHAR_HEIGHT+1);
+ gr_color(C_HEADER, 255);
for (; i < menu_top + menu_items; ++i) {
+ if (i == menu_top) gr_color(C_MENU, 255);
+
if (i == menu_top + menu_sel) {
+ // draw the highlight bar
+ gr_fill(0, y-2, gr_fb_width(), y+char_height+2);
+ // white text of selected item
gr_color(255, 255, 255, 255);
- draw_text_line(i, menu[i]);
- gr_color(64, 96, 255, 255);
+ if (menu[i][0]) gr_text(4, y, menu[i], 1);
+ gr_color(C_MENU, 255);
} else {
- draw_text_line(i, menu[i]);
+ if (menu[i][0]) gr_text(4, y, menu[i], i < menu_top);
}
+ y += char_height+4;
}
- gr_fill(0, i*CHAR_HEIGHT+CHAR_HEIGHT/2-1,
- gr_fb_width(), i*CHAR_HEIGHT+CHAR_HEIGHT/2+1);
+ gr_color(C_MENU, 255);
+ y += 4;
+ gr_fill(0, y, gr_fb_width(), y+2);
+ y += 4;
++i;
}
- gr_color(255, 255, 0, 255);
+ gr_color(C_LOG, 255);
- for (; i < text_rows; ++i) {
- draw_text_line(i, text[(i+text_top) % text_rows]);
+ // display from the bottom up, until we hit the top of the
+ // screen, the bottom of the menu, or we've displayed the
+ // entire text buffer.
+ int ty;
+ int row = (text_top+text_rows-1) % text_rows;
+ for (int ty = gr_fb_height() - char_height, count = 0;
+ ty > y+2 && count < text_rows;
+ ty -= char_height, ++count) {
+ gr_text(4, ty, text[row], 0);
+ --row;
+ if (row < 0) row = text_rows-1;
}
}
}
@@ -248,7 +298,8 @@
// update the installation animation, if active
// skip this if we have a text overlay (too expensive to update)
- if (currentIcon == INSTALLING && installing_frames > 0 && !show_text) {
+ if ((currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) &&
+ installing_frames > 0 && !show_text) {
installingFrame = (installingFrame + 1) % installing_frames;
redraw = 1;
}
@@ -289,23 +340,40 @@
}
}
+void ScreenRecoveryUI::LoadLocalizedBitmap(const char* filename, gr_surface* surface) {
+ int result = res_create_localized_surface(filename, surface);
+ if (result < 0) {
+ LOGE("missing bitmap %s\n(Code %d)\n", filename, result);
+ }
+}
+
void ScreenRecoveryUI::Init()
{
gr_init();
+ gr_font_size(&char_width, &char_height);
+
text_col = text_row = 0;
- text_rows = gr_fb_height() / CHAR_HEIGHT;
+ text_rows = gr_fb_height() / char_height;
if (text_rows > kMaxRows) text_rows = kMaxRows;
text_top = 1;
- text_cols = gr_fb_width() / CHAR_WIDTH;
+ text_cols = gr_fb_width() / char_width;
if (text_cols > kMaxCols - 1) text_cols = kMaxCols - 1;
- LoadBitmap("icon_installing", &backgroundIcon[INSTALLING]);
+ LoadBitmap("icon_installing", &backgroundIcon[INSTALLING_UPDATE]);
+ backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE];
LoadBitmap("icon_error", &backgroundIcon[ERROR]);
+ backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR];
+
LoadBitmap("progress_empty", &progressBarEmpty);
LoadBitmap("progress_fill", &progressBarFill);
+ LoadLocalizedBitmap("installing_text", &backgroundText[INSTALLING_UPDATE]);
+ LoadLocalizedBitmap("erasing_text", &backgroundText[ERASING]);
+ LoadLocalizedBitmap("no_command_text", &backgroundText[NO_COMMAND]);
+ LoadLocalizedBitmap("error_text", &backgroundText[ERROR]);
+
int i;
progressBarIndeterminate = (gr_surface*)malloc(indeterminate_frames *
@@ -327,14 +395,6 @@
sprintf(filename, "icon_installing_overlay%02d", i+1);
LoadBitmap(filename, installationOverlay+i);
}
-
- // Adjust the offset to account for the positioning of the
- // base image on the screen.
- if (backgroundIcon[INSTALLING] != NULL) {
- gr_surface bg = backgroundIcon[INSTALLING];
- install_overlay_offset_x += (gr_fb_width() - gr_get_width(bg)) / 2;
- install_overlay_offset_y += (gr_fb_height() - gr_get_height(bg)) / 2;
- }
} else {
installationOverlay = NULL;
}
@@ -344,11 +404,46 @@
RecoveryUI::Init();
}
+void ScreenRecoveryUI::SetLocale(const char* locale) {
+ if (locale) {
+ char* lang = strdup(locale);
+ for (char* p = lang; *p; ++p) {
+ if (*p == '_') {
+ *p = '\0';
+ break;
+ }
+ }
+
+ // A bit cheesy: keep an explicit list of supported languages
+ // that are RTL.
+ if (strcmp(lang, "ar") == 0 || // Arabic
+ strcmp(lang, "fa") == 0 || // Persian (Farsi)
+ strcmp(lang, "he") == 0 || // Hebrew (new language code)
+ strcmp(lang, "iw") == 0 || // Hebrew (old language code)
+ strcmp(lang, "ur") == 0) { // Urdu
+ rtl_locale = true;
+ }
+ free(lang);
+ }
+}
+
void ScreenRecoveryUI::SetBackground(Icon icon)
{
pthread_mutex_lock(&updateMutex);
+
+ // Adjust the offset to account for the positioning of the
+ // base image on the screen.
+ if (backgroundIcon[icon] != NULL) {
+ gr_surface bg = backgroundIcon[icon];
+ gr_surface text = backgroundText[icon];
+ overlay_offset_x = install_overlay_offset_x + (gr_fb_width() - gr_get_width(bg)) / 2;
+ overlay_offset_y = install_overlay_offset_y +
+ (gr_fb_height() - (gr_get_height(bg) + gr_get_height(text) + 40)) / 2;
+ }
+
currentIcon = icon;
update_screen_locked();
+
pthread_mutex_unlock(&updateMutex);
}
diff --git a/screen_ui.h b/screen_ui.h
index 34929ee..fe0de46 100644
--- a/screen_ui.h
+++ b/screen_ui.h
@@ -29,6 +29,7 @@
ScreenRecoveryUI();
void Init();
+ void SetLocale(const char* locale);
// overall recovery state ("background image")
void SetBackground(Icon icon);
@@ -55,9 +56,11 @@
private:
Icon currentIcon;
int installingFrame;
+ bool rtl_locale;
pthread_mutex_t updateMutex;
- gr_surface backgroundIcon[3];
+ gr_surface backgroundIcon[5];
+ gr_surface backgroundText[5];
gr_surface *installationOverlay;
gr_surface *progressBarIndeterminate;
gr_surface progressBarEmpty;
@@ -73,7 +76,7 @@
bool pagesIdentical;
static const int kMaxCols = 96;
- static const int kMaxRows = 32;
+ static const int kMaxRows = 96;
// Log text overlay, displayed when a magic key is pressed
char text[kMaxRows][kMaxCols];
@@ -92,11 +95,11 @@
int indeterminate_frames;
int installing_frames;
int install_overlay_offset_x, install_overlay_offset_y;
+ int overlay_offset_x, overlay_offset_y;
void draw_install_overlay_locked(int frame);
void draw_background_locked(Icon icon);
void draw_progress_locked();
- void draw_text_line(int row, const char* t);
void draw_screen_locked();
void update_screen_locked();
void update_progress_locked();
@@ -104,7 +107,7 @@
void progress_loop();
void LoadBitmap(const char* filename, gr_surface* surface);
-
+ void LoadLocalizedBitmap(const char* filename, gr_surface* surface);
};
#endif // RECOVERY_UI_H
diff --git a/testdata/otasigned_f4.zip b/testdata/otasigned_f4.zip
new file mode 100644
index 0000000..dd1e4dd
--- /dev/null
+++ b/testdata/otasigned_f4.zip
Binary files differ
diff --git a/testdata/test_f4.pk8 b/testdata/test_f4.pk8
new file mode 100644
index 0000000..3052613
--- /dev/null
+++ b/testdata/test_f4.pk8
Binary files differ
diff --git a/testdata/test_f4.x509.pem b/testdata/test_f4.x509.pem
new file mode 100644
index 0000000..814abcf
--- /dev/null
+++ b/testdata/test_f4.x509.pem
@@ -0,0 +1,25 @@
+-----BEGIN CERTIFICATE-----
+MIIENjCCAx6gAwIBAgIJAKhkCO1dDYMaMA0GCSqGSIb3DQEBBQUAMG8xCzAJBgNV
+BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBW
+aWV3MQ8wDQYDVQQKEwZHb29nbGUxEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMT
+B1Rlc3QxMjMwHhcNMTIwNzI1MTg1NzAzWhcNMzkxMjExMTg1NzAzWjBvMQswCQYD
+VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g
+VmlldzEPMA0GA1UEChMGR29vZ2xlMRAwDgYDVQQLEwdBbmRyb2lkMRAwDgYDVQQD
+EwdUZXN0MTIzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu8WwMN9x
+4Mz7YgkG2qy9g8/kl5ZoYrUM0ApHhaITAcL7RXLZaNipCf0w/YjYTQgj+75MK30x
+TsnPeWNOEwA62gkHrZyyWfxBRO6kBYuIuI4roGDBJOmKQ1OEaDeIRKu7q5V8v3Cs
+0wQDAQWTbhpxBZr9UYFgJUg8XWBfPrGJLVwsoiy4xrMhoTlNZKHfwOMMqVtSHkZX
+qydYrcIzyjh+TO0e/xSNQ8MMRRbtqWgCHN6Rzhog3IHZu0RaPoukariopjXM/s0V
+gTm3rHDHCOpna2pNblyiFlvbkoCs769mtNmx/yrDShO30jg/xaG8RypKDvTChzOT
+oWW/XQ5VEXjbHwIDAQABo4HUMIHRMB0GA1UdDgQWBBRlT2dEZJY1tmUM8mZ0xnhS
+GdD9TTCBoQYDVR0jBIGZMIGWgBRlT2dEZJY1tmUM8mZ0xnhSGdD9TaFzpHEwbzEL
+MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1vdW50
+YWluIFZpZXcxDzANBgNVBAoTBkdvb2dsZTEQMA4GA1UECxMHQW5kcm9pZDEQMA4G
+A1UEAxMHVGVzdDEyM4IJAKhkCO1dDYMaMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcN
+AQEFBQADggEBAHqnXHtE+h3hvGmHh24GT51vGAYLc68WUUtCVlMIU85zQ757wlxZ
+BmRypZ1i9hSqnXj5n+mETV5rFX3g2gvdAPVHkRycuDa2aUdZSE8cW4Z6qYFx6SaD
+e+3SyXokpUquW64RuHJrf/yd/FnGjneBe3Qpm2reuzGWNH90qZGdbsfNaCm5kx2L
+X+ZNHM3CcGMLaphY5++sM0JxSEcju5EK33ZYgLf4YdlbyMp8LDFVNd7ff0SFi9fF
+0ZlAsJWoS3QmVCj2744BFdsCu7UHpnYpG6X3MT4SHAawdOaT5zSuaCl2xx6H0O7t
+w/Fvbl/KVD1ZmLHgBKjDMNSh0OB9mSsDWpw=
+-----END CERTIFICATE-----
diff --git a/twrp.cpp b/twrp.cpp
index 32b411c..d4e861d 100644
--- a/twrp.cpp
+++ b/twrp.cpp
@@ -120,6 +120,18 @@
bool Cache_Wipe = false, Factory_Reset = false, Perform_Backup = false;
{
+ TWPartition* misc = PartitionManager.Find_Partition_By_Path("/misc");
+ if (misc != NULL) {
+ if (misc->Current_File_System == "emmc") {
+ set_device_type('e');
+ set_device_name(misc->Actual_Block_Device.c_str());
+ } else if (misc->Current_File_System == "mtd") {
+ set_device_type('m');
+ set_device_name(misc->MTD_Name.c_str());
+ } else {
+ LOGERR("Unknown file system for /misc\n");
+ }
+ }
get_args(&argc, &argv);
int index, index2, len;
diff --git a/ui.cpp b/ui.cpp
index 27ecc28..042da2e 100644
--- a/ui.cpp
+++ b/ui.cpp
@@ -47,7 +47,8 @@
RecoveryUI::RecoveryUI() :
key_queue_len(0),
- key_last_down(-1) {
+ key_last_down(-1),
+ key_down_time(0) {
pthread_mutex_init(&key_queue_mutex, NULL);
pthread_cond_init(&key_queue_cond, NULL);
self = this;
@@ -111,19 +112,29 @@
// updown == 1 for key down events; 0 for key up events
void RecoveryUI::process_key(int key_code, int updown) {
bool register_key = false;
+ bool long_press = false;
+
+ const long long_threshold = CLOCKS_PER_SEC * 750 / 1000;
pthread_mutex_lock(&key_queue_mutex);
key_pressed[key_code] = updown;
if (updown) {
key_last_down = key_code;
+ key_down_time = clock();
} else {
- if (key_last_down == key_code)
+ if (key_last_down == key_code) {
+ long duration = clock() - key_down_time;
+ if (duration > long_threshold) {
+ long_press = true;
+ }
register_key = true;
+ }
key_last_down = -1;
}
pthread_mutex_unlock(&key_queue_mutex);
if (register_key) {
+ NextCheckKeyIsLong(long_press);
switch (CheckKey(key_code)) {
case RecoveryUI::IGNORE:
break;
@@ -139,18 +150,23 @@
break;
case RecoveryUI::ENQUEUE:
- pthread_mutex_lock(&key_queue_mutex);
- const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]);
- if (key_queue_len < queue_max) {
- key_queue[key_queue_len++] = key_code;
- pthread_cond_signal(&key_queue_cond);
- }
- pthread_mutex_unlock(&key_queue_mutex);
+ EnqueueKey(key_code);
break;
}
}
}
+void RecoveryUI::EnqueueKey(int key_code) {
+ pthread_mutex_lock(&key_queue_mutex);
+ const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]);
+ if (key_queue_len < queue_max) {
+ key_queue[key_queue_len++] = key_code;
+ pthread_cond_signal(&key_queue_cond);
+ }
+ pthread_mutex_unlock(&key_queue_mutex);
+}
+
+
// Reads input events, handles special hot keys, and adds to the key queue.
void* RecoveryUI::input_thread(void *cookie)
{
@@ -227,3 +243,6 @@
RecoveryUI::KeyAction RecoveryUI::CheckKey(int key) {
return RecoveryUI::ENQUEUE;
}
+
+void RecoveryUI::NextCheckKeyIsLong(bool is_long_press) {
+}
diff --git a/ui.h b/ui.h
index 0d3b7bb..aca7b7b 100644
--- a/ui.h
+++ b/ui.h
@@ -19,6 +19,7 @@
#include <linux/input.h>
#include <pthread.h>
+#include <time.h>
// Abstract class for controlling the user interface during recovery.
class RecoveryUI {
@@ -30,8 +31,11 @@
// Initialize the object; called before anything else.
virtual void Init();
+ // After calling Init(), you can tell the UI what locale it is operating in.
+ virtual void SetLocale(const char* locale) { }
+
// Set the overall recovery state ("background image").
- enum Icon { NONE, INSTALLING, ERROR };
+ enum Icon { NONE, INSTALLING_UPDATE, ERASING, NO_COMMAND, ERROR };
virtual void SetBackground(Icon icon) = 0;
// --- progress indicator ---
@@ -76,6 +80,8 @@
enum KeyAction { ENQUEUE, TOGGLE, REBOOT, IGNORE };
virtual KeyAction CheckKey(int key);
+ virtual void NextCheckKeyIsLong(bool is_long_press);
+
// --- menu display ---
// Display some header text followed by a menu of items, which appears
@@ -92,6 +98,9 @@
// statements will be displayed.
virtual void EndMenu() = 0;
+protected:
+ void EnqueueKey(int key_code);
+
private:
// Key event input queue
pthread_mutex_t key_queue_mutex;
@@ -99,6 +108,7 @@
int key_queue[256], key_queue_len;
char key_pressed[KEY_MAX + 1]; // under key_queue_mutex
int key_last_down; // under key_queue_mutex
+ clock_t key_down_time; // under key_queue_mutex
int rel_sum;
pthread_t input_t;
diff --git a/updater/Android.mk b/updater/Android.mk
index c087686..3b883e4 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -32,18 +32,13 @@
endif
endif
-ifeq ($(HAVE_SELINUX), true)
-LOCAL_C_INCLUDES += external/libselinux/include
-LOCAL_STATIC_LIBRARIES += libselinux
-LOCAL_CFLAGS += -DHAVE_SELINUX
-endif # HAVE_SELINUX
-
LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UPDATER_LIBS) $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS)
LOCAL_STATIC_LIBRARIES += libapplypatch libedify libmtdutils libminzip libz
LOCAL_STATIC_LIBRARIES += libflashutils libmmcutils libbmlutils
LOCAL_STATIC_LIBRARIES += libmincrypt libbz
LOCAL_STATIC_LIBRARIES += libminelf
-LOCAL_STATIC_LIBRARIES += libcutils libstdc++ libc
+LOCAL_STATIC_LIBRARIES += libcutils liblog libstdc++ libc
+LOCAL_STATIC_LIBRARIES += libselinux
LOCAL_C_INCLUDES += $(LOCAL_PATH)/..
# Each library in TARGET_RECOVERY_UPDATER_LIBS should have a function
diff --git a/updater/install.c b/updater/install.c
index c5aa77c..305703c 100644
--- a/updater/install.c
+++ b/updater/install.c
@@ -78,23 +78,19 @@
goto done;
}
-#ifdef HAVE_SELINUX
char *secontext = NULL;
if (sehandle) {
selabel_lookup(sehandle, &secontext, mount_point, 0755);
setfscreatecon(secontext);
}
-#endif
mkdir(mount_point, 0755);
-#ifdef HAVE_SELINUX
if (secontext) {
freecon(secontext);
setfscreatecon(NULL);
}
-#endif
if (strcmp(partition_type, "MTD") == 0) {
mtd_scan_partitions();
@@ -456,6 +452,26 @@
}
}
+// Create all parent directories of name, if necessary.
+static int make_parents(char* name) {
+ char* p;
+ for (p = name + (strlen(name)-1); p > name; --p) {
+ if (*p != '/') continue;
+ *p = '\0';
+ if (make_parents(name) < 0) return -1;
+ int result = mkdir(name, 0700);
+ if (result == 0) fprintf(stderr, "symlink(): created [%s]\n", name);
+ *p = '/';
+ if (result == 0 || errno == EEXIST) {
+ // successfully created or already existed; we're done
+ return 0;
+ } else {
+ fprintf(stderr, "failed to mkdir %s: %s\n", name, strerror(errno));
+ return -1;
+ }
+ }
+ return 0;
+}
// symlink target src1 src2 ...
// unlinks any previously existing src1, src2, etc before creating symlinks.
@@ -483,6 +499,11 @@
++bad;
}
}
+ if (make_parents(srcs[i])) {
+ fprintf(stderr, "%s: failed to symlink %s to %s: making parents failed\n",
+ name, srcs[i], target);
+ ++bad;
+ }
if (symlink(target, srcs[i]) < 0) {
fprintf(stderr, "%s: failed to symlink %s to %s: %s\n",
name, srcs[i], target, strerror(errno));
@@ -504,7 +525,8 @@
int min_args = 4 + (recursive ? 1 : 0);
if (argc < min_args) {
- return ErrorAbort(state, "%s() expects %d+ args, got %d", name, argc);
+ return ErrorAbort(state, "%s() expects %d+ args, got %d",
+ name, min_args, argc);
}
char** args = ReadVarArgs(state, argc, argv);
@@ -626,7 +648,7 @@
buffer = malloc(st.st_size+1);
if (buffer == NULL) {
- ErrorAbort(state, "%s: failed to alloc %d bytes", name, st.st_size+1);
+ ErrorAbort(state, "%s: failed to alloc %lld bytes", name, st.st_size+1);
goto done;
}
@@ -638,7 +660,7 @@
}
if (fread(buffer, 1, st.st_size, f) != st.st_size) {
- ErrorAbort(state, "%s: failed to read %d bytes from %s",
+ ErrorAbort(state, "%s: failed to read %lld bytes from %s",
name, st.st_size+1, filename);
fclose(f);
goto done;
@@ -823,7 +845,7 @@
int result = applypatch(source_filename, target_filename,
target_sha1, target_size,
- patchcount, patch_sha_str, patches);
+ patchcount, patch_sha_str, patches, NULL);
for (i = 0; i < patchcount; ++i) {
FreeValue(patches[i]);
diff --git a/updater/updater.c b/updater/updater.c
index 5f15808..58ac27f 100644
--- a/updater/updater.c
+++ b/updater/updater.c
@@ -105,7 +105,6 @@
return 6;
}
-#ifdef HAVE_SELINUX
struct selinux_opt seopts[] = {
{ SELABEL_OPT_PATH, "/file_contexts" }
};
@@ -116,7 +115,6 @@
fprintf(stderr, "Warning: No file_contexts\n");
fprintf(cmd_pipe, "ui_print Warning: No file_contexts\n");
}
-#endif
// Evaluate the parsed script.
diff --git a/updater/updater.h b/updater/updater.h
index a00872c..d2e9011 100644
--- a/updater/updater.h
+++ b/updater/updater.h
@@ -20,12 +20,8 @@
#include <stdio.h>
#include "minzip/Zip.h"
-#ifdef HAVE_SELINUX
#include <selinux/selinux.h>
#include <selinux/label.h>
-#else
-struct selabel_handle;
-#endif
typedef struct {
FILE* cmd_pipe;
diff --git a/verifier.cpp b/verifier.cpp
index 82739f3..a93e8d1 100644
--- a/verifier.cpp
+++ b/verifier.cpp
@@ -29,82 +29,6 @@
#define PUBLIC_KEYS_FILE "/res/keys"
-// Reads a file containing one or more public keys as produced by
-// DumpPublicKey: this is an RSAPublicKey struct as it would appear
-// as a C source literal, eg:
-//
-// "{64,0xc926ad21,{1795090719,...,-695002876},{-857949815,...,1175080310}}"
-//
-// (Note that the braces and commas in this example are actual
-// characters the parser expects to find in the file; the ellipses
-// indicate more numbers omitted from this example.)
-//
-// The file may contain multiple keys in this format, separated by
-// commas. The last key must not be followed by a comma.
-//
-// Returns NULL if the file failed to parse, or if it contain zero keys.
-static RSAPublicKey*
-load_keys(const char* filename, int* numKeys) {
- RSAPublicKey* out = NULL;
- *numKeys = 0;
-
- FILE* f = fopen(filename, "r");
- if (f == NULL) {
- printf("opening %s: %s\n", filename, strerror(errno));
- goto exit;
- }
-
- {
- int i;
- bool done = false;
- while (!done) {
- ++*numKeys;
- out = (RSAPublicKey*)realloc(out, *numKeys * sizeof(RSAPublicKey));
- RSAPublicKey* key = out + (*numKeys - 1);
- if (fscanf(f, " { %i , 0x%x , { %u",
- &(key->len), &(key->n0inv), &(key->n[0])) != 3) {
- goto exit;
- }
- if (key->len != RSANUMWORDS) {
- printf("key length (%d) does not match expected size\n", key->len);
- goto exit;
- }
- for (i = 1; i < key->len; ++i) {
- if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit;
- }
- if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit;
- for (i = 1; i < key->len; ++i) {
- if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit;
- }
- fscanf(f, " } } ");
-
- // if the line ends in a comma, this file has more keys.
- switch (fgetc(f)) {
- case ',':
- // more keys to come.
- break;
-
- case EOF:
- done = true;
- break;
-
- default:
- printf("unexpected character between keys\n");
- goto exit;
- }
- }
- }
-
- fclose(f);
- return out;
-
-exit:
- if (f) fclose(f);
- free(out);
- *numKeys = 0;
- return NULL;
-}
-
// Look for an RSA signature embedded in the .ZIP file comment given
// the path to the zip. Verify it matches one of the given public
// keys.
@@ -120,6 +44,7 @@
LOGE("Failed to load keys\n");
return VERIFY_FAILURE;
}
+ /*
LOGI("%d key(s) loaded from %s\n\n RSA Key:\n\n", numKeys, PUBLIC_KEYS_FILE);
int rsa_size = sizeof(RSAPublicKey);
unsigned char* ptr = (unsigned char*) loadedKeys;
@@ -129,7 +54,7 @@
printf("%02x ", valuedees);
ptr++;
}
- printf("\n\n");
+ printf("\n\n");*/
FILE* f = fopen(path, "rb");
if (f == NULL) {
@@ -274,6 +199,8 @@
LOGI("whole-file signature verified against key %d\n", i);
free(eocd);
return VERIFY_SUCCESS;
+ } else {
+ LOGI("failed to verify against key %d\n", i);
}
LOGI("i: %i, eocd_size: %i, RSANUMBYTES: %i, returned %i\n", i, eocd_size, RSANUMBYTES, dees);
}
@@ -281,3 +208,108 @@
LOGE("failed to verify whole-file signature\n");
return VERIFY_FAILURE;
}
+
+// Reads a file containing one or more public keys as produced by
+// DumpPublicKey: this is an RSAPublicKey struct as it would appear
+// as a C source literal, eg:
+//
+// "{64,0xc926ad21,{1795090719,...,-695002876},{-857949815,...,1175080310}}"
+//
+// For key versions newer than the original 2048-bit e=3 keys
+// supported by Android, the string is preceded by a version
+// identifier, eg:
+//
+// "v2 {64,0xc926ad21,{1795090719,...,-695002876},{-857949815,...,1175080310}}"
+//
+// (Note that the braces and commas in this example are actual
+// characters the parser expects to find in the file; the ellipses
+// indicate more numbers omitted from this example.)
+//
+// The file may contain multiple keys in this format, separated by
+// commas. The last key must not be followed by a comma.
+//
+// Returns NULL if the file failed to parse, or if it contain zero keys.
+RSAPublicKey*
+load_keys(const char* filename, int* numKeys) {
+ RSAPublicKey* out = NULL;
+ *numKeys = 0;
+
+ FILE* f = fopen(filename, "r");
+ if (f == NULL) {
+ LOGE("opening %s: %s\n", filename, strerror(errno));
+ goto exit;
+ }
+
+ {
+ int i;
+ bool done = false;
+ while (!done) {
+ ++*numKeys;
+ out = (RSAPublicKey*)realloc(out, *numKeys * sizeof(RSAPublicKey));
+ RSAPublicKey* key = out + (*numKeys - 1);
+
+#ifdef HAS_EXPONENT
+ char start_char;
+ if (fscanf(f, " %c", &start_char) != 1) goto exit;
+ if (start_char == '{') {
+ // a version 1 key has no version specifier.
+ key->exponent = 3;
+ } else if (start_char == 'v') {
+ int version;
+ if (fscanf(f, "%d {", &version) != 1) goto exit;
+ if (version == 2) {
+ key->exponent = 65537;
+ } else {
+ goto exit;
+ }
+ }
+
+ if (fscanf(f, " %i , 0x%x , { %u",
+#else
+ if (fscanf(f, " { %i , 0x%x , { %u",
+#endif
+ &(key->len), &(key->n0inv), &(key->n[0])) != 3) {
+ goto exit;
+ }
+ if (key->len != RSANUMWORDS) {
+ LOGE("key length (%d) does not match expected size\n", key->len);
+ goto exit;
+ }
+ for (i = 1; i < key->len; ++i) {
+ if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit;
+ }
+ if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit;
+ for (i = 1; i < key->len; ++i) {
+ if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit;
+ }
+ fscanf(f, " } } ");
+
+ // if the line ends in a comma, this file has more keys.
+ switch (fgetc(f)) {
+ case ',':
+ // more keys to come.
+ break;
+
+ case EOF:
+ done = true;
+ break;
+
+ default:
+ LOGE("unexpected character between keys\n");
+ goto exit;
+ }
+#ifdef HAS_EXPONENT
+ LOGI("read key e=%d\n", key->exponent);
+#endif
+ }
+ }
+
+ fclose(f);
+ return out;
+
+exit:
+ if (f) fclose(f);
+ free(out);
+ *numKeys = 0;
+ return NULL;
+}
diff --git a/verifier.h b/verifier.h
index c5a2391..b355b3e 100644
--- a/verifier.h
+++ b/verifier.h
@@ -30,6 +30,8 @@
*/
int verify_file(const char* path);
+RSAPublicKey* load_keys(const char* filename, int* numKeys);
+
#define VERIFY_SUCCESS 0
#define VERIFY_FAILURE 1
diff --git a/verifier_test.cpp b/verifier_test.cpp
index b263db8..2ef52a0 100644
--- a/verifier_test.cpp
+++ b/verifier_test.cpp
@@ -18,6 +18,7 @@
#include <stdlib.h>
#include <stdarg.h>
+#include "common.h"
#include "verifier.h"
#include "ui.h"
@@ -56,7 +57,45 @@
9135381, 1625809335, -1490225159, -1342673351,
1117190829, -57654514, 1825108855, -1281819325,
1111251351, -1726129724, 1684324211, -1773988491,
- 367251975, 810756730, -1941182952, 1175080310 }
+ 367251975, 810756730, -1941182952, 1175080310 },
+ 3
+ };
+
+RSAPublicKey test_f4_key =
+ { 64, 0xc9bd1f21,
+ { 293133087u, 3210546773u, 865313125u, 250921607u,
+ 3158780490u, 943703457u, 1242806226u, 2986289859u,
+ 2942743769u, 2457906415u, 2719374299u, 1783459420u,
+ 149579627u, 3081531591u, 3440738617u, 2788543742u,
+ 2758457512u, 1146764939u, 3699497403u, 2446203424u,
+ 1744968926u, 1159130537u, 2370028300u, 3978231572u,
+ 3392699980u, 1487782451u, 1180150567u, 2841334302u,
+ 3753960204u, 961373345u, 3333628321u, 748825784u,
+ 2978557276u, 1566596926u, 1613056060u, 2600292737u,
+ 1847226629u, 50398611u, 1890374404u, 2878700735u,
+ 2286201787u, 1401186359u, 619285059u, 731930817u,
+ 2340993166u, 1156490245u, 2992241729u, 151498140u,
+ 318782170u, 3480838990u, 2100383433u, 4223552555u,
+ 3628927011u, 4247846280u, 1759029513u, 4215632601u,
+ 2719154626u, 3490334597u, 1751299340u, 3487864726u,
+ 3668753795u, 4217506054u, 3748782284u, 3150295088u },
+ { 1772626313u, 445326068u, 3477676155u, 1758201194u,
+ 2986784722u, 491035581u, 3922936562u, 702212696u,
+ 2979856666u, 3324974564u, 2488428922u, 3056318590u,
+ 1626954946u, 664714029u, 398585816u, 3964097931u,
+ 3356701905u, 2298377729u, 2040082097u, 3025491477u,
+ 539143308u, 3348777868u, 2995302452u, 3602465520u,
+ 212480763u, 2691021393u, 1307177300u, 704008044u,
+ 2031136606u, 1054106474u, 3838318865u, 2441343869u,
+ 1477566916u, 700949900u, 2534790355u, 3353533667u,
+ 336163563u, 4106790558u, 2701448228u, 1571536379u,
+ 1103842411u, 3623110423u, 1635278839u, 1577828979u,
+ 910322800u, 715583630u, 138128831u, 1017877531u,
+ 2289162787u, 447994798u, 1897243165u, 4121561445u,
+ 4150719842u, 2131821093u, 2262395396u, 3305771534u,
+ 980753571u, 3256525190u, 3128121808u, 1072869975u,
+ 3507939515u, 4229109952u, 118381341u, 2209831334u },
+ 65537
};
RecoveryUI* ui = NULL;
@@ -75,13 +114,10 @@
bool IsTextVisible() { return false; }
bool WasTextEverVisible() { return false; }
void Print(const char* fmt, ...) {
- char buf[256];
va_list ap;
va_start(ap, fmt);
- vsnprintf(buf, 256, fmt, ap);
+ vfprintf(stderr, fmt, ap);
va_end(ap);
-
- fputs(buf, stderr);
}
void StartMenu(const char* const * headers, const char* const * items,
@@ -90,15 +126,35 @@
void EndMenu() { }
};
+void
+ui_print(const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ vfprintf(stdout, format, ap);
+ va_end(ap);
+}
+
int main(int argc, char **argv) {
- if (argc != 2) {
- fprintf(stderr, "Usage: %s <package>\n", argv[0]);
+ if (argc < 2 || argc > 4) {
+ fprintf(stderr, "Usage: %s [-f4 | -file <keys>] <package>\n", argv[0]);
return 2;
}
+ RSAPublicKey* key = &test_key;
+ int num_keys = 1;
+ ++argv;
+ if (strcmp(argv[0], "-f4") == 0) {
+ ++argv;
+ key = &test_f4_key;
+ } else if (strcmp(argv[0], "-file") == 0) {
+ ++argv;
+ key = load_keys(argv[0], &num_keys);
+ ++argv;
+ }
+
ui = new FakeUI();
- int result = verify_file(argv[1]);
+ int result = verify_file(*argv, key, num_keys);
if (result == VERIFY_SUCCESS) {
printf("SUCCESS\n");
return 0;
diff --git a/verifier_test.sh b/verifier_test.sh
index a1de5c5..378b0e5 100755
--- a/verifier_test.sh
+++ b/verifier_test.sh
@@ -73,9 +73,24 @@
run_command $WORK_DIR/verifier_test $WORK_DIR/package.zip && fail
}
+expect_succeed_f4() {
+ testname "$1 (should succeed)"
+ $ADB push $DATA_DIR/$1 $WORK_DIR/package.zip
+ run_command $WORK_DIR/verifier_test -f4 $WORK_DIR/package.zip || fail
+}
+
+expect_fail_f4() {
+ testname "$1 (should fail)"
+ $ADB push $DATA_DIR/$1 $WORK_DIR/package.zip
+ run_command $WORK_DIR/verifier_test -f4 $WORK_DIR/package.zip && fail
+}
+
expect_fail unsigned.zip
expect_fail jarsigned.zip
expect_succeed otasigned.zip
+expect_fail_f4 otasigned.zip
+expect_succeed_f4 otasigned_f4.zip
+expect_fail otasigned_f4.zip
expect_fail random.zip
expect_fail fake-eocd.zip
expect_fail alter-metadata.zip