summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.clang-format3
-rw-r--r--Android.mk12
-rw-r--r--README.md7
-rw-r--r--applypatch/Android.mk105
-rw-r--r--applypatch/applypatch.cpp13
-rw-r--r--applypatch/imgdiff.cpp109
-rw-r--r--applypatch/imgdiff_main.cpp21
-rw-r--r--applypatch/imgpatch.cpp405
-rw-r--r--applypatch/include/applypatch/applypatch.h3
-rw-r--r--applypatch/include/applypatch/imgdiff.h (renamed from applypatch/imgdiff.h)21
-rw-r--r--applypatch/include/applypatch/imgpatch.h10
-rw-r--r--bootloader_message/Android.mk1
-rw-r--r--bootloader_message/bootloader_message.cpp21
-rw-r--r--bootloader_message/include/bootloader_message/bootloader_message.h15
-rw-r--r--error_code.h2
-rw-r--r--etc/init.rc2
-rw-r--r--install.cpp468
-rw-r--r--minadbd/README.md (renamed from minadbd/README.txt)2
-rw-r--r--minui/Android.mk31
-rw-r--r--minui/events.cpp211
-rw-r--r--minui/graphics.cpp6
-rw-r--r--minui/graphics.h2
-rw-r--r--minui/graphics_drm.cpp3
-rw-r--r--minui/graphics_fbdev.cpp3
-rw-r--r--minui/include/minui/minui.h (renamed from minui/minui.h)11
-rw-r--r--minui/resources.cpp20
-rw-r--r--otafault/config.cpp6
-rw-r--r--otafault/config.h2
-rw-r--r--recovery.cpp349
-rw-r--r--res-hdpi/images/erasing_text.pngbin50373 -> 50680 bytes
-rw-r--r--res-hdpi/images/error_text.pngbin35720 -> 36036 bytes
-rw-r--r--res-hdpi/images/installing_security_text.pngbin111582 -> 113819 bytes
-rw-r--r--res-hdpi/images/installing_text.pngbin102734 -> 104768 bytes
-rw-r--r--res-hdpi/images/no_command_text.pngbin61665 -> 62037 bytes
-rw-r--r--res-mdpi/images/erasing_text.pngbin29558 -> 29898 bytes
-rw-r--r--res-mdpi/images/error_text.pngbin20926 -> 21147 bytes
-rw-r--r--res-mdpi/images/installing_security_text.pngbin69709 -> 70010 bytes
-rw-r--r--res-mdpi/images/installing_text.pngbin61548 -> 61701 bytes
-rw-r--r--res-mdpi/images/no_command_text.pngbin34232 -> 34683 bytes
-rw-r--r--res-xhdpi/images/erasing_text.pngbin72595 -> 73361 bytes
-rw-r--r--res-xhdpi/images/error_text.pngbin51739 -> 52019 bytes
-rw-r--r--res-xhdpi/images/installing_security_text.pngbin195507 -> 196871 bytes
-rw-r--r--res-xhdpi/images/installing_text.pngbin174877 -> 175570 bytes
-rw-r--r--res-xhdpi/images/no_command_text.pngbin85999 -> 86622 bytes
-rw-r--r--res-xxhdpi/images/erasing_text.pngbin119060 -> 121608 bytes
-rw-r--r--res-xxhdpi/images/error_text.pngbin84432 -> 84727 bytes
-rw-r--r--res-xxhdpi/images/installing_security_text.pngbin447442 -> 447158 bytes
-rw-r--r--res-xxhdpi/images/installing_text.pngbin416541 -> 415889 bytes
-rw-r--r--res-xxhdpi/images/no_command_text.pngbin243828 -> 243028 bytes
-rw-r--r--res-xxxhdpi/images/erasing_text.pngbin264753 -> 263768 bytes
-rw-r--r--res-xxxhdpi/images/error_text.pngbin178491 -> 176936 bytes
-rw-r--r--res-xxxhdpi/images/installing_security_text.pngbin611664 -> 610107 bytes
-rw-r--r--res-xxxhdpi/images/installing_text.pngbin569291 -> 567834 bytes
-rw-r--r--res-xxxhdpi/images/no_command_text.pngbin333053 -> 331159 bytes
-rw-r--r--roots.cpp7
-rw-r--r--screen_ui.cpp303
-rw-r--r--screen_ui.h61
-rw-r--r--stub_ui.h65
-rw-r--r--tests/Android.mk70
-rw-r--r--tests/component/bootloader_message_test.cpp165
-rw-r--r--tests/component/imgdiff_test.cpp528
-rw-r--r--tests/component/uncrypt_test.cpp174
-rw-r--r--tests/component/updater_test.cpp50
-rw-r--r--tests/component/verifier_test.cpp70
-rw-r--r--tests/manual/recovery_test.cpp141
-rw-r--r--tests/unit/dirutil_test.cpp150
-rw-r--r--tests/unit/locale_test.cpp3
-rw-r--r--tests/unit/zip_test.cpp21
-rw-r--r--tests/unit/ziputil_test.cpp191
-rw-r--r--tools/recovery_l10n/README.md3
-rw-r--r--tools/recovery_l10n/src/com/android/recovery_l10n/Main.java22
-rw-r--r--ui.cpp199
-rw-r--r--ui.h38
-rw-r--r--uncrypt/uncrypt.cpp78
-rw-r--r--update_verifier/update_verifier.cpp96
-rw-r--r--updater/Android.mk6
-rw-r--r--updater/blockimg.cpp308
-rw-r--r--updater/install.cpp5
-rw-r--r--updater/updater.cpp3
-rw-r--r--wear_ui.cpp416
-rw-r--r--wear_ui.h76
81 files changed, 3364 insertions, 1749 deletions
diff --git a/.clang-format b/.clang-format
index b8c642840..532278864 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,6 +1,7 @@
BasedOnStyle: Google
AllowShortBlocksOnASingleLine: false
-AllowShortFunctionsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Empty
+AllowShortIfStatementsOnASingleLine: true
ColumnLimit: 100
CommentPragmas: NOLINT:.*
diff --git a/Android.mk b/Android.mk
index 4a7afb743..f8e5ac24a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -76,8 +76,8 @@ LOCAL_C_INCLUDES += \
LOCAL_STATIC_LIBRARIES := \
libbatterymonitor \
libbootloader_message \
- libext4_utils_static \
- libsparse_static \
+ libext4_utils \
+ libsparse \
libziparchive \
libotautil \
libmounts \
@@ -151,7 +151,11 @@ LOCAL_SRC_FILES := \
asn1_decoder.cpp \
verifier.cpp \
ui.cpp
-LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libbase
+LOCAL_STATIC_LIBRARIES := \
+ libminui \
+ libcrypto_utils \
+ libcrypto \
+ libbase
LOCAL_CFLAGS := -Werror
include $(BUILD_STATIC_LIBRARY)
@@ -159,8 +163,8 @@ include \
$(LOCAL_PATH)/applypatch/Android.mk \
$(LOCAL_PATH)/bootloader_message/Android.mk \
$(LOCAL_PATH)/edify/Android.mk \
- $(LOCAL_PATH)/minui/Android.mk \
$(LOCAL_PATH)/minadbd/Android.mk \
+ $(LOCAL_PATH)/minui/Android.mk \
$(LOCAL_PATH)/otafault/Android.mk \
$(LOCAL_PATH)/otautil/Android.mk \
$(LOCAL_PATH)/tests/Android.mk \
diff --git a/README.md b/README.md
index dc3d44e6e..8e20b5a62 100644
--- a/README.md
+++ b/README.md
@@ -40,3 +40,10 @@ Running the manual tests
- Reboot the device immediately and run the test again. The test should save the
contents of pmsg buffer into /data/misc/recovery/inject.txt. Test will pass if
this file has expected contents.
+
+`ResourceTest` validates whether the png files are qualified as background text
+image under recovery.
+
+ 1. `adb sync data` to make sure the test-dir has the images to test.
+ 2. The test will automatically pickup and verify all `_text.png` files in
+ the test dir.
diff --git a/applypatch/Android.mk b/applypatch/Android.mk
index fa0fe8a37..bdaef1b27 100644
--- a/applypatch/Android.mk
+++ b/applypatch/Android.mk
@@ -17,7 +17,6 @@ LOCAL_PATH := $(call my-dir)
# libapplypatch (static library)
# ===============================
include $(CLEAR_VARS)
-LOCAL_CLANG := true
LOCAL_SRC_FILES := \
applypatch.cpp \
bspatch.cpp \
@@ -26,52 +25,67 @@ LOCAL_SRC_FILES := \
utils.cpp
LOCAL_MODULE := libapplypatch
LOCAL_MODULE_TAGS := eng
-LOCAL_C_INCLUDES += \
+LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/include \
bootable/recovery
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES += \
+LOCAL_STATIC_LIBRARIES := \
libotafault \
libbase \
libcrypto \
libbz \
libz
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := \
+ -DZLIB_CONST \
+ -Werror
include $(BUILD_STATIC_LIBRARY)
# libimgpatch (static library)
# ===============================
include $(CLEAR_VARS)
-LOCAL_CLANG := true
-LOCAL_SRC_FILES := bspatch.cpp imgpatch.cpp utils.cpp
+LOCAL_SRC_FILES := \
+ bspatch.cpp \
+ imgpatch.cpp \
+ utils.cpp
LOCAL_MODULE := libimgpatch
-LOCAL_C_INCLUDES += \
+LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/include \
bootable/recovery
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES += libcrypto libbz libz
-LOCAL_CFLAGS := -Werror
+LOCAL_STATIC_LIBRARIES := \
+ libcrypto \
+ libbz \
+ libz
+LOCAL_CFLAGS := \
+ -DZLIB_CONST \
+ -Werror
include $(BUILD_STATIC_LIBRARY)
# libimgpatch (host static library)
# ===============================
include $(CLEAR_VARS)
-LOCAL_CLANG := true
-LOCAL_SRC_FILES := bspatch.cpp imgpatch.cpp utils.cpp
+LOCAL_SRC_FILES := \
+ bspatch.cpp \
+ imgpatch.cpp \
+ utils.cpp
LOCAL_MODULE := libimgpatch
LOCAL_MODULE_HOST_OS := linux
-LOCAL_C_INCLUDES += \
+LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/include \
bootable/recovery
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES += libcrypto libbz libz
-LOCAL_CFLAGS := -Werror
+LOCAL_STATIC_LIBRARIES := \
+ libcrypto \
+ libbz \
+ libz
+LOCAL_CFLAGS := \
+ -DZLIB_CONST \
+ -Werror
include $(BUILD_HOST_STATIC_LIBRARY)
# libapplypatch_modes (static library)
# ===============================
include $(CLEAR_VARS)
-LOCAL_CLANG := true
LOCAL_SRC_FILES := \
applypatch_modes.cpp
LOCAL_MODULE := libapplypatch_modes
@@ -87,7 +101,6 @@ include $(BUILD_STATIC_LIBRARY)
# applypatch (target executable)
# ===============================
include $(CLEAR_VARS)
-LOCAL_CLANG := true
LOCAL_SRC_FILES := applypatch_main.cpp
LOCAL_MODULE := applypatch
LOCAL_C_INCLUDES := bootable/recovery
@@ -106,18 +119,60 @@ LOCAL_SHARED_LIBRARIES := \
LOCAL_CFLAGS := -Werror
include $(BUILD_EXECUTABLE)
+libimgdiff_src_files := \
+ imgdiff.cpp \
+ utils.cpp
+
+# libbsdiff is compiled with -D_FILE_OFFSET_BITS=64.
+libimgdiff_cflags := \
+ -Werror \
+ -D_FILE_OFFSET_BITS=64
+
+libimgdiff_static_libraries := \
+ libbsdiff \
+ libdivsufsort \
+ libdivsufsort64 \
+ libbase \
+ libz
+
+# libimgdiff (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ $(libimgdiff_src_files)
+LOCAL_MODULE := libimgdiff
+LOCAL_CFLAGS := \
+ $(libimgdiff_cflags)
+LOCAL_STATIC_LIBRARIES := \
+ $(libimgdiff_static_libraries)
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+include $(BUILD_STATIC_LIBRARY)
+
+# libimgdiff (host static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ $(libimgdiff_src_files)
+LOCAL_MODULE := libimgdiff
+LOCAL_CFLAGS := \
+ $(libimgdiff_cflags)
+LOCAL_STATIC_LIBRARIES := \
+ $(libimgdiff_static_libraries)
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+include $(BUILD_HOST_STATIC_LIBRARY)
+
# imgdiff (host static executable)
# ===============================
include $(CLEAR_VARS)
-LOCAL_CLANG := true
-LOCAL_SRC_FILES := imgdiff.cpp utils.cpp
+LOCAL_SRC_FILES := imgdiff_main.cpp
LOCAL_MODULE := imgdiff
-LOCAL_STATIC_LIBRARIES += \
- libbsdiff \
- libbz \
- libdivsufsort64 \
- libdivsufsort \
- libz
LOCAL_CFLAGS := -Werror
-LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_STATIC_LIBRARIES := \
+ libimgdiff \
+ $(libimgdiff_static_libraries) \
+ libbz
include $(BUILD_HOST_EXECUTABLE)
diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp
index 95389da6e..500663120 100644
--- a/applypatch/applypatch.cpp
+++ b/applypatch/applypatch.cpp
@@ -332,6 +332,17 @@ int WriteToPartition(const unsigned char* data, size_t len, const std::string& t
success = true;
break;
}
+
+ if (ota_close(fd) != 0) {
+ printf("failed to close %s: %s\n", partition, strerror(errno));
+ return -1;
+ }
+
+ fd.reset(ota_open(partition, O_RDWR));
+ if (fd == -1) {
+ printf("failed to reopen %s for retry write && verify: %s\n", partition, strerror(errno));
+ return -1;
+ }
}
if (!success) {
@@ -380,7 +391,7 @@ int ParseSha1(const char* str, uint8_t* digest) {
// 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 std::vector<std::string>& patch_sha1_str) {
+static int FindMatchingPatch(uint8_t* sha1, const std::vector<std::string>& patch_sha1_str) {
for (size_t i = 0; i < patch_sha1_str.size(); ++i) {
uint8_t patch_sha1[SHA_DIGEST_LENGTH];
if (ParseSha1(patch_sha1_str[i].c_str(), patch_sha1) == 0 &&
diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp
index 7d6ebab6e..2f0e1651c 100644
--- a/applypatch/imgdiff.cpp
+++ b/applypatch/imgdiff.cpp
@@ -121,19 +121,23 @@
* information that is stored on the system partition.
*/
+#include "applypatch/imgdiff.h"
+
#include <errno.h>
-#include <inttypes.h>
+#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
-#include <unistd.h>
#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
#include <bsdiff.h>
+#include <zlib.h>
-#include "zlib.h"
-#include "imgdiff.h"
#include "utils.h"
typedef struct {
@@ -375,8 +379,7 @@ unsigned char* ReadZip(const char* filename,
* return value when done with all the chunks. Returns NULL on
* failure.
*/
-unsigned char* ReadImage(const char* filename,
- int* num_chunks, ImageChunk** chunks) {
+unsigned char* ReadImage(const char* filename, int* num_chunks, ImageChunk** chunks) {
struct stat st;
if (stat(filename, &st) != 0) {
printf("failed to stat \"%s\": %s\n", filename, strerror(errno));
@@ -384,19 +387,12 @@ unsigned char* ReadImage(const char* filename,
}
size_t sz = static_cast<size_t>(st.st_size);
- unsigned char* img = static_cast<unsigned char*>(malloc(sz + 4));
- FILE* f = fopen(filename, "rb");
- if (fread(img, 1, sz, f) != sz) {
+ unsigned char* img = static_cast<unsigned char*>(malloc(sz));
+ android::base::unique_fd fd(open(filename, O_RDONLY));
+ if (!android::base::ReadFully(fd, img, sz)) {
printf("failed to read \"%s\" %s\n", filename, strerror(errno));
- fclose(f);
- return NULL;
+ return nullptr;
}
- fclose(f);
-
- // append 4 zero bytes to the data so we can always search for the
- // four-byte string 1f8b0800 starting at any point in the actual
- // file data, without special-casing the end of the data.
- memset(img+sz, 0, 4);
size_t pos = 0;
@@ -404,7 +400,7 @@ unsigned char* ReadImage(const char* filename,
*chunks = NULL;
while (pos < sz) {
- unsigned char* p = img+pos;
+ unsigned char* p = img + pos;
if (sz - pos >= 4 &&
p[0] == 0x1f && p[1] == 0x8b &&
@@ -414,8 +410,7 @@ unsigned char* ReadImage(const char* filename,
size_t chunk_offset = pos;
*num_chunks += 3;
- *chunks = static_cast<ImageChunk*>(realloc(*chunks,
- *num_chunks * sizeof(ImageChunk)));
+ *chunks = static_cast<ImageChunk*>(realloc(*chunks, *num_chunks * sizeof(ImageChunk)));
ImageChunk* curr = *chunks + (*num_chunks-3);
// create a normal chunk for the header.
@@ -503,8 +498,7 @@ unsigned char* ReadImage(const char* filename,
// the decompression.
size_t footer_size = Read4(p-4);
if (footer_size != curr[-2].len) {
- printf("Error: footer size %zu != decompressed size %zu\n",
- footer_size, curr[-2].len);
+ printf("Error: footer size %zu != decompressed size %zu\n", footer_size, curr[-2].len);
free(img);
return NULL;
}
@@ -522,10 +516,8 @@ unsigned char* ReadImage(const char* filename,
curr->data = p;
for (curr->len = 0; curr->len < (sz - pos); ++curr->len) {
- if (p[curr->len] == 0x1f &&
- p[curr->len+1] == 0x8b &&
- p[curr->len+2] == 0x08 &&
- p[curr->len+3] == 0x00) {
+ if (sz - pos >= 4 && p[curr->len] == 0x1f && p[curr->len + 1] == 0x8b &&
+ p[curr->len + 2] == 0x08 && p[curr->len + 3] == 0x00) {
break;
}
}
@@ -624,12 +616,12 @@ int ReconstructDeflateChunk(ImageChunk* chunk) {
}
/*
- * Given source and target chunks, compute a bsdiff patch between them
- * by running bsdiff in a subprocess. Return the patch data, placing
- * its length in *size. Return NULL on failure. We expect the bsdiff
- * program to be in the path.
+ * Given source and target chunks, compute a bsdiff patch between them.
+ * Return the patch data, placing its length in *size. Return NULL on failure.
+ * |bsdiff_cache| can be used to cache the suffix array if the same |src| chunk
+ * is used repeatedly, pass nullptr if not needed.
*/
-unsigned char* MakePatch(ImageChunk* src, ImageChunk* tgt, size_t* size) {
+unsigned char* MakePatch(ImageChunk* src, ImageChunk* tgt, size_t* size, saidx_t** bsdiff_cache) {
if (tgt->type == CHUNK_NORMAL) {
if (tgt->len <= 160) {
tgt->type = CHUNK_RAW;
@@ -638,7 +630,11 @@ unsigned char* MakePatch(ImageChunk* src, ImageChunk* tgt, size_t* size) {
}
}
+#if defined(__ANDROID__)
+ char ptemp[] = "/data/local/tmp/imgdiff-patch-XXXXXX";
+#else
char ptemp[] = "/tmp/imgdiff-patch-XXXXXX";
+#endif
int fd = mkstemp(ptemp);
if (fd == -1) {
@@ -649,7 +645,7 @@ unsigned char* MakePatch(ImageChunk* src, ImageChunk* tgt, size_t* size) {
close(fd); // temporary file is created and we don't need its file
// descriptor
- int r = bsdiff::bsdiff(src->data, src->len, tgt->data, tgt->len, ptemp);
+ int r = bsdiff::bsdiff(src->data, src->len, tgt->data, tgt->len, ptemp, bsdiff_cache);
if (r != 0) {
printf("bsdiff() failed: %d\n", r);
return NULL;
@@ -794,10 +790,8 @@ void MergeAdjacentNormalChunks(ImageChunk* chunks, int* num_chunks) {
*num_chunks = out;
}
-ImageChunk* FindChunkByName(const char* name,
- ImageChunk* chunks, int num_chunks) {
- int i;
- for (i = 0; i < num_chunks; ++i) {
+ImageChunk* FindChunkByName(const char* name, ImageChunk* chunks, int num_chunks) {
+ for (int i = 0; i < num_chunks; ++i) {
if (chunks[i].type == CHUNK_DEFLATE && chunks[i].filename &&
strcmp(name, chunks[i].filename) == 0) {
return chunks+i;
@@ -813,11 +807,11 @@ void DumpChunks(ImageChunk* chunks, int num_chunks) {
}
}
-int main(int argc, char** argv) {
- int zip_mode = 0;
+int imgdiff(int argc, const char** argv) {
+ bool zip_mode = false;
if (argc >= 2 && strcmp(argv[1], "-z") == 0) {
- zip_mode = 1;
+ zip_mode = true;
--argc;
++argv;
}
@@ -881,12 +875,10 @@ int main(int argc, char** argv) {
// Verify that the source and target images have the same chunk
// structure (ie, the same sequence of deflate and normal chunks).
- if (!zip_mode) {
- // Merge the gzip header and footer in with any adjacent
- // normal chunks.
- MergeAdjacentNormalChunks(tgt_chunks, &num_tgt_chunks);
- MergeAdjacentNormalChunks(src_chunks, &num_src_chunks);
- }
+ // Merge the gzip header and footer in with any adjacent
+ // normal chunks.
+ MergeAdjacentNormalChunks(tgt_chunks, &num_tgt_chunks);
+ MergeAdjacentNormalChunks(src_chunks, &num_src_chunks);
if (num_src_chunks != num_tgt_chunks) {
printf("source and target don't have same number of chunks!\n");
@@ -898,8 +890,7 @@ int main(int argc, char** argv) {
}
for (i = 0; i < num_src_chunks; ++i) {
if (src_chunks[i].type != tgt_chunks[i].type) {
- printf("source and target don't have same chunk "
- "structure! (chunk %d)\n", i);
+ printf("source and target don't have same chunk structure! (chunk %d)\n", i);
printf("source chunks:\n");
DumpChunks(src_chunks, num_src_chunks);
printf("target chunks:\n");
@@ -980,30 +971,31 @@ int main(int argc, char** argv) {
unsigned char** patch_data = static_cast<unsigned char**>(malloc(
num_tgt_chunks * sizeof(unsigned char*)));
size_t* patch_size = static_cast<size_t*>(malloc(num_tgt_chunks * sizeof(size_t)));
+ saidx_t* bsdiff_cache = nullptr;
for (i = 0; i < num_tgt_chunks; ++i) {
if (zip_mode) {
ImageChunk* src;
if (tgt_chunks[i].type == CHUNK_DEFLATE &&
- (src = FindChunkByName(tgt_chunks[i].filename, src_chunks,
- num_src_chunks))) {
- patch_data[i] = MakePatch(src, tgt_chunks+i, patch_size+i);
+ (src = FindChunkByName(tgt_chunks[i].filename, src_chunks, num_src_chunks))) {
+ patch_data[i] = MakePatch(src, tgt_chunks + i, patch_size + i, nullptr);
} else {
- patch_data[i] = MakePatch(src_chunks, tgt_chunks+i, patch_size+i);
+ patch_data[i] = MakePatch(src_chunks, tgt_chunks + i, patch_size + i, &bsdiff_cache);
}
} else {
if (i == 1 && bonus_data) {
printf(" using %zu bytes of bonus data for chunk %d\n", bonus_size, i);
- src_chunks[i].data = static_cast<unsigned char*>(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].data =
+ static_cast<unsigned char*>(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);
+ patch_data[i] = MakePatch(src_chunks + i, tgt_chunks + i, patch_size + i, nullptr);
}
- printf("patch %3d is %zu bytes (of %zu)\n",
- i, patch_size[i], tgt_chunks[i].source_len);
+ printf("patch %3d is %zu bytes (of %zu)\n", i, patch_size[i], tgt_chunks[i].source_len);
}
+ free(bsdiff_cache);
+ free(src_chunks);
// Figure out how big the imgdiff file header is going to be, so
// that we can correctly compute the offset of each bsdiff patch
@@ -1080,6 +1072,7 @@ int main(int argc, char** argv) {
}
}
+ free(tgt_chunks);
free(patch_data);
free(patch_size);
diff --git a/applypatch/imgdiff_main.cpp b/applypatch/imgdiff_main.cpp
new file mode 100644
index 000000000..7d5bdf9aa
--- /dev/null
+++ b/applypatch/imgdiff_main.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "applypatch/imgdiff.h"
+
+int main(int argc, char** argv) {
+ return imgdiff(argc, const_cast<const char**>(argv));
+}
diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp
index 1c4409e36..00ea90efa 100644
--- a/applypatch/imgpatch.cpp
+++ b/applypatch/imgpatch.cpp
@@ -14,32 +14,34 @@
* limitations under the License.
*/
-// See imgdiff.c in this directory for a description of the patch file
+// See imgdiff.cpp in this directory for a description of the patch file
// format.
+#include <applypatch/imgpatch.h>
+
+#include <errno.h>
#include <stdio.h>
+#include <string.h>
#include <sys/cdefs.h>
#include <sys/stat.h>
-#include <errno.h>
#include <unistd.h>
-#include <string.h>
#include <string>
#include <vector>
-#include "zlib.h"
-#include "openssl/sha.h"
-#include "applypatch/applypatch.h"
-#include "imgdiff.h"
+#include <applypatch/applypatch.h>
+#include <applypatch/imgdiff.h>
+#include <openssl/sha.h>
+#include <zlib.h>
+
#include "utils.h"
int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
const unsigned char* patch_data, ssize_t patch_size,
SinkFn sink, void* token) {
- Value patch(VAL_BLOB, std::string(
- reinterpret_cast<const char*>(patch_data), patch_size));
+ Value patch(VAL_BLOB, std::string(reinterpret_cast<const char*>(patch_data), patch_size));
- return ApplyImagePatch(old_data, old_size, &patch, sink, token, nullptr, nullptr);
+ return ApplyImagePatch(old_data, old_size, &patch, sink, token, nullptr, nullptr);
}
/*
@@ -48,208 +50,201 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
* file, and update the SHA context with the output data as well.
* Return 0 on success.
*/
-int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
- const Value* patch,
- SinkFn sink, void* token, SHA_CTX* ctx,
- const Value* bonus_data) {
- if (patch->data.size() < 12) {
- printf("patch too short to contain header\n");
- return -1;
+int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value* patch,
+ SinkFn sink, void* token, SHA_CTX* ctx, const Value* bonus_data) {
+ if (patch->data.size() < 12) {
+ printf("patch too short to contain header\n");
+ return -1;
+ }
+
+ // IMGDIFF2 uses CHUNK_NORMAL, CHUNK_DEFLATE, and CHUNK_RAW.
+ // (IMGDIFF1, which is no longer supported, used CHUNK_NORMAL and
+ // CHUNK_GZIP.)
+ size_t pos = 12;
+ const char* header = &patch->data[0];
+ if (memcmp(header, "IMGDIFF2", 8) != 0) {
+ printf("corrupt patch file header (magic number)\n");
+ return -1;
+ }
+
+ int num_chunks = Read4(header + 8);
+
+ for (int i = 0; i < num_chunks; ++i) {
+ // each chunk's header record starts with 4 bytes.
+ if (pos + 4 > patch->data.size()) {
+ printf("failed to read chunk %d record\n", i);
+ return -1;
}
+ int type = Read4(&patch->data[pos]);
+ pos += 4;
+
+ if (type == CHUNK_NORMAL) {
+ const char* normal_header = &patch->data[pos];
+ pos += 24;
+ if (pos > patch->data.size()) {
+ printf("failed to read chunk %d normal header data\n", i);
+ return -1;
+ }
- // IMGDIFF2 uses CHUNK_NORMAL, CHUNK_DEFLATE, and CHUNK_RAW.
- // (IMGDIFF1, which is no longer supported, used CHUNK_NORMAL and
- // CHUNK_GZIP.)
- size_t pos = 12;
- const char* header = &patch->data[0];
- if (memcmp(header, "IMGDIFF2", 8) != 0) {
- printf("corrupt patch file header (magic number)\n");
+ size_t src_start = Read8(normal_header);
+ size_t src_len = Read8(normal_header + 8);
+ size_t patch_offset = Read8(normal_header + 16);
+
+ if (src_start + src_len > static_cast<size_t>(old_size)) {
+ printf("source data too short\n");
return -1;
- }
+ }
+ ApplyBSDiffPatch(old_data + src_start, src_len, patch, patch_offset, sink, token, ctx);
+ } else if (type == CHUNK_RAW) {
+ const char* raw_header = &patch->data[pos];
+ pos += 4;
+ if (pos > patch->data.size()) {
+ printf("failed to read chunk %d raw header data\n", i);
+ return -1;
+ }
- int num_chunks = Read4(header+8);
+ ssize_t data_len = Read4(raw_header);
- for (int i = 0; i < num_chunks; ++i) {
- // each chunk's header record starts with 4 bytes.
- if (pos + 4 > patch->data.size()) {
- printf("failed to read chunk %d record\n", i);
- return -1;
+ if (pos + data_len > patch->data.size()) {
+ printf("failed to read chunk %d raw data\n", i);
+ return -1;
+ }
+ if (ctx) SHA1_Update(ctx, &patch->data[pos], data_len);
+ if (sink(reinterpret_cast<const unsigned char*>(&patch->data[pos]), data_len, token) !=
+ data_len) {
+ printf("failed to write chunk %d raw data\n", i);
+ return -1;
+ }
+ pos += data_len;
+ } else if (type == CHUNK_DEFLATE) {
+ // deflate chunks have an additional 60 bytes in their chunk header.
+ const char* deflate_header = &patch->data[pos];
+ pos += 60;
+ if (pos > patch->data.size()) {
+ printf("failed to read chunk %d deflate header data\n", i);
+ return -1;
+ }
+
+ size_t src_start = Read8(deflate_header);
+ size_t src_len = Read8(deflate_header + 8);
+ size_t patch_offset = Read8(deflate_header + 16);
+ size_t expanded_len = Read8(deflate_header + 24);
+ size_t target_len = Read8(deflate_header + 32);
+ int level = Read4(deflate_header + 40);
+ int method = Read4(deflate_header + 44);
+ int windowBits = Read4(deflate_header + 48);
+ int memLevel = Read4(deflate_header + 52);
+ int strategy = Read4(deflate_header + 56);
+
+ if (src_start + src_len > static_cast<size_t>(old_size)) {
+ printf("source data too short\n");
+ return -1;
+ }
+
+ // 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->data.size() : 0;
+
+ std::vector<unsigned char> expanded_source(expanded_len);
+
+ // inflate() doesn't like strm.next_out being a nullptr even with
+ // avail_out being zero (Z_STREAM_ERROR).
+ if (expanded_len != 0) {
+ z_stream strm;
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ strm.avail_in = src_len;
+ strm.next_in = old_data + src_start;
+ strm.avail_out = expanded_len;
+ strm.next_out = expanded_source.data();
+
+ int ret = inflateInit2(&strm, -15);
+ if (ret != Z_OK) {
+ printf("failed to init source inflation: %d\n", ret);
+ return -1;
}
- int type = Read4(&patch->data[pos]);
- pos += 4;
-
- if (type == CHUNK_NORMAL) {
- const char* normal_header = &patch->data[pos];
- pos += 24;
- if (pos > patch->data.size()) {
- printf("failed to read chunk %d normal header data\n", i);
- return -1;
- }
-
- size_t src_start = Read8(normal_header);
- size_t src_len = Read8(normal_header+8);
- size_t patch_offset = Read8(normal_header+16);
-
- if (src_start + src_len > static_cast<size_t>(old_size)) {
- printf("source data too short\n");
- return -1;
- }
- ApplyBSDiffPatch(old_data + src_start, src_len,
- patch, patch_offset, sink, token, ctx);
- } else if (type == CHUNK_RAW) {
- const char* raw_header = &patch->data[pos];
- pos += 4;
- if (pos > patch->data.size()) {
- printf("failed to read chunk %d raw header data\n", i);
- return -1;
- }
-
- ssize_t data_len = Read4(raw_header);
-
- if (pos + data_len > patch->data.size()) {
- printf("failed to read chunk %d raw data\n", i);
- return -1;
- }
- if (ctx) SHA1_Update(ctx, &patch->data[pos], data_len);
- if (sink(reinterpret_cast<const unsigned char*>(&patch->data[pos]),
- data_len, token) != data_len) {
- printf("failed to write chunk %d raw data\n", i);
- return -1;
- }
- pos += data_len;
- } else if (type == CHUNK_DEFLATE) {
- // deflate chunks have an additional 60 bytes in their chunk header.
- const char* deflate_header = &patch->data[pos];
- pos += 60;
- if (pos > patch->data.size()) {
- printf("failed to read chunk %d deflate header data\n", i);
- return -1;
- }
-
- size_t src_start = Read8(deflate_header);
- size_t src_len = Read8(deflate_header+8);
- size_t patch_offset = Read8(deflate_header+16);
- size_t expanded_len = Read8(deflate_header+24);
- size_t target_len = Read8(deflate_header+32);
- int level = Read4(deflate_header+40);
- int method = Read4(deflate_header+44);
- int windowBits = Read4(deflate_header+48);
- int memLevel = Read4(deflate_header+52);
- int strategy = Read4(deflate_header+56);
-
- if (src_start + src_len > static_cast<size_t>(old_size)) {
- printf("source data too short\n");
- return -1;
- }
-
- // 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->data.size() : 0;
-
- std::vector<unsigned char> expanded_source(expanded_len);
-
- // inflate() doesn't like strm.next_out being a nullptr even with
- // avail_out being zero (Z_STREAM_ERROR).
- if (expanded_len != 0) {
- z_stream strm;
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
- strm.opaque = Z_NULL;
- strm.avail_in = src_len;
- strm.next_in = (unsigned char*)(old_data + src_start);
- strm.avail_out = expanded_len;
- strm.next_out = expanded_source.data();
-
- int ret;
- ret = inflateInit2(&strm, -15);
- if (ret != Z_OK) {
- printf("failed to init source inflation: %d\n", ret);
- return -1;
- }
-
- // Because we've provided enough room to accommodate the output
- // data, we expect one call to inflate() to suffice.
- ret = inflate(&strm, Z_SYNC_FLUSH);
- if (ret != Z_STREAM_END) {
- printf("source inflation returned %d\n", ret);
- return -1;
- }
- // We should have filled the output buffer exactly, except
- // for the bonus_size.
- if (strm.avail_out != bonus_size) {
- printf("source inflation short by %zu bytes\n", strm.avail_out-bonus_size);
- return -1;
- }
- inflateEnd(&strm);
-
- if (bonus_size) {
- memcpy(expanded_source.data() + (expanded_len - bonus_size),
- &bonus_data->data[0], bonus_size);
- }
- }
-
- // Next, apply the bsdiff patch (in memory) to the uncompressed
- // data.
- std::vector<unsigned char> uncompressed_target_data;
- if (ApplyBSDiffPatchMem(expanded_source.data(), expanded_len,
- patch, patch_offset,
- &uncompressed_target_data) != 0) {
- return -1;
- }
- if (uncompressed_target_data.size() != target_len) {
- printf("expected target len to be %zu, but it's %zu\n",
- target_len, uncompressed_target_data.size());
- return -1;
- }
-
- // Now compress the target data and append it to the output.
-
- // we're done with the expanded_source data buffer, so we'll
- // reuse that memory to receive the output of deflate.
- if (expanded_source.size() < 32768U) {
- expanded_source.resize(32768U);
- }
-
- {
- std::vector<unsigned char>& temp_data = expanded_source;
-
- // now the deflate stream
- z_stream strm;
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
- strm.opaque = Z_NULL;
- strm.avail_in = uncompressed_target_data.size();
- strm.next_in = uncompressed_target_data.data();
- int ret = deflateInit2(&strm, level, method, windowBits, memLevel, strategy);
- if (ret != Z_OK) {
- printf("failed to init uncompressed data deflation: %d\n", ret);
- return -1;
- }
- do {
- strm.avail_out = temp_data.size();
- strm.next_out = temp_data.data();
- ret = deflate(&strm, Z_FINISH);
- ssize_t have = temp_data.size() - strm.avail_out;
-
- if (sink(temp_data.data(), have, token) != have) {
- printf("failed to write %zd compressed bytes to output\n",
- have);
- return -1;
- }
- if (ctx) SHA1_Update(ctx, temp_data.data(), have);
- } while (ret != Z_STREAM_END);
- deflateEnd(&strm);
- }
- } else {
- printf("patch chunk %d is unknown type %d\n", i, type);
- return -1;
+
+ // Because we've provided enough room to accommodate the output
+ // data, we expect one call to inflate() to suffice.
+ ret = inflate(&strm, Z_SYNC_FLUSH);
+ if (ret != Z_STREAM_END) {
+ printf("source inflation returned %d\n", ret);
+ return -1;
+ }
+ // We should have filled the output buffer exactly, except
+ // for the bonus_size.
+ if (strm.avail_out != bonus_size) {
+ printf("source inflation short by %zu bytes\n", strm.avail_out - bonus_size);
+ return -1;
}
+ inflateEnd(&strm);
+
+ if (bonus_size) {
+ memcpy(expanded_source.data() + (expanded_len - bonus_size), &bonus_data->data[0],
+ bonus_size);
+ }
+ }
+
+ // Next, apply the bsdiff patch (in memory) to the uncompressed data.
+ std::vector<unsigned char> uncompressed_target_data;
+ if (ApplyBSDiffPatchMem(expanded_source.data(), expanded_len, patch, patch_offset,
+ &uncompressed_target_data) != 0) {
+ return -1;
+ }
+ if (uncompressed_target_data.size() != target_len) {
+ printf("expected target len to be %zu, but it's %zu\n", target_len,
+ uncompressed_target_data.size());
+ return -1;
+ }
+
+ // Now compress the target data and append it to the output.
+
+ // we're done with the expanded_source data buffer, so we'll
+ // reuse that memory to receive the output of deflate.
+ if (expanded_source.size() < 32768U) {
+ expanded_source.resize(32768U);
+ }
+
+ {
+ std::vector<unsigned char>& temp_data = expanded_source;
+
+ // now the deflate stream
+ z_stream strm;
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ strm.avail_in = uncompressed_target_data.size();
+ strm.next_in = uncompressed_target_data.data();
+ int ret = deflateInit2(&strm, level, method, windowBits, memLevel, strategy);
+ if (ret != Z_OK) {
+ printf("failed to init uncompressed data deflation: %d\n", ret);
+ return -1;
+ }
+ do {
+ strm.avail_out = temp_data.size();
+ strm.next_out = temp_data.data();
+ ret = deflate(&strm, Z_FINISH);
+ ssize_t have = temp_data.size() - strm.avail_out;
+
+ if (sink(temp_data.data(), have, token) != have) {
+ printf("failed to write %zd compressed bytes to output\n", have);
+ return -1;
+ }
+ if (ctx) SHA1_Update(ctx, temp_data.data(), have);
+ } while (ret != Z_STREAM_END);
+ deflateEnd(&strm);
+ }
+ } else {
+ printf("patch chunk %d is unknown type %d\n", i, type);
+ return -1;
}
+ }
- return 0;
+ return 0;
}
diff --git a/applypatch/include/applypatch/applypatch.h b/applypatch/include/applypatch/applypatch.h
index ca3dafbc9..4489decb6 100644
--- a/applypatch/include/applypatch/applypatch.h
+++ b/applypatch/include/applypatch/applypatch.h
@@ -63,9 +63,8 @@ int applypatch_flash(const char* source_filename, const char* target_filename,
int LoadFileContents(const char* filename, FileContents* file);
int SaveFileContents(const char* filename, const FileContents* file);
-int FindMatchingPatch(uint8_t* sha1, const std::vector<std::string>& patch_sha1_str);
-// bsdiff.cpp
+// bspatch.cpp
void ShowBSDiffLicense();
int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
const Value* patch, ssize_t patch_offset,
diff --git a/applypatch/imgdiff.h b/applypatch/include/applypatch/imgdiff.h
index f2069b4f3..22cbd4fa0 100644
--- a/applypatch/imgdiff.h
+++ b/applypatch/include/applypatch/imgdiff.h
@@ -14,17 +14,26 @@
* limitations under the License.
*/
+#ifndef _APPLYPATCH_IMGDIFF_H
+#define _APPLYPATCH_IMGDIFF_H
+
+#include <stddef.h>
+
// Image patch chunk types
-#define CHUNK_NORMAL 0
-#define CHUNK_GZIP 1 // version 1 only
-#define CHUNK_DEFLATE 2 // version 2 only
-#define CHUNK_RAW 3 // version 2 only
+#define CHUNK_NORMAL 0
+#define CHUNK_GZIP 1 // version 1 only
+#define CHUNK_DEFLATE 2 // version 2 only
+#define CHUNK_RAW 3 // version 2 only
// The gzip header size is actually variable, but we currently don't
// support gzipped data with any of the optional fields, so for now it
// will always be ten bytes. See RFC 1952 for the definition of the
// gzip format.
-#define GZIP_HEADER_LEN 10
+static constexpr size_t GZIP_HEADER_LEN = 10;
// The gzip footer size really is fixed.
-#define GZIP_FOOTER_LEN 8
+static constexpr size_t GZIP_FOOTER_LEN = 8;
+
+int imgdiff(int argc, const char** argv);
+
+#endif // _APPLYPATCH_IMGDIFF_H
diff --git a/applypatch/include/applypatch/imgpatch.h b/applypatch/include/applypatch/imgpatch.h
index 64d9aa9eb..6549f79f0 100644
--- a/applypatch/include/applypatch/imgpatch.h
+++ b/applypatch/include/applypatch/imgpatch.h
@@ -14,13 +14,15 @@
* limitations under the License.
*/
-#ifndef _IMGPATCH_H
-#define _IMGPATCH_H
+#ifndef _APPLYPATCH_IMGPATCH_H
+#define _APPLYPATCH_IMGPATCH_H
-typedef ssize_t (*SinkFn)(const unsigned char*, ssize_t, void*);
+#include <sys/types.h>
+
+using SinkFn = ssize_t (*)(const unsigned char*, ssize_t, void*);
int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
const unsigned char* patch_data, ssize_t patch_size,
SinkFn sink, void* token);
-#endif //_IMGPATCH_H
+#endif // _APPLYPATCH_IMGPATCH_H
diff --git a/bootloader_message/Android.mk b/bootloader_message/Android.mk
index 815ac67d7..a8c50819b 100644
--- a/bootloader_message/Android.mk
+++ b/bootloader_message/Android.mk
@@ -19,6 +19,7 @@ LOCAL_CLANG := true
LOCAL_SRC_FILES := bootloader_message.cpp
LOCAL_MODULE := libbootloader_message
LOCAL_STATIC_LIBRARIES := libbase libfs_mgr
+LOCAL_CFLAGS := -Werror
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
include $(BUILD_STATIC_LIBRARY)
diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp
index 9a5671843..b873d3dc3 100644
--- a/bootloader_message/bootloader_message.cpp
+++ b/bootloader_message/bootloader_message.cpp
@@ -176,6 +176,27 @@ bool write_bootloader_message(const std::vector<std::string>& options, std::stri
return write_bootloader_message(boot, err);
}
+bool update_bootloader_message(const std::vector<std::string>& options, std::string* err) {
+ bootloader_message boot;
+ if (!read_bootloader_message(&boot, err)) {
+ return false;
+ }
+
+ // Zero out the entire fields.
+ memset(boot.command, 0, sizeof(boot.command));
+ memset(boot.recovery, 0, sizeof(boot.recovery));
+
+ strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
+ strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
+ for (const auto& s : options) {
+ strlcat(boot.recovery, s.c_str(), sizeof(boot.recovery));
+ if (s.back() != '\n') {
+ strlcat(boot.recovery, "\n", sizeof(boot.recovery));
+ }
+ }
+ return write_bootloader_message(boot, err);
+}
+
bool write_reboot_bootloader(std::string* err) {
bootloader_message boot;
if (!read_bootloader_message(&boot, err)) {
diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h
index e45f42487..bc5104ddf 100644
--- a/bootloader_message/include/bootloader_message/bootloader_message.h
+++ b/bootloader_message/include/bootloader_message/bootloader_message.h
@@ -22,7 +22,7 @@
#include <stdint.h>
// Spaces used by misc partition are as below:
-// 0 - 2K Bootloader Message
+// 0 - 2K For bootloader_message
// 2K - 16K Used by Vendor's bootloader (the 2K - 4K range may be optionally used
// as bootloader_message_ab struct)
// 16K - 64K Used by uncrypt and recovery to store wipe_package for A/B devices
@@ -42,8 +42,9 @@ static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024;
* It is also updated by the bootloader when firmware update
* is complete (to boot into recovery for any final cleanup)
*
- * The status field is written by the bootloader after the
- * completion of an "update-radio" or "update-hboot" command.
+ * The status field was used by the bootloader after the completion
+ * of an "update-radio" or "update-hboot" command, which has been
+ * deprecated since Froyo.
*
* The recovery field is only written by linux and used
* for the system to send a message to recovery or the
@@ -173,6 +174,7 @@ static_assert(sizeof(struct bootloader_control) ==
sizeof(((struct bootloader_message_ab *)0)->slot_suffix),
"struct bootloader_control has wrong size");
#endif
+
#ifdef __cplusplus
#include <string>
@@ -192,9 +194,14 @@ bool write_bootloader_message(const bootloader_message& boot, std::string* err);
bool write_bootloader_message_to(const bootloader_message& boot,
const std::string& misc_blk_device, std::string* err);
-// Write bootloader message (boots into recovery with the options) to BCB.
+// Write bootloader message (boots into recovery with the options) to BCB. Will
+// set the command and recovery fields, and reset the rest.
bool write_bootloader_message(const std::vector<std::string>& options, std::string* err);
+// Update bootloader message (boots into recovery with the options) to BCB. Will
+// only update the command and recovery fields.
+bool update_bootloader_message(const std::vector<std::string>& options, std::string* err);
+
// Clear BCB.
bool clear_bootloader_message(std::string* err);
diff --git a/error_code.h b/error_code.h
index 92b1574d4..5dad6b263 100644
--- a/error_code.h
+++ b/error_code.h
@@ -47,7 +47,7 @@ enum CauseCode {
enum UncryptErrorCode {
kUncryptNoError = -1,
- kUncryptErrorHolder = 50,
+ kUncryptErrorPlaceholder = 50,
kUncryptTimeoutError = 100,
kUncryptFileRemoveError,
kUncryptFileOpenError,
diff --git a/etc/init.rc b/etc/init.rc
index b1473ba4b..477e13d5e 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -30,6 +30,7 @@ on init
write /proc/sys/vm/max_map_count 1000000
on fs
+ write /sys/class/android_usb/android0/f_ffs/aliases adb
mkdir /dev/usb-ffs 0770 shell shell
mkdir /dev/usb-ffs/adb 0770 shell shell
mount functionfs adb /dev/usb-ffs/adb uid=2000,gid=2000
@@ -37,7 +38,6 @@ on fs
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 18D1
write /sys/class/android_usb/android0/idProduct D001
- write /sys/class/android_usb/android0/f_ffs/aliases adb
write /sys/class/android_usb/android0/functions adb
write /sys/class/android_usb/android0/iManufacturer ${ro.product.manufacturer}
write /sys/class/android_usb/android0/iProduct ${ro.product.model}
diff --git a/install.cpp b/install.cpp
index f124a2688..959a74222 100644
--- a/install.cpp
+++ b/install.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "install.h"
+
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
@@ -32,15 +34,15 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/parsedouble.h>
#include <android-base/parseint.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <cutils/properties.h>
#include <ziparchive/zip_archive.h>
#include "common.h"
#include "error_code.h"
-#include "install.h"
#include "minui/minui.h"
#include "otautil/SysUtil.h"
#include "roots.h"
@@ -55,10 +57,10 @@ static constexpr const char* METADATA_PATH = "META-INF/com/android/metadata";
static constexpr const char* UNCRYPT_STATUS = "/cache/recovery/uncrypt_status";
// Default allocation of progress bar segments to operations
-static const int VERIFICATION_PROGRESS_TIME = 60;
-static const float VERIFICATION_PROGRESS_FRACTION = 0.25;
-static const float DEFAULT_FILES_PROGRESS_FRACTION = 0.4;
-static const float DEFAULT_IMAGE_PROGRESS_FRACTION = 0.1;
+static constexpr int VERIFICATION_PROGRESS_TIME = 60;
+static constexpr float VERIFICATION_PROGRESS_FRACTION = 0.25;
+static constexpr float DEFAULT_FILES_PROGRESS_FRACTION = 0.4;
+static constexpr float DEFAULT_IMAGE_PROGRESS_FRACTION = 0.1;
// This function parses and returns the build.version.incremental
static int parse_build_number(const std::string& str) {
@@ -137,81 +139,79 @@ update_binary_command(const char* path, ZipArchiveHandle zip, int retry_count,
// Parses the metadata of the OTA package in |zip| and checks whether we are
// allowed to accept this A/B package. Downgrading is not allowed unless
// explicitly enabled in the package and only for incremental packages.
-static int check_newer_ab_build(ZipArchiveHandle zip)
-{
- std::string metadata_str;
- if (!read_metadata_from_package(zip, &metadata_str)) {
- return INSTALL_CORRUPT;
- }
- std::map<std::string, std::string> metadata;
- for (const std::string& line : android::base::Split(metadata_str, "\n")) {
- size_t eq = line.find('=');
- if (eq != std::string::npos) {
- metadata[line.substr(0, eq)] = line.substr(eq + 1);
- }
- }
- char value[PROPERTY_VALUE_MAX];
-
- property_get("ro.product.device", value, "");
- const std::string& pkg_device = metadata["pre-device"];
- if (pkg_device != value || pkg_device.empty()) {
- LOG(ERROR) << "Package is for product " << pkg_device << " but expected " << value;
- return INSTALL_ERROR;
- }
-
- // We allow the package to not have any serialno, but if it has a non-empty
- // value it should match.
- property_get("ro.serialno", value, "");
- const std::string& pkg_serial_no = metadata["serialno"];
- if (!pkg_serial_no.empty() && pkg_serial_no != value) {
- LOG(ERROR) << "Package is for serial " << pkg_serial_no;
- return INSTALL_ERROR;
- }
-
- if (metadata["ota-type"] != "AB") {
- LOG(ERROR) << "Package is not A/B";
- return INSTALL_ERROR;
- }
-
- // Incremental updates should match the current build.
- property_get("ro.build.version.incremental", value, "");
- const std::string& pkg_pre_build = metadata["pre-build-incremental"];
- if (!pkg_pre_build.empty() && pkg_pre_build != value) {
- LOG(ERROR) << "Package is for source build " << pkg_pre_build << " but expected " << value;
- return INSTALL_ERROR;
- }
- property_get("ro.build.fingerprint", value, "");
- const std::string& pkg_pre_build_fingerprint = metadata["pre-build"];
- if (!pkg_pre_build_fingerprint.empty() &&
- pkg_pre_build_fingerprint != value) {
- LOG(ERROR) << "Package is for source build " << pkg_pre_build_fingerprint
- << " but expected " << value;
- return INSTALL_ERROR;
- }
-
- // Check for downgrade version.
- int64_t build_timestamp = property_get_int64(
- "ro.build.date.utc", std::numeric_limits<int64_t>::max());
- int64_t pkg_post_timestamp = 0;
- // We allow to full update to the same version we are running, in case there
- // is a problem with the current copy of that version.
- if (metadata["post-timestamp"].empty() ||
- !android::base::ParseInt(metadata["post-timestamp"].c_str(),
- &pkg_post_timestamp) ||
- pkg_post_timestamp < build_timestamp) {
- if (metadata["ota-downgrade"] != "yes") {
- LOG(ERROR) << "Update package is older than the current build, expected a build "
- "newer than timestamp " << build_timestamp << " but package has "
- "timestamp " << pkg_post_timestamp << " and downgrade not allowed.";
- return INSTALL_ERROR;
- }
- if (pkg_pre_build_fingerprint.empty()) {
- LOG(ERROR) << "Downgrade package must have a pre-build version set, not allowed.";
- return INSTALL_ERROR;
- }
- }
-
- return 0;
+static int check_newer_ab_build(ZipArchiveHandle zip) {
+ std::string metadata_str;
+ if (!read_metadata_from_package(zip, &metadata_str)) {
+ return INSTALL_CORRUPT;
+ }
+ std::map<std::string, std::string> metadata;
+ for (const std::string& line : android::base::Split(metadata_str, "\n")) {
+ size_t eq = line.find('=');
+ if (eq != std::string::npos) {
+ metadata[line.substr(0, eq)] = line.substr(eq + 1);
+ }
+ }
+
+ std::string value = android::base::GetProperty("ro.product.device", "");
+ const std::string& pkg_device = metadata["pre-device"];
+ if (pkg_device != value || pkg_device.empty()) {
+ LOG(ERROR) << "Package is for product " << pkg_device << " but expected " << value;
+ return INSTALL_ERROR;
+ }
+
+ // We allow the package to not have any serialno, but if it has a non-empty
+ // value it should match.
+ value = android::base::GetProperty("ro.serialno", "");
+ const std::string& pkg_serial_no = metadata["serialno"];
+ if (!pkg_serial_no.empty() && pkg_serial_no != value) {
+ LOG(ERROR) << "Package is for serial " << pkg_serial_no;
+ return INSTALL_ERROR;
+ }
+
+ if (metadata["ota-type"] != "AB") {
+ LOG(ERROR) << "Package is not A/B";
+ return INSTALL_ERROR;
+ }
+
+ // Incremental updates should match the current build.
+ value = android::base::GetProperty("ro.build.version.incremental", "");
+ const std::string& pkg_pre_build = metadata["pre-build-incremental"];
+ if (!pkg_pre_build.empty() && pkg_pre_build != value) {
+ LOG(ERROR) << "Package is for source build " << pkg_pre_build << " but expected " << value;
+ return INSTALL_ERROR;
+ }
+
+ value = android::base::GetProperty("ro.build.fingerprint", "");
+ const std::string& pkg_pre_build_fingerprint = metadata["pre-build"];
+ if (!pkg_pre_build_fingerprint.empty() && pkg_pre_build_fingerprint != value) {
+ LOG(ERROR) << "Package is for source build " << pkg_pre_build_fingerprint << " but expected "
+ << value;
+ return INSTALL_ERROR;
+ }
+
+ // Check for downgrade version.
+ int64_t build_timestamp =
+ android::base::GetIntProperty("ro.build.date.utc", std::numeric_limits<int64_t>::max());
+ int64_t pkg_post_timestamp = 0;
+ // We allow to full update to the same version we are running, in case there
+ // is a problem with the current copy of that version.
+ if (metadata["post-timestamp"].empty() ||
+ !android::base::ParseInt(metadata["post-timestamp"].c_str(), &pkg_post_timestamp) ||
+ pkg_post_timestamp < build_timestamp) {
+ if (metadata["ota-downgrade"] != "yes") {
+ LOG(ERROR) << "Update package is older than the current build, expected a build "
+ "newer than timestamp "
+ << build_timestamp << " but package has timestamp " << pkg_post_timestamp
+ << " and downgrade not allowed.";
+ return INSTALL_ERROR;
+ }
+ if (pkg_pre_build_fingerprint.empty()) {
+ LOG(ERROR) << "Downgrade package must have a pre-build version set, not allowed.";
+ return INSTALL_ERROR;
+ }
+ }
+
+ return 0;
}
static int
@@ -299,158 +299,164 @@ update_binary_command(const char* path, ZipArchiveHandle zip, int retry_count,
#endif // !AB_OTA_UPDATER
// If the package contains an update binary, extract it and run it.
-static int
-try_update_binary(const char* path, ZipArchiveHandle zip, bool* wipe_cache,
- std::vector<std::string>& log_buffer, int retry_count)
-{
- read_source_target_build(zip, log_buffer);
-
- int pipefd[2];
- pipe(pipefd);
+static int try_update_binary(const char* path, ZipArchiveHandle zip, bool* wipe_cache,
+ std::vector<std::string>& log_buffer, int retry_count) {
+ read_source_target_build(zip, log_buffer);
- std::vector<std::string> args;
- int ret = update_binary_command(path, zip, retry_count, pipefd[1], &args);
- if (ret) {
- close(pipefd[0]);
- close(pipefd[1]);
- return ret;
- }
-
- // When executing the update binary contained in the package, the
- // arguments passed are:
- //
- // - the version number for this interface
- //
- // - an fd to which the program can write in order to update the
- // progress bar. The program can write single-line commands:
- //
- // progress <frac> <secs>
- // fill up the next <frac> part of of the progress bar
- // over <secs> seconds. If <secs> is zero, use
- // set_progress commands to manually control the
- // progress of this segment of the bar.
- //
- // set_progress <frac>
- // <frac> should be between 0.0 and 1.0; sets the
- // progress bar within the segment defined by the most
- // recent progress command.
- //
- // firmware <"hboot"|"radio"> <filename>
- // arrange to install the contents of <filename> in the
- // given partition on reboot.
- //
- // (API v2: <filename> may start with "PACKAGE:" to
- // indicate taking a file from the OTA package.)
- //
- // (API v3: this command no longer exists.)
- //
- // ui_print <string>
- // display <string> on the screen.
- //
- // wipe_cache
- // a wipe of cache will be performed following a successful
- // installation.
- //
- // clear_display
- // turn off the text display.
- //
- // enable_reboot
- // packages can explicitly request that they want the user
- // to be able to reboot during installation (useful for
- // debugging packages that don't exit).
- //
- // - the name of the package zip file.
- //
- // - an optional argument "retry" if this update is a retry of a failed
- // update attempt.
- //
-
- // Convert the vector to a NULL-terminated char* array suitable for execv.
- const char* chr_args[args.size() + 1];
- chr_args[args.size()] = NULL;
- for (size_t i = 0; i < args.size(); i++) {
- chr_args[i] = args[i].c_str();
- }
-
- pid_t pid = fork();
-
- if (pid == -1) {
- close(pipefd[0]);
- close(pipefd[1]);
- PLOG(ERROR) << "Failed to fork update binary";
- return INSTALL_ERROR;
- }
+ int pipefd[2];
+ pipe(pipefd);
- if (pid == 0) {
- umask(022);
- close(pipefd[0]);
- execv(chr_args[0], const_cast<char**>(chr_args));
- fprintf(stdout, "E:Can't run %s (%s)\n", chr_args[0], strerror(errno));
- _exit(-1);
- }
+ std::vector<std::string> args;
+ int ret = update_binary_command(path, zip, retry_count, pipefd[1], &args);
+ if (ret) {
+ close(pipefd[0]);
close(pipefd[1]);
-
- *wipe_cache = false;
- bool retry_update = false;
-
- char buffer[1024];
- FILE* from_child = fdopen(pipefd[0], "r");
- while (fgets(buffer, sizeof(buffer), from_child) != NULL) {
- char* command = strtok(buffer, " \n");
- if (command == NULL) {
- continue;
- } else if (strcmp(command, "progress") == 0) {
- char* fraction_s = strtok(NULL, " \n");
- char* seconds_s = strtok(NULL, " \n");
-
- float fraction = strtof(fraction_s, NULL);
- int seconds = strtol(seconds_s, NULL, 10);
-
- ui->ShowProgress(fraction * (1-VERIFICATION_PROGRESS_FRACTION), seconds);
- } else if (strcmp(command, "set_progress") == 0) {
- char* fraction_s = strtok(NULL, " \n");
- float fraction = strtof(fraction_s, NULL);
- ui->SetProgress(fraction);
- } else if (strcmp(command, "ui_print") == 0) {
- char* str = strtok(NULL, "\n");
- if (str) {
- ui->PrintOnScreenOnly("%s", str);
- } else {
- ui->PrintOnScreenOnly("\n");
- }
- fflush(stdout);
- } else if (strcmp(command, "wipe_cache") == 0) {
- *wipe_cache = true;
- } else if (strcmp(command, "clear_display") == 0) {
- ui->SetBackground(RecoveryUI::NONE);
- } else if (strcmp(command, "enable_reboot") == 0) {
- // packages can explicitly request that they want the user
- // to be able to reboot during installation (useful for
- // debugging packages that don't exit).
- ui->SetEnableReboot(true);
- } else if (strcmp(command, "retry_update") == 0) {
- retry_update = true;
- } else if (strcmp(command, "log") == 0) {
- // Save the logging request from updater and write to
- // last_install later.
- log_buffer.push_back(std::string(strtok(NULL, "\n")));
- } else {
- LOG(ERROR) << "unknown command [" << command << "]";
- }
- }
- fclose(from_child);
-
- int status;
- waitpid(pid, &status, 0);
- if (retry_update) {
- return INSTALL_RETRY;
- }
- if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- LOG(ERROR) << "Error in " << path << " (Status " << WEXITSTATUS(status) << ")";
- return INSTALL_ERROR;
- }
-
- return INSTALL_SUCCESS;
+ return ret;
+ }
+
+ // When executing the update binary contained in the package, the
+ // arguments passed are:
+ //
+ // - the version number for this interface
+ //
+ // - an FD to which the program can write in order to update the
+ // progress bar. The program can write single-line commands:
+ //
+ // progress <frac> <secs>
+ // fill up the next <frac> part of of the progress bar
+ // over <secs> seconds. If <secs> is zero, use
+ // set_progress commands to manually control the
+ // progress of this segment of the bar.
+ //
+ // set_progress <frac>
+ // <frac> should be between 0.0 and 1.0; sets the
+ // progress bar within the segment defined by the most
+ // recent progress command.
+ //
+ // ui_print <string>
+ // display <string> on the screen.
+ //
+ // wipe_cache
+ // a wipe of cache will be performed following a successful
+ // installation.
+ //
+ // clear_display
+ // turn off the text display.
+ //
+ // enable_reboot
+ // packages can explicitly request that they want the user
+ // to be able to reboot during installation (useful for
+ // debugging packages that don't exit).
+ //
+ // retry_update
+ // updater encounters some issue during the update. It requests
+ // a reboot to retry the same package automatically.
+ //
+ // log <string>
+ // updater requests logging the string (e.g. cause of the
+ // failure).
+ //
+ // - the name of the package zip file.
+ //
+ // - an optional argument "retry" if this update is a retry of a failed
+ // update attempt.
+ //
+
+ // Convert the vector to a NULL-terminated char* array suitable for execv.
+ const char* chr_args[args.size() + 1];
+ chr_args[args.size()] = nullptr;
+ for (size_t i = 0; i < args.size(); i++) {
+ chr_args[i] = args[i].c_str();
+ }
+
+ pid_t pid = fork();
+
+ if (pid == -1) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ PLOG(ERROR) << "Failed to fork update binary";
+ return INSTALL_ERROR;
+ }
+
+ if (pid == 0) {
+ umask(022);
+ close(pipefd[0]);
+ execv(chr_args[0], const_cast<char**>(chr_args));
+ PLOG(ERROR) << "Can't run " << chr_args[0];
+ _exit(-1);
+ }
+ close(pipefd[1]);
+
+ *wipe_cache = false;
+ bool retry_update = false;
+
+ char buffer[1024];
+ FILE* from_child = fdopen(pipefd[0], "r");
+ while (fgets(buffer, sizeof(buffer), from_child) != nullptr) {
+ std::string line(buffer);
+ size_t space = line.find_first_of(" \n");
+ std::string command(line.substr(0, space));
+ if (command.empty()) continue;
+
+ // Get rid of the leading and trailing space and/or newline.
+ std::string args = space == std::string::npos ? "" : android::base::Trim(line.substr(space));
+
+ if (command == "progress") {
+ std::vector<std::string> tokens = android::base::Split(args, " ");
+ double fraction;
+ int seconds;
+ if (tokens.size() == 2 && android::base::ParseDouble(tokens[0].c_str(), &fraction) &&
+ android::base::ParseInt(tokens[1], &seconds)) {
+ ui->ShowProgress(fraction * (1 - VERIFICATION_PROGRESS_FRACTION), seconds);
+ } else {
+ LOG(ERROR) << "invalid \"progress\" parameters: " << line;
+ }
+ } else if (command == "set_progress") {
+ std::vector<std::string> tokens = android::base::Split(args, " ");
+ double fraction;
+ if (tokens.size() == 1 && android::base::ParseDouble(tokens[0].c_str(), &fraction)) {
+ ui->SetProgress(fraction);
+ } else {
+ LOG(ERROR) << "invalid \"set_progress\" parameters: " << line;
+ }
+ } else if (command == "ui_print") {
+ ui->PrintOnScreenOnly("%s\n", args.c_str());
+ fflush(stdout);
+ } else if (command == "wipe_cache") {
+ *wipe_cache = true;
+ } else if (command == "clear_display") {
+ ui->SetBackground(RecoveryUI::NONE);
+ } else if (command == "enable_reboot") {
+ // packages can explicitly request that they want the user
+ // to be able to reboot during installation (useful for
+ // debugging packages that don't exit).
+ ui->SetEnableReboot(true);
+ } else if (command == "retry_update") {
+ retry_update = true;
+ } else if (command == "log") {
+ if (!args.empty()) {
+ // Save the logging request from updater and write to last_install later.
+ log_buffer.push_back(args);
+ } else {
+ LOG(ERROR) << "invalid \"log\" parameters: " << line;
+ }
+ } else {
+ LOG(ERROR) << "unknown command [" << command << "]";
+ }
+ }
+ fclose(from_child);
+
+ int status;
+ waitpid(pid, &status, 0);
+ if (retry_update) {
+ return INSTALL_RETRY;
+ }
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ LOG(ERROR) << "Error in " << path << " (Status " << WEXITSTATUS(status) << ")";
+ return INSTALL_ERROR;
+ }
+
+ return INSTALL_SUCCESS;
}
static int
@@ -542,7 +548,7 @@ install_package(const char* path, bool* wipe_cache, const char* install_file,
if (!android::base::ReadFileToString(UNCRYPT_STATUS, &uncrypt_status)) {
PLOG(WARNING) << "failed to read uncrypt status";
} else if (!android::base::StartsWith(uncrypt_status, "uncrypt_")) {
- LOG(WARNING) << "corrupted uncrypt_status: " << uncrypt_status;
+ PLOG(WARNING) << "corrupted uncrypt_status: " << uncrypt_status;
} else {
log_buffer.push_back(android::base::Trim(uncrypt_status));
}
diff --git a/minadbd/README.txt b/minadbd/README.md
index e69dc87c6..5a0a067de 100644
--- a/minadbd/README.txt
+++ b/minadbd/README.md
@@ -3,6 +3,6 @@ minadbd, and services.c has been modified as follows:
- all services removed
- all host mode support removed
- - sideload_service() added; this is the only service supported. It
+ - `sideload_service()` added; this is the only service supported. It
receives a single blob of data, writes it to a fixed filename, and
makes the process exit.
diff --git a/minui/Android.mk b/minui/Android.mk
index 67b81fc6d..281f64912 100644
--- a/minui/Android.mk
+++ b/minui/Android.mk
@@ -1,3 +1,17 @@
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
@@ -9,16 +23,18 @@ LOCAL_SRC_FILES := \
graphics_fbdev.cpp \
resources.cpp \
-LOCAL_WHOLE_STATIC_LIBRARIES += libadf
-LOCAL_WHOLE_STATIC_LIBRARIES += libdrm
-LOCAL_WHOLE_STATIC_LIBRARIES += libsync_recovery
-LOCAL_STATIC_LIBRARIES += libpng
+LOCAL_WHOLE_STATIC_LIBRARIES := \
+ libadf \
+ libdrm \
+ libsync_recovery
+
+LOCAL_STATIC_LIBRARIES := libpng
LOCAL_CFLAGS := -Werror
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
LOCAL_MODULE := libminui
-LOCAL_CLANG := true
-
# 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.
@@ -43,9 +59,10 @@ include $(BUILD_STATIC_LIBRARY)
# Used by OEMs for factory test images.
include $(CLEAR_VARS)
-LOCAL_CLANG := true
LOCAL_MODULE := libminui
LOCAL_WHOLE_STATIC_LIBRARIES += libminui
LOCAL_SHARED_LIBRARIES := libpng
LOCAL_CFLAGS := -Werror
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
include $(BUILD_SHARED_LIBRARY)
diff --git a/minui/events.cpp b/minui/events.cpp
index e6e7bd28c..6dd60fe68 100644
--- a/minui/events.cpp
+++ b/minui/events.cpp
@@ -15,17 +15,15 @@
*/
#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
+#include <linux/input.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
+#include <sys/ioctl.h>
#include <unistd.h>
-#include <linux/input.h>
-
-#include "minui.h"
+#include "minui/minui.h"
#define MAX_DEVICES 16
#define MAX_MISC_FDS 16
@@ -34,9 +32,8 @@
#define BITS_TO_LONGS(x) (((x) + BITS_PER_LONG - 1) / BITS_PER_LONG)
struct fd_info {
- int fd;
- ev_callback cb;
- void* data;
+ int fd;
+ ev_callback cb;
};
static int g_epoll_fd;
@@ -53,89 +50,87 @@ static bool test_bit(size_t bit, unsigned long* array) { // NOLINT
return (array[bit/BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))) != 0;
}
-int ev_init(ev_callback input_cb, void* data) {
- bool epollctlfail = false;
+int ev_init(ev_callback input_cb) {
+ bool epollctlfail = false;
- g_epoll_fd = epoll_create(MAX_DEVICES + MAX_MISC_FDS);
- if (g_epoll_fd == -1) {
- return -1;
+ g_epoll_fd = epoll_create(MAX_DEVICES + MAX_MISC_FDS);
+ if (g_epoll_fd == -1) {
+ return -1;
+ }
+
+ DIR* dir = opendir("/dev/input");
+ if (dir != NULL) {
+ dirent* de;
+ while ((de = readdir(dir))) {
+ // Use unsigned long to match ioctl's parameter type.
+ unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT
+
+ // fprintf(stderr,"/dev/input/%s\n", de->d_name);
+ if (strncmp(de->d_name, "event", 5)) continue;
+ int fd = openat(dirfd(dir), de->d_name, O_RDONLY);
+ if (fd == -1) continue;
+
+ // Read the evbits of the input device.
+ if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
+ close(fd);
+ continue;
+ }
+
+ // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed.
+ if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) {
+ close(fd);
+ continue;
+ }
+
+ epoll_event ev;
+ ev.events = EPOLLIN | EPOLLWAKEUP;
+ ev.data.ptr = &ev_fdinfo[ev_count];
+ if (epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
+ close(fd);
+ epollctlfail = true;
+ continue;
+ }
+
+ ev_fdinfo[ev_count].fd = fd;
+ ev_fdinfo[ev_count].cb = std::move(input_cb);
+ ev_count++;
+ ev_dev_count++;
+ if (ev_dev_count == MAX_DEVICES) break;
}
- DIR* dir = opendir("/dev/input");
- if (dir != NULL) {
- dirent* de;
- while ((de = readdir(dir))) {
- // Use unsigned long to match ioctl's parameter type.
- unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT
-
-// fprintf(stderr,"/dev/input/%s\n", de->d_name);
- if (strncmp(de->d_name, "event", 5)) continue;
- int fd = openat(dirfd(dir), de->d_name, O_RDONLY);
- if (fd == -1) continue;
-
- // Read the evbits of the input device.
- if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
- close(fd);
- continue;
- }
-
- // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed.
- if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) {
- close(fd);
- continue;
- }
-
- epoll_event ev;
- ev.events = EPOLLIN | EPOLLWAKEUP;
- ev.data.ptr = &ev_fdinfo[ev_count];
- if (epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
- close(fd);
- epollctlfail = true;
- continue;
- }
-
- ev_fdinfo[ev_count].fd = fd;
- ev_fdinfo[ev_count].cb = input_cb;
- ev_fdinfo[ev_count].data = data;
- ev_count++;
- ev_dev_count++;
- if (ev_dev_count == MAX_DEVICES) break;
- }
+ closedir(dir);
+ }
- closedir(dir);
- }
-
- if (epollctlfail && !ev_count) {
- close(g_epoll_fd);
- g_epoll_fd = -1;
- return -1;
- }
+ if (epollctlfail && !ev_count) {
+ close(g_epoll_fd);
+ g_epoll_fd = -1;
+ return -1;
+ }
- return 0;
+ return 0;
}
int ev_get_epollfd(void) {
return g_epoll_fd;
}
-int ev_add_fd(int fd, ev_callback cb, void* data) {
- if (ev_misc_count == MAX_MISC_FDS || cb == NULL) {
- return -1;
- }
-
- epoll_event ev;
- ev.events = EPOLLIN | EPOLLWAKEUP;
- ev.data.ptr = (void *)&ev_fdinfo[ev_count];
- int ret = epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev);
- if (!ret) {
- ev_fdinfo[ev_count].fd = fd;
- ev_fdinfo[ev_count].cb = cb;
- ev_fdinfo[ev_count].data = data;
- ev_count++;
- ev_misc_count++;
- }
-
- return ret;
+int ev_add_fd(int fd, ev_callback cb) {
+ if (ev_misc_count == MAX_MISC_FDS || cb == NULL) {
+ return -1;
+ }
+
+ epoll_event ev;
+ ev.events = EPOLLIN | EPOLLWAKEUP;
+ ev.data.ptr = static_cast<void*>(&ev_fdinfo[ev_count]);
+ int ret = epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev);
+ if (!ret) {
+ ev_fdinfo[ev_count].fd = fd;
+ ev_fdinfo[ev_count].cb = std::move(cb);
+ ev_count++;
+ ev_misc_count++;
+ }
+
+ return ret;
}
void ev_exit(void) {
@@ -156,13 +151,13 @@ int ev_wait(int timeout) {
}
void ev_dispatch(void) {
- for (int n = 0; n < npolledevents; n++) {
- fd_info* fdi = reinterpret_cast<fd_info*>(polledevents[n].data.ptr);
- ev_callback cb = fdi->cb;
- if (cb) {
- cb(fdi->fd, polledevents[n].events, fdi->data);
- }
+ for (int n = 0; n < npolledevents; n++) {
+ fd_info* fdi = static_cast<fd_info*>(polledevents[n].data.ptr);
+ const ev_callback& cb = fdi->cb;
+ if (cb) {
+ cb(fdi->fd, polledevents[n].events);
}
+ }
}
int ev_get_input(int fd, uint32_t epevents, input_event* ev) {
@@ -175,33 +170,33 @@ int ev_get_input(int fd, uint32_t epevents, input_event* ev) {
return -1;
}
-int ev_sync_key_state(ev_set_key_callback set_key_cb, void* data) {
- // Use unsigned long to match ioctl's parameter type.
- unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT
- unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)]; // NOLINT
+int ev_sync_key_state(const ev_set_key_callback& set_key_cb) {
+ // Use unsigned long to match ioctl's parameter type.
+ unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT
+ unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)]; // NOLINT
- for (size_t i = 0; i < ev_dev_count; ++i) {
- memset(ev_bits, 0, sizeof(ev_bits));
- memset(key_bits, 0, sizeof(key_bits));
+ for (size_t i = 0; i < ev_dev_count; ++i) {
+ memset(ev_bits, 0, sizeof(ev_bits));
+ memset(key_bits, 0, sizeof(key_bits));
- if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
- continue;
- }
- if (!test_bit(EV_KEY, ev_bits)) {
- continue;
- }
- if (ioctl(ev_fdinfo[i].fd, EVIOCGKEY(sizeof(key_bits)), key_bits) == -1) {
- continue;
- }
+ if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
+ continue;
+ }
+ if (!test_bit(EV_KEY, ev_bits)) {
+ continue;
+ }
+ if (ioctl(ev_fdinfo[i].fd, EVIOCGKEY(sizeof(key_bits)), key_bits) == -1) {
+ continue;
+ }
- for (int code = 0; code <= KEY_MAX; code++) {
- if (test_bit(code, key_bits)) {
- set_key_cb(code, 1, data);
- }
- }
+ for (int code = 0; code <= KEY_MAX; code++) {
+ if (test_bit(code, key_bits)) {
+ set_key_cb(code, 1);
+ }
}
+ }
- return 0;
+ return 0;
}
void ev_iterate_available_keys(const std::function<void(int)>& f) {
diff --git a/minui/graphics.cpp b/minui/graphics.cpp
index dcca3ec41..34ea81c7c 100644
--- a/minui/graphics.cpp
+++ b/minui/graphics.cpp
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-#include <stdbool.h>
+#include "graphics.h"
+
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -32,8 +33,7 @@
#include <time.h>
#include "font_10x18.h"
-#include "minui.h"
-#include "graphics.h"
+#include "minui/minui.h"
static GRFont* gr_font = NULL;
static minui_backend* gr_backend = NULL;
diff --git a/minui/graphics.h b/minui/graphics.h
index 52968eb10..1eaafc75a 100644
--- a/minui/graphics.h
+++ b/minui/graphics.h
@@ -17,7 +17,7 @@
#ifndef _GRAPHICS_H_
#define _GRAPHICS_H_
-#include "minui.h"
+#include "minui/minui.h"
// TODO: lose the function pointers.
struct minui_backend {
diff --git a/minui/graphics_drm.cpp b/minui/graphics_drm.cpp
index 03e33b775..199f4d83c 100644
--- a/minui/graphics_drm.cpp
+++ b/minui/graphics_drm.cpp
@@ -16,7 +16,6 @@
#include <drm_fourcc.h>
#include <fcntl.h>
-#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -28,7 +27,7 @@
#include <xf86drm.h>
#include <xf86drmMode.h>
-#include "minui.h"
+#include "minui/minui.h"
#include "graphics.h"
#define ARRAY_SIZE(A) (sizeof(A)/sizeof(*(A)))
diff --git a/minui/graphics_fbdev.cpp b/minui/graphics_fbdev.cpp
index 631ef4e13..2d70249ed 100644
--- a/minui/graphics_fbdev.cpp
+++ b/minui/graphics_fbdev.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -30,7 +29,7 @@
#include <linux/fb.h>
#include <linux/kd.h>
-#include "minui.h"
+#include "minui/minui.h"
#include "graphics.h"
static GRSurface* fbdev_init(minui_backend*);
diff --git a/minui/minui.h b/minui/include/minui/minui.h
index 78890b84b..a1749dfe6 100644
--- a/minui/minui.h
+++ b/minui/include/minui/minui.h
@@ -70,15 +70,14 @@ unsigned int gr_get_height(GRSurface* surface);
struct input_event;
-// TODO: move these over to std::function.
-typedef int (*ev_callback)(int fd, uint32_t epevents, void* data);
-typedef int (*ev_set_key_callback)(int code, int value, void* data);
+using ev_callback = std::function<int(int fd, uint32_t epevents)>;
+using ev_set_key_callback = std::function<int(int code, int value)>;
-int ev_init(ev_callback input_cb, void* data);
+int ev_init(ev_callback input_cb);
void ev_exit();
-int ev_add_fd(int fd, ev_callback cb, void* data);
+int ev_add_fd(int fd, ev_callback cb);
void ev_iterate_available_keys(const std::function<void(int)>& f);
-int ev_sync_key_state(ev_set_key_callback set_key_cb, void* data);
+int ev_sync_key_state(const ev_set_key_callback& set_key_cb);
// 'timeout' has the same semantics as poll(2).
// 0 : don't block
diff --git a/minui/resources.cpp b/minui/resources.cpp
index 9ccbf4b1b..c0f9c5c85 100644
--- a/minui/resources.cpp
+++ b/minui/resources.cpp
@@ -14,24 +14,22 @@
* limitations under the License.
*/
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
#include <fcntl.h>
+#include <linux/fb.h>
+#include <linux/kd.h>
#include <stdio.h>
-
+#include <stdlib.h>
+#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>
-
-#include <linux/fb.h>
-#include <linux/kd.h>
+#include <unistd.h>
#include <vector>
+
#include <png.h>
-#include "minui.h"
+#include "minui/minui.h"
#define SURFACE_DATA_ALIGNMENT 8
@@ -374,7 +372,9 @@ int res_create_alpha_surface(const char* name, GRSurface** pSurface) {
// This function tests if a locale string stored in PNG (prefix) matches
// the locale string provided by the system (locale).
bool matches_locale(const char* prefix, const char* locale) {
- if (locale == NULL) return false;
+ if (locale == nullptr) {
+ return false;
+ }
// Return true if the whole string of prefix matches the top part of
// locale. For instance, prefix == "en" matches locale == "en_US";
diff --git a/otafault/config.cpp b/otafault/config.cpp
index ee4ef8911..8590833ee 100644
--- a/otafault/config.cpp
+++ b/otafault/config.cpp
@@ -29,21 +29,23 @@
#define OTAIO_MAX_FNAME_SIZE 128
static ZipArchiveHandle archive;
+static bool is_retry = false;
static std::map<std::string, bool> should_inject_cache;
static std::string get_type_path(const char* io_type) {
return android::base::StringPrintf("%s/%s", OTAIO_BASE_DIR, io_type);
}
-void ota_io_init(ZipArchiveHandle za) {
+void ota_io_init(ZipArchiveHandle za, bool retry) {
archive = za;
+ is_retry = retry;
ota_set_fault_files();
}
bool should_fault_inject(const char* io_type) {
// archive will be NULL if we used an entry point other
// than updater/updater.cpp:main
- if (archive == NULL) {
+ if (archive == nullptr || is_retry) {
return false;
}
const std::string type_path = get_type_path(io_type);
diff --git a/otafault/config.h b/otafault/config.h
index c048617c2..4adbdd121 100644
--- a/otafault/config.h
+++ b/otafault/config.h
@@ -52,7 +52,7 @@
/*
* Initialize libotafault by providing a reference to the OTA package.
*/
-void ota_io_init(ZipArchiveHandle zip);
+void ota_io_init(ZipArchiveHandle zip, bool retry);
/*
* Return true if a config file is present for the given IO type.
diff --git a/recovery.cpp b/recovery.cpp
index 5f1607553..25d3546e3 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -68,6 +68,7 @@
#include "roots.h"
#include "rotate_logs.h"
#include "screen_ui.h"
+#include "stub_ui.h"
#include "ui.h"
static const struct option OPTIONS[] = {
@@ -86,6 +87,7 @@ static const struct option OPTIONS[] = {
{ "security", no_argument, NULL, 'e'},
{ "wipe_ab", no_argument, NULL, 0 },
{ "wipe_package_size", required_argument, NULL, 0 },
+ { "prompt_and_wipe_data", no_argument, NULL, 0 },
{ NULL, 0, NULL, 0 },
};
@@ -137,6 +139,8 @@ struct selabel_handle* sehandle;
* The arguments which may be supplied in the recovery.command file:
* --update_package=path - verify install an OTA package file
* --wipe_data - erase user data (and cache), then reboot
+ * --prompt_and_wipe_data - prompt the user that data is corrupt,
+ * with their consent erase user data (and cache), then reboot
* --wipe_cache - wipe cache (but not user data), then reboot
* --set_encrypted_filesystem=on|off - enables / diasables encrypted fs
* --just_exit - do nothing; exit and reboot
@@ -169,21 +173,7 @@ struct selabel_handle* sehandle;
* -- after this, rebooting will (try to) restart the main system --
* 7. ** if install failed **
* 7a. prompt_and_wait() shows an error icon and waits for the user
- * 7b; the user reboots (pulling the battery, etc) into the main system
- * 8. main() calls maybe_install_firmware_update()
- * ** if the update contained radio/hboot firmware **:
- * 8a. m_i_f_u() writes BCB with "boot-recovery" and "--wipe_cache"
- * -- after this, rebooting will reformat cache & restart main system --
- * 8b. m_i_f_u() writes firmware image into raw cache partition
- * 8c. m_i_f_u() writes BCB with "update-radio/hboot" and "--wipe_cache"
- * -- after this, rebooting will attempt to reinstall firmware --
- * 8d. bootloader tries to flash firmware
- * 8e. bootloader writes BCB with "boot-recovery" (keeping "--wipe_cache")
- * -- after this, rebooting will reformat cache & restart main system --
- * 8f. erase_volume() reformats /cache
- * 8g. finish_recovery() erases BCB
- * -- after this, rebooting will (try to) restart the main system --
- * 9. main() calls reboot() to boot main system
+ * 7b. the user reboots (pulling the battery, etc) into the main system
*/
// open a given path, mounting partitions as necessary
@@ -332,7 +322,7 @@ static std::vector<std::string> get_args(const int argc, char** const argv) {
std::vector<std::string> args(argv, argv + argc);
// --- if arguments weren't supplied, look in the bootloader control block
- if (argc == 1) {
+ if (args.size() == 1) {
boot.recovery[sizeof(boot.recovery) - 1] = '\0'; // Ensure termination
std::string boot_recovery(boot.recovery);
std::vector<std::string> tokens = android::base::Split(boot_recovery, "\n");
@@ -348,11 +338,14 @@ static std::vector<std::string> get_args(const int argc, char** const argv) {
}
// --- if that doesn't work, try the command file (if we have /cache).
- if (argc == 1 && has_cache) {
+ if (args.size() == 1 && has_cache) {
std::string content;
- if (android::base::ReadFileToString(COMMAND_FILE, &content)) {
+ if (ensure_path_mounted(COMMAND_FILE) == 0 &&
+ android::base::ReadFileToString(COMMAND_FILE, &content)) {
std::vector<std::string> tokens = android::base::Split(content, "\n");
- for (auto it = tokens.begin() + 1; it != tokens.end(); it++) {
+ // All the arguments in COMMAND_FILE are needed (unlike the BCB message,
+ // COMMAND_FILE doesn't use filename as the first argument).
+ for (auto it = tokens.begin(); it != tokens.end(); it++) {
// Skip empty and '\0'-filled tokens.
if (!it->empty() && (*it)[0] != '\0') args.push_back(std::move(*it));
}
@@ -364,20 +357,21 @@ static std::vector<std::string> get_args(const int argc, char** const argv) {
// bootloader control block. So the device will always boot into recovery to
// finish the pending work, until finish_recovery() is called.
std::vector<std::string> options(args.cbegin() + 1, args.cend());
- if (!write_bootloader_message(options, &err)) {
- LOG(ERROR) << err;
+ if (!update_bootloader_message(options, &err)) {
+ LOG(ERROR) << "Failed to set BCB message: " << err;
}
return args;
}
-static void
-set_sdcard_update_bootloader_message() {
- std::vector<std::string> options;
- std::string err;
- if (!write_bootloader_message(options, &err)) {
- LOG(ERROR) << err;
- }
+// Set the BCB to reboot back into recovery (it won't resume the install from
+// sdcard though).
+static void set_sdcard_update_bootloader_message() {
+ std::vector<std::string> options;
+ std::string err;
+ if (!update_bootloader_message(options, &err)) {
+ LOG(ERROR) << "Failed to set BCB message: " << err;
+ }
}
// Read from kernel log into buffer and write out to file.
@@ -495,7 +489,7 @@ static void finish_recovery() {
// Reset to normal system boot so recovery won't cycle indefinitely.
std::string err;
if (!clear_bootloader_message(&err)) {
- LOG(ERROR) << err;
+ LOG(ERROR) << "Failed to clear BCB message: " << err;
}
// Remove the command file, so recovery won't repeat indefinitely.
@@ -509,117 +503,107 @@ static void finish_recovery() {
sync(); // For good measure.
}
-typedef struct _saved_log_file {
- char* name;
- struct stat st;
- unsigned char* data;
- struct _saved_log_file* next;
-} saved_log_file;
+struct saved_log_file {
+ std::string name;
+ struct stat sb;
+ std::string data;
+};
static bool erase_volume(const char* volume) {
- bool is_cache = (strcmp(volume, CACHE_ROOT) == 0);
- bool is_data = (strcmp(volume, DATA_ROOT) == 0);
+ bool is_cache = (strcmp(volume, CACHE_ROOT) == 0);
+ bool is_data = (strcmp(volume, DATA_ROOT) == 0);
- ui->SetBackground(RecoveryUI::ERASING);
- ui->SetProgressType(RecoveryUI::INDETERMINATE);
+ ui->SetBackground(RecoveryUI::ERASING);
+ ui->SetProgressType(RecoveryUI::INDETERMINATE);
- saved_log_file* head = NULL;
-
- if (is_cache) {
- // If we're reformatting /cache, we load any past logs
- // (i.e. "/cache/recovery/last_*") and the current log
- // ("/cache/recovery/log") into memory, so we can restore them after
- // the reformat.
-
- ensure_path_mounted(volume);
-
- DIR* d;
- struct dirent* de;
- d = opendir(CACHE_LOG_DIR);
- if (d) {
- char path[PATH_MAX];
- strcpy(path, CACHE_LOG_DIR);
- strcat(path, "/");
- int path_len = strlen(path);
- while ((de = readdir(d)) != NULL) {
- if (strncmp(de->d_name, "last_", 5) == 0 || strcmp(de->d_name, "log") == 0) {
- saved_log_file* p = (saved_log_file*) malloc(sizeof(saved_log_file));
- strcpy(path+path_len, de->d_name);
- p->name = strdup(path);
- if (stat(path, &(p->st)) == 0) {
- // truncate files to 512kb
- if (p->st.st_size > (1 << 19)) {
- p->st.st_size = 1 << 19;
- }
- p->data = (unsigned char*) malloc(p->st.st_size);
- FILE* f = fopen(path, "rb");
- fread(p->data, 1, p->st.st_size, f);
- fclose(f);
- p->next = head;
- head = p;
- } else {
- free(p);
- }
- }
- }
- closedir(d);
- } else {
- if (errno != ENOENT) {
- printf("opendir failed: %s\n", strerror(errno));
+ std::vector<saved_log_file> log_files;
+
+ if (is_cache) {
+ // If we're reformatting /cache, we load any past logs
+ // (i.e. "/cache/recovery/last_*") and the current log
+ // ("/cache/recovery/log") into memory, so we can restore them after
+ // the reformat.
+
+ ensure_path_mounted(volume);
+
+ struct dirent* de;
+ std::unique_ptr<DIR, decltype(&closedir)> d(opendir(CACHE_LOG_DIR), closedir);
+ if (d) {
+ while ((de = readdir(d.get())) != nullptr) {
+ if (strncmp(de->d_name, "last_", 5) == 0 || strcmp(de->d_name, "log") == 0) {
+ std::string path = android::base::StringPrintf("%s/%s", CACHE_LOG_DIR, de->d_name);
+
+ struct stat sb;
+ if (stat(path.c_str(), &sb) == 0) {
+ // truncate files to 512kb
+ if (sb.st_size > (1 << 19)) {
+ sb.st_size = 1 << 19;
}
+
+ std::string data(sb.st_size, '\0');
+ FILE* f = fopen(path.c_str(), "rb");
+ fread(&data[0], 1, data.size(), f);
+ fclose(f);
+
+ log_files.emplace_back(saved_log_file{ path, sb, data });
+ }
}
+ }
+ } else {
+ if (errno != ENOENT) {
+ PLOG(ERROR) << "Failed to opendir " << CACHE_LOG_DIR;
+ }
}
+ }
- ui->Print("Formatting %s...\n", volume);
+ ui->Print("Formatting %s...\n", volume);
- ensure_path_unmounted(volume);
+ ensure_path_unmounted(volume);
- int result;
+ int result;
- if (is_data && reason && strcmp(reason, "convert_fbe") == 0) {
- // Create convert_fbe breadcrumb file to signal to init
- // to convert to file based encryption, not full disk encryption
- if (mkdir(CONVERT_FBE_DIR, 0700) != 0) {
- ui->Print("Failed to make convert_fbe dir %s\n", strerror(errno));
- return true;
- }
- FILE* f = fopen(CONVERT_FBE_FILE, "wb");
- if (!f) {
- ui->Print("Failed to convert to file encryption %s\n", strerror(errno));
- return true;
- }
- fclose(f);
- result = format_volume(volume, CONVERT_FBE_DIR);
- remove(CONVERT_FBE_FILE);
- rmdir(CONVERT_FBE_DIR);
- } else {
- result = format_volume(volume);
+ if (is_data && reason && strcmp(reason, "convert_fbe") == 0) {
+ // Create convert_fbe breadcrumb file to signal to init
+ // to convert to file based encryption, not full disk encryption
+ if (mkdir(CONVERT_FBE_DIR, 0700) != 0) {
+ ui->Print("Failed to make convert_fbe dir %s\n", strerror(errno));
+ return true;
}
+ FILE* f = fopen(CONVERT_FBE_FILE, "wb");
+ if (!f) {
+ ui->Print("Failed to convert to file encryption %s\n", strerror(errno));
+ return true;
+ }
+ fclose(f);
+ result = format_volume(volume, CONVERT_FBE_DIR);
+ remove(CONVERT_FBE_FILE);
+ rmdir(CONVERT_FBE_DIR);
+ } else {
+ result = format_volume(volume);
+ }
- if (is_cache) {
- while (head) {
- FILE* f = fopen_path(head->name, "wb");
- if (f) {
- fwrite(head->data, 1, head->st.st_size, f);
- fclose(f);
- chmod(head->name, head->st.st_mode);
- chown(head->name, head->st.st_uid, head->st.st_gid);
- }
- free(head->name);
- free(head->data);
- saved_log_file* temp = head->next;
- free(head);
- head = temp;
+ if (is_cache) {
+ // Re-create the log dir and write back the log entries.
+ if (ensure_path_mounted(CACHE_LOG_DIR) == 0 &&
+ dirCreateHierarchy(CACHE_LOG_DIR, 0777, nullptr, false, sehandle) == 0) {
+ for (const auto& log : log_files) {
+ if (!android::base::WriteStringToFile(log.data, log.name, log.sb.st_mode, log.sb.st_uid,
+ log.sb.st_gid)) {
+ PLOG(ERROR) << "Failed to write to " << log.name;
}
-
- // Any part of the log we'd copied to cache is now gone.
- // Reset the pointer so we copy from the beginning of the temp
- // log.
- tmplog_offset = 0;
- copy_logs();
+ }
+ } else {
+ PLOG(ERROR) << "Failed to mount / create " << CACHE_LOG_DIR;
}
- return (result == 0);
+ // Any part of the log we'd copied to cache is now gone.
+ // Reset the pointer so we copy from the beginning of the temp
+ // log.
+ tmplog_offset = 0;
+ copy_logs();
+ }
+
+ return (result == 0);
}
static int
@@ -784,12 +768,12 @@ static bool yes_no(Device* device, const char* question1, const char* question2)
return (chosen_item == 1);
}
-// Return true on success.
-static bool wipe_data(int should_confirm, Device* device) {
- if (should_confirm && !yes_no(device, "Wipe all user data?", " THIS CAN NOT BE UNDONE!")) {
- return false;
- }
+static bool ask_to_wipe_data(Device* device) {
+ return yes_no(device, "Wipe all user data?", " THIS CAN NOT BE UNDONE!");
+}
+// Return true on success.
+static bool wipe_data(Device* device) {
modified_flash = true;
ui->Print("\n-- Wiping data...\n");
@@ -802,6 +786,28 @@ static bool wipe_data(int should_confirm, Device* device) {
return success;
}
+static bool prompt_and_wipe_data(Device* device) {
+ const char* const headers[] = {
+ "Boot halted, user data is corrupt",
+ "Wipe all user data to recover",
+ NULL
+ };
+ const char* const items[] = {
+ "Retry boot",
+ "Wipe user data",
+ NULL
+ };
+ for (;;) {
+ int chosen_item = get_menu_selection(headers, items, 1, 0, device);
+ if (chosen_item != 1) {
+ return true; // Just reboot, no wipe; not a failure, user asked for it
+ }
+ if (ask_to_wipe_data(device)) {
+ return wipe_data(device);
+ }
+ }
+}
+
// Return true on success.
static bool wipe_cache(bool should_confirm, Device* device) {
if (!has_cache) {
@@ -886,8 +892,8 @@ static bool check_wipe_package(size_t wipe_package_size) {
// Extract metadata
ZipArchiveHandle zip;
- int err = OpenArchiveFromMemory(reinterpret_cast<void*>(&wipe_package[0]),
- wipe_package.size(), "wipe_package", &zip);
+ int err = OpenArchiveFromMemory(static_cast<void*>(&wipe_package[0]), wipe_package.size(),
+ "wipe_package", &zip);
if (err != 0) {
LOG(ERROR) << "Can't open wipe package : " << ErrorCodeString(err);
return false;
@@ -910,13 +916,11 @@ static bool check_wipe_package(size_t wipe_package_size) {
ota_type_matched = true;
} else if (android::base::StartsWith(line, "pre-device=")) {
std::string device_type = line.substr(strlen("pre-device="));
- char real_device_type[PROPERTY_VALUE_MAX];
- property_get("ro.build.product", real_device_type, "");
+ std::string real_device_type = android::base::GetProperty("ro.build.product", "");
device_type_matched = (device_type == real_device_type);
} else if (android::base::StartsWith(line, "serialno=")) {
std::string serial_no = line.substr(strlen("serialno="));
- char real_serial_no[PROPERTY_VALUE_MAX];
- property_get("ro.serialno", real_serial_no, "");
+ std::string real_serial_no = android::base::GetProperty("ro.serialno", "");
has_serial_number = true;
serial_number_matched = (serial_no == real_serial_no);
}
@@ -1166,8 +1170,14 @@ prompt_and_wait(Device* device, int status) {
return chosen_action;
case Device::WIPE_DATA:
- wipe_data(ui->IsTextVisible(), device);
- if (!ui->IsTextVisible()) return Device::NO_ACTION;
+ if (ui->IsTextVisible()) {
+ if (ask_to_wipe_data(device)) {
+ wipe_data(device);
+ }
+ } else {
+ wipe_data(device);
+ return Device::NO_ACTION;
+ }
break;
case Device::WIPE_CACHE:
@@ -1343,21 +1353,21 @@ static void set_retry_bootloader_message(int retry_count, int argc, char** argv)
// Increment the retry counter by 1.
options.push_back(android::base::StringPrintf("--retry_count=%d", retry_count+1));
std::string err;
- if (!write_bootloader_message(options, &err)) {
+ if (!update_bootloader_message(options, &err)) {
LOG(ERROR) << err;
}
}
static bool bootreason_in_blacklist() {
- char bootreason[PROPERTY_VALUE_MAX];
- if (property_get("ro.boot.bootreason", bootreason, nullptr) > 0) {
- for (const auto& str : bootreason_blacklist) {
- if (strcasecmp(str.c_str(), bootreason) == 0) {
- return true;
- }
- }
+ std::string bootreason = android::base::GetProperty("ro.boot.bootreason", "");
+ if (!bootreason.empty()) {
+ for (const auto& str : bootreason_blacklist) {
+ if (strcasecmp(str.c_str(), bootreason.c_str()) == 0) {
+ return true;
+ }
}
- return false;
+ }
+ return false;
}
static void log_failure_code(ErrorCode code, const char *update_package) {
@@ -1423,6 +1433,7 @@ int main(int argc, char **argv) {
const char *update_package = NULL;
bool should_wipe_data = false;
+ bool should_prompt_and_wipe_data = false;
bool should_wipe_cache = false;
bool should_wipe_ab = false;
size_t wipe_package_size = 0;
@@ -1460,12 +1471,13 @@ int main(int argc, char **argv) {
case 'r': reason = optarg; break;
case 'e': security_update = true; break;
case 0: {
- if (strcmp(OPTIONS[option_index].name, "wipe_ab") == 0) {
+ std::string option = OPTIONS[option_index].name;
+ if (option == "wipe_ab") {
should_wipe_ab = true;
- break;
- } else if (strcmp(OPTIONS[option_index].name, "wipe_package_size") == 0) {
+ } else if (option == "wipe_package_size") {
android::base::ParseUint(optarg, &wipe_package_size);
- break;
+ } else if (option == "prompt_and_wipe_data") {
+ should_prompt_and_wipe_data = true;
}
break;
}
@@ -1492,8 +1504,10 @@ int main(int argc, char **argv) {
Device* device = make_device();
ui = device->GetUI();
- ui->SetLocale(locale.c_str());
- ui->Init();
+ if (!ui->Init(locale)) {
+ printf("Failed to initialize UI, use stub UI instead.");
+ ui = new StubRecoveryUI();
+ }
// Set background string to "installing security update" for security update,
// otherwise set it to "installing system update".
ui->SetSystemUpdateText(security_update);
@@ -1522,27 +1536,7 @@ int main(int argc, char **argv) {
for (const auto& arg : args) {
printf(" \"%s\"", arg.c_str());
}
- printf("\n");
-
- if (update_package) {
- // For backwards compatibility on the cache partition only, if
- // we're given an old 'root' path "CACHE:foo", change it to
- // "/cache/foo".
- if (strncmp(update_package, "CACHE:", 6) == 0) {
- int len = strlen(update_package) + 10;
- char* modified_path = (char*)malloc(len);
- if (modified_path) {
- strlcpy(modified_path, "/cache/", len);
- strlcat(modified_path, update_package+6, len);
- printf("(replacing path \"%s\" with \"%s\")\n",
- update_package, modified_path);
- update_package = modified_path;
- }
- else
- printf("modified_path allocation failed\n");
- }
- }
- printf("\n");
+ printf("\n\n");
property_list(print_property, NULL);
printf("\n");
@@ -1602,9 +1596,16 @@ int main(int argc, char **argv) {
}
}
} else if (should_wipe_data) {
- if (!wipe_data(false, device)) {
+ if (!wipe_data(device)) {
+ status = INSTALL_ERROR;
+ }
+ } else if (should_prompt_and_wipe_data) {
+ ui->ShowText(true);
+ ui->SetBackground(RecoveryUI::ERROR);
+ if (!prompt_and_wipe_data(device)) {
status = INSTALL_ERROR;
}
+ ui->ShowText(false);
} else if (should_wipe_cache) {
if (!wipe_cache(false, device)) {
status = INSTALL_ERROR;
diff --git a/res-hdpi/images/erasing_text.png b/res-hdpi/images/erasing_text.png
index 2186c1950..684fc7c6c 100644
--- a/res-hdpi/images/erasing_text.png
+++ b/res-hdpi/images/erasing_text.png
Binary files differ
diff --git a/res-hdpi/images/error_text.png b/res-hdpi/images/error_text.png
index 9700f459f..00c485d70 100644
--- a/res-hdpi/images/error_text.png
+++ b/res-hdpi/images/error_text.png
Binary files differ
diff --git a/res-hdpi/images/installing_security_text.png b/res-hdpi/images/installing_security_text.png
index 0f605952c..dadcfc32b 100644
--- a/res-hdpi/images/installing_security_text.png
+++ b/res-hdpi/images/installing_security_text.png
Binary files differ
diff --git a/res-hdpi/images/installing_text.png b/res-hdpi/images/installing_text.png
index ec8b875a5..abe73b4b9 100644
--- a/res-hdpi/images/installing_text.png
+++ b/res-hdpi/images/installing_text.png
Binary files differ
diff --git a/res-hdpi/images/no_command_text.png b/res-hdpi/images/no_command_text.png
index 3eddcbbed..958e10613 100644
--- a/res-hdpi/images/no_command_text.png
+++ b/res-hdpi/images/no_command_text.png
Binary files differ
diff --git a/res-mdpi/images/erasing_text.png b/res-mdpi/images/erasing_text.png
index b0dd3c6d3..10e317829 100644
--- a/res-mdpi/images/erasing_text.png
+++ b/res-mdpi/images/erasing_text.png
Binary files differ
diff --git a/res-mdpi/images/error_text.png b/res-mdpi/images/error_text.png
index 6a47a5956..0022d10c1 100644
--- a/res-mdpi/images/error_text.png
+++ b/res-mdpi/images/error_text.png
Binary files differ
diff --git a/res-mdpi/images/installing_security_text.png b/res-mdpi/images/installing_security_text.png
index 149939862..7a4cd414b 100644
--- a/res-mdpi/images/installing_security_text.png
+++ b/res-mdpi/images/installing_security_text.png
Binary files differ
diff --git a/res-mdpi/images/installing_text.png b/res-mdpi/images/installing_text.png
index 01e9bfefb..ee95e569c 100644
--- a/res-mdpi/images/installing_text.png
+++ b/res-mdpi/images/installing_text.png
Binary files differ
diff --git a/res-mdpi/images/no_command_text.png b/res-mdpi/images/no_command_text.png
index d340df518..af7660908 100644
--- a/res-mdpi/images/no_command_text.png
+++ b/res-mdpi/images/no_command_text.png
Binary files differ
diff --git a/res-xhdpi/images/erasing_text.png b/res-xhdpi/images/erasing_text.png
index 2f8b46918..91cc35871 100644
--- a/res-xhdpi/images/erasing_text.png
+++ b/res-xhdpi/images/erasing_text.png
Binary files differ
diff --git a/res-xhdpi/images/error_text.png b/res-xhdpi/images/error_text.png
index ad18851c5..772b139e6 100644
--- a/res-xhdpi/images/error_text.png
+++ b/res-xhdpi/images/error_text.png
Binary files differ
diff --git a/res-xhdpi/images/installing_security_text.png b/res-xhdpi/images/installing_security_text.png
index acc6a7c2b..a7113a04a 100644
--- a/res-xhdpi/images/installing_security_text.png
+++ b/res-xhdpi/images/installing_security_text.png
Binary files differ
diff --git a/res-xhdpi/images/installing_text.png b/res-xhdpi/images/installing_text.png
index 32897d0f0..566eb0658 100644
--- a/res-xhdpi/images/installing_text.png
+++ b/res-xhdpi/images/installing_text.png
Binary files differ
diff --git a/res-xhdpi/images/no_command_text.png b/res-xhdpi/images/no_command_text.png
index eb43c59c5..b8da125cb 100644
--- a/res-xhdpi/images/no_command_text.png
+++ b/res-xhdpi/images/no_command_text.png
Binary files differ
diff --git a/res-xxhdpi/images/erasing_text.png b/res-xxhdpi/images/erasing_text.png
index 8ff2b2fcf..86693f435 100644
--- a/res-xxhdpi/images/erasing_text.png
+++ b/res-xxhdpi/images/erasing_text.png
Binary files differ
diff --git a/res-xxhdpi/images/error_text.png b/res-xxhdpi/images/error_text.png
index 658d4ea56..9c4bcab95 100644
--- a/res-xxhdpi/images/error_text.png
+++ b/res-xxhdpi/images/error_text.png
Binary files differ
diff --git a/res-xxhdpi/images/installing_security_text.png b/res-xxhdpi/images/installing_security_text.png
index 23fcaa441..f5ec698f8 100644
--- a/res-xxhdpi/images/installing_security_text.png
+++ b/res-xxhdpi/images/installing_security_text.png
Binary files differ
diff --git a/res-xxhdpi/images/installing_text.png b/res-xxhdpi/images/installing_text.png
index fd8e58464..100a5b303 100644
--- a/res-xxhdpi/images/installing_text.png
+++ b/res-xxhdpi/images/installing_text.png
Binary files differ
diff --git a/res-xxhdpi/images/no_command_text.png b/res-xxhdpi/images/no_command_text.png
index 23932d6b7..590030c8b 100644
--- a/res-xxhdpi/images/no_command_text.png
+++ b/res-xxhdpi/images/no_command_text.png
Binary files differ
diff --git a/res-xxxhdpi/images/erasing_text.png b/res-xxxhdpi/images/erasing_text.png
index 031529371..4cf5d76e8 100644
--- a/res-xxxhdpi/images/erasing_text.png
+++ b/res-xxxhdpi/images/erasing_text.png
Binary files differ
diff --git a/res-xxxhdpi/images/error_text.png b/res-xxxhdpi/images/error_text.png
index dba127f02..8dd6f1236 100644
--- a/res-xxxhdpi/images/error_text.png
+++ b/res-xxxhdpi/images/error_text.png
Binary files differ
diff --git a/res-xxxhdpi/images/installing_security_text.png b/res-xxxhdpi/images/installing_security_text.png
index 6cdbef48e..fa06f3147 100644
--- a/res-xxxhdpi/images/installing_security_text.png
+++ b/res-xxxhdpi/images/installing_security_text.png
Binary files differ
diff --git a/res-xxxhdpi/images/installing_text.png b/res-xxxhdpi/images/installing_text.png
index 32511a9c5..d0f930160 100644
--- a/res-xxxhdpi/images/installing_text.png
+++ b/res-xxxhdpi/images/installing_text.png
Binary files differ
diff --git a/res-xxxhdpi/images/no_command_text.png b/res-xxxhdpi/images/no_command_text.png
index b6cdd7718..233aec468 100644
--- a/res-xxxhdpi/images/no_command_text.png
+++ b/res-xxxhdpi/images/no_command_text.png
Binary files differ
diff --git a/roots.cpp b/roots.cpp
index 14018fcdc..376fcbd1b 100644
--- a/roots.cpp
+++ b/roots.cpp
@@ -215,7 +215,12 @@ int format_volume(const char* volume, const char* directory) {
}
int result;
if (strcmp(v->fs_type, "ext4") == 0) {
- result = make_ext4fs_directory(v->blk_device, length, volume, sehandle, directory);
+ if (v->erase_blk_size != 0 && v->logical_blk_size != 0) {
+ result = make_ext4fs_directory_align(v->blk_device, length, volume, sehandle,
+ directory, v->erase_blk_size, v->logical_blk_size);
+ } else {
+ result = make_ext4fs_directory(v->blk_device, length, volume, sehandle, directory);
+ }
} else { /* Has to be f2fs because we checked earlier. */
if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0 && length < 0) {
LOG(ERROR) << "format_volume: crypt footer + negative length (" << length
diff --git a/screen_ui.cpp b/screen_ui.cpp
index fab348964..706877b4d 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -29,6 +29,7 @@
#include <time.h>
#include <unistd.h>
+#include <string>
#include <vector>
#include <android-base/logging.h>
@@ -51,37 +52,34 @@ static double now() {
return tv.tv_sec + tv.tv_usec / 1000000.0;
}
-ScreenRecoveryUI::ScreenRecoveryUI() :
- currentIcon(NONE),
- locale(nullptr),
- intro_done(false),
- current_frame(0),
- progressBarType(EMPTY),
- progressScopeStart(0),
- progressScopeSize(0),
- progress(0),
- pagesIdentical(false),
- text_cols_(0),
- text_rows_(0),
- text_(nullptr),
- text_col_(0),
- text_row_(0),
- text_top_(0),
- show_text(false),
- show_text_ever(false),
- menu_(nullptr),
- show_menu(false),
- menu_items(0),
- menu_sel(0),
- file_viewer_text_(nullptr),
- intro_frames(0),
- loop_frames(0),
- animation_fps(30), // TODO: there's currently no way to infer this.
- stage(-1),
- max_stage(-1),
- updateMutex(PTHREAD_MUTEX_INITIALIZER),
- rtl_locale(false) {
-}
+ScreenRecoveryUI::ScreenRecoveryUI()
+ : currentIcon(NONE),
+ progressBarType(EMPTY),
+ progressScopeStart(0),
+ progressScopeSize(0),
+ progress(0),
+ pagesIdentical(false),
+ text_cols_(0),
+ text_rows_(0),
+ text_(nullptr),
+ text_col_(0),
+ text_row_(0),
+ text_top_(0),
+ show_text(false),
+ show_text_ever(false),
+ menu_(nullptr),
+ show_menu(false),
+ menu_items(0),
+ menu_sel(0),
+ file_viewer_text_(nullptr),
+ intro_frames(0),
+ loop_frames(0),
+ current_frame(0),
+ intro_done(false),
+ animation_fps(30), // TODO: there's currently no way to infer this.
+ stage(-1),
+ max_stage(-1),
+ updateMutex(PTHREAD_MUTEX_INITIALIZER) {}
GRSurface* ScreenRecoveryUI::GetCurrentFrame() {
if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) {
@@ -106,29 +104,41 @@ int ScreenRecoveryUI::PixelsFromDp(int dp) {
// Here's the intended layout:
-// | regular large
-// ---------+--------------------
-// | 220dp 366dp
-// icon | (200dp) (200dp)
-// | 68dp 68dp
-// text | (14sp) (14sp)
-// | 32dp 32dp
-// progress | (2dp) (2dp)
-// | 194dp 340dp
+// | portrait large landscape large
+// ---------+-------------------------------------------------
+// gap | 220dp 366dp 142dp 284dp
+// icon | (200dp)
+// gap | 68dp 68dp 56dp 112dp
+// text | (14sp)
+// gap | 32dp 32dp 26dp 52dp
+// progress | (2dp)
+// gap | 194dp 340dp 131dp 262dp
// Note that "baseline" is actually the *top* of each icon (because that's how our drawing
// routines work), so that's the more useful measurement for calling code.
+enum Layout { PORTRAIT = 0, PORTRAIT_LARGE = 1, LANDSCAPE = 2, LANDSCAPE_LARGE = 3, LAYOUT_MAX };
+enum Dimension { PROGRESS = 0, TEXT = 1, ICON = 2, DIMENSION_MAX };
+static constexpr int kLayouts[LAYOUT_MAX][DIMENSION_MAX] = {
+ { 194, 32, 68, }, // PORTRAIT
+ { 340, 32, 68, }, // PORTRAIT_LARGE
+ { 131, 26, 56, }, // LANDSCAPE
+ { 262, 52, 112, }, // LANDSCAPE_LARGE
+};
+
int ScreenRecoveryUI::GetAnimationBaseline() {
- return GetTextBaseline() - PixelsFromDp(68) - gr_get_height(loopFrames[0]);
+ return GetTextBaseline() - PixelsFromDp(kLayouts[layout_][ICON]) -
+ gr_get_height(loopFrames[0]);
}
int ScreenRecoveryUI::GetTextBaseline() {
- return GetProgressBaseline() - PixelsFromDp(32) - gr_get_height(installing_text);
+ return GetProgressBaseline() - PixelsFromDp(kLayouts[layout_][TEXT]) -
+ gr_get_height(installing_text);
}
int ScreenRecoveryUI::GetProgressBaseline() {
- return gr_fb_height() - PixelsFromDp(is_large_ ? 340 : 194) - gr_get_height(progressBarFill);
+ return gr_fb_height() - PixelsFromDp(kLayouts[layout_][PROGRESS]) -
+ gr_get_height(progressBarFill);
}
// Clear the screen and draw the currently selected background icon (if any).
@@ -163,51 +173,50 @@ void ScreenRecoveryUI::draw_background_locked() {
// Does not flip pages.
// Should only be called with updateMutex locked.
void ScreenRecoveryUI::draw_foreground_locked() {
- if (currentIcon != NONE) {
- GRSurface* frame = GetCurrentFrame();
- int frame_width = gr_get_width(frame);
- int frame_height = gr_get_height(frame);
- int frame_x = (gr_fb_width() - frame_width) / 2;
- int frame_y = GetAnimationBaseline();
- gr_blit(frame, 0, 0, frame_width, frame_height, frame_x, frame_y);
- }
-
- if (progressBarType != EMPTY) {
- int width = gr_get_width(progressBarEmpty);
- int height = gr_get_height(progressBarEmpty);
-
- int progress_x = (gr_fb_width() - width)/2;
- int progress_y = GetProgressBaseline();
-
- // Erase behind the progress bar (in case this was a progress-only update)
- gr_color(0, 0, 0, 255);
- gr_fill(progress_x, progress_y, width, height);
+ if (currentIcon != NONE) {
+ GRSurface* frame = GetCurrentFrame();
+ int frame_width = gr_get_width(frame);
+ int frame_height = gr_get_height(frame);
+ int frame_x = (gr_fb_width() - frame_width) / 2;
+ int frame_y = GetAnimationBaseline();
+ gr_blit(frame, 0, 0, frame_width, frame_height, frame_x, frame_y);
+ }
+
+ if (progressBarType != EMPTY) {
+ int width = gr_get_width(progressBarEmpty);
+ int height = gr_get_height(progressBarEmpty);
+
+ int progress_x = (gr_fb_width() - width) / 2;
+ int progress_y = GetProgressBaseline();
+
+ // Erase behind the progress bar (in case this was a progress-only update)
+ gr_color(0, 0, 0, 255);
+ gr_fill(progress_x, progress_y, width, height);
- if (progressBarType == DETERMINATE) {
- float p = progressScopeStart + progress * progressScopeSize;
- int pos = (int) (p * width);
+ if (progressBarType == DETERMINATE) {
+ float p = progressScopeStart + progress * progressScopeSize;
+ int pos = static_cast<int>(p * width);
- if (rtl_locale) {
- // Fill the progress bar from right to left.
- if (pos > 0) {
- gr_blit(progressBarFill, width-pos, 0, pos, height,
- progress_x+width-pos, progress_y);
- }
- if (pos < width-1) {
- gr_blit(progressBarEmpty, 0, 0, width-pos, height, progress_x, progress_y);
- }
- } else {
- // Fill the progress bar from left to right.
- if (pos > 0) {
- gr_blit(progressBarFill, 0, 0, pos, height, progress_x, progress_y);
- }
- if (pos < width-1) {
- gr_blit(progressBarEmpty, pos, 0, width-pos, height,
- progress_x+pos, progress_y);
- }
- }
+ if (rtl_locale_) {
+ // Fill the progress bar from right to left.
+ if (pos > 0) {
+ gr_blit(progressBarFill, width - pos, 0, pos, height, progress_x + width - pos,
+ progress_y);
+ }
+ if (pos < width - 1) {
+ gr_blit(progressBarEmpty, 0, 0, width - pos, height, progress_x, progress_y);
+ }
+ } else {
+ // Fill the progress bar from left to right.
+ if (pos > 0) {
+ gr_blit(progressBarFill, 0, 0, pos, height, progress_x, progress_y);
}
+ if (pos < width - 1) {
+ gr_blit(progressBarEmpty, pos, 0, width - pos, height, progress_x + pos, progress_y);
+ }
+ }
}
+ }
}
void ScreenRecoveryUI::SetColor(UIElement e) {
@@ -411,10 +420,10 @@ void ScreenRecoveryUI::LoadBitmap(const char* filename, GRSurface** surface) {
}
void ScreenRecoveryUI::LoadLocalizedBitmap(const char* filename, GRSurface** surface) {
- int result = res_create_localized_alpha_surface(filename, locale, surface);
- if (result < 0) {
- LOG(ERROR) << "couldn't load bitmap " << filename << " (error " << result << ")";
- }
+ int result = res_create_localized_alpha_surface(filename, locale_.c_str(), surface);
+ if (result < 0) {
+ LOG(ERROR) << "couldn't load bitmap " << filename << " (error " << result << ")";
+ }
}
static char** Alloc2d(size_t rows, size_t cols) {
@@ -436,98 +445,94 @@ void ScreenRecoveryUI::SetSystemUpdateText(bool security_update) {
Redraw();
}
-void ScreenRecoveryUI::Init() {
- gr_init();
-
- density_ = static_cast<float>(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f;
- is_large_ = gr_fb_height() > PixelsFromDp(800);
+bool ScreenRecoveryUI::InitTextParams() {
+ if (gr_init() < 0) {
+ return false;
+ }
gr_font_size(gr_sys_font(), &char_width_, &char_height_);
text_rows_ = gr_fb_height() / char_height_;
text_cols_ = gr_fb_width() / char_width_;
+ return true;
+}
- text_ = Alloc2d(text_rows_, text_cols_ + 1);
- file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1);
- menu_ = Alloc2d(text_rows_, text_cols_ + 1);
+bool ScreenRecoveryUI::Init(const std::string& locale) {
+ RecoveryUI::Init(locale);
+ if (!InitTextParams()) {
+ return false;
+ }
- text_col_ = text_row_ = 0;
- text_top_ = 1;
+ density_ = static_cast<float>(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f;
+
+ // Are we portrait or landscape?
+ layout_ = (gr_fb_width() > gr_fb_height()) ? LANDSCAPE : PORTRAIT;
+ // Are we the large variant of our base layout?
+ if (gr_fb_height() > PixelsFromDp(800)) ++layout_;
+
+ text_ = Alloc2d(text_rows_, text_cols_ + 1);
+ file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1);
+ menu_ = Alloc2d(text_rows_, text_cols_ + 1);
- LoadBitmap("icon_error", &error_icon);
+ text_col_ = text_row_ = 0;
+ text_top_ = 1;
- LoadBitmap("progress_empty", &progressBarEmpty);
- LoadBitmap("progress_fill", &progressBarFill);
+ LoadBitmap("icon_error", &error_icon);
- LoadBitmap("stage_empty", &stageMarkerEmpty);
- LoadBitmap("stage_fill", &stageMarkerFill);
+ LoadBitmap("progress_empty", &progressBarEmpty);
+ LoadBitmap("progress_fill", &progressBarFill);
- // Background text for "installing_update" could be "installing update"
- // or "installing security update". It will be set after UI init according
- // to commands in BCB.
- installing_text = nullptr;
- LoadLocalizedBitmap("erasing_text", &erasing_text);
- LoadLocalizedBitmap("no_command_text", &no_command_text);
- LoadLocalizedBitmap("error_text", &error_text);
+ LoadBitmap("stage_empty", &stageMarkerEmpty);
+ LoadBitmap("stage_fill", &stageMarkerFill);
- LoadAnimation();
+ // Background text for "installing_update" could be "installing update"
+ // or "installing security update". It will be set after UI init according
+ // to commands in BCB.
+ installing_text = nullptr;
+ LoadLocalizedBitmap("erasing_text", &erasing_text);
+ LoadLocalizedBitmap("no_command_text", &no_command_text);
+ LoadLocalizedBitmap("error_text", &error_text);
- pthread_create(&progress_thread_, nullptr, ProgressThreadStartRoutine, this);
+ LoadAnimation();
- RecoveryUI::Init();
+ pthread_create(&progress_thread_, nullptr, ProgressThreadStartRoutine, this);
+
+ return true;
}
void ScreenRecoveryUI::LoadAnimation() {
- // How many frames of intro and loop do we have?
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir("/res/images"), closedir);
dirent* de;
+ std::vector<std::string> intro_frame_names;
+ std::vector<std::string> loop_frame_names;
+
while ((de = readdir(dir.get())) != nullptr) {
- int value;
- if (sscanf(de->d_name, "intro%d", &value) == 1 && intro_frames < (value + 1)) {
- intro_frames = value + 1;
- } else if (sscanf(de->d_name, "loop%d", &value) == 1 && loop_frames < (value + 1)) {
- loop_frames = value + 1;
+ int value, num_chars;
+ if (sscanf(de->d_name, "intro%d%n.png", &value, &num_chars) == 1) {
+ intro_frame_names.emplace_back(de->d_name, num_chars);
+ } else if (sscanf(de->d_name, "loop%d%n.png", &value, &num_chars) == 1) {
+ loop_frame_names.emplace_back(de->d_name, num_chars);
}
}
+ intro_frames = intro_frame_names.size();
+ loop_frames = loop_frame_names.size();
+
// It's okay to not have an intro.
if (intro_frames == 0) intro_done = true;
// But you must have an animation.
if (loop_frames == 0) abort();
+ std::sort(intro_frame_names.begin(), intro_frame_names.end());
+ std::sort(loop_frame_names.begin(), loop_frame_names.end());
+
introFrames = new GRSurface*[intro_frames];
- for (int i = 0; i < intro_frames; ++i) {
- // TODO: remember the names above, so we don't have to hard-code the number of 0s.
- LoadBitmap(android::base::StringPrintf("intro%05d", i).c_str(), &introFrames[i]);
+ for (size_t i = 0; i < intro_frames; i++) {
+ LoadBitmap(intro_frame_names.at(i).c_str(), &introFrames[i]);
}
loopFrames = new GRSurface*[loop_frames];
- for (int i = 0; i < loop_frames; ++i) {
- LoadBitmap(android::base::StringPrintf("loop%05d", i).c_str(), &loopFrames[i]);
- }
-}
-
-void ScreenRecoveryUI::SetLocale(const char* new_locale) {
- this->locale = new_locale;
- this->rtl_locale = false;
-
- 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 RTL languages.
- 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);
+ for (size_t i = 0; i < loop_frames; i++) {
+ LoadBitmap(loop_frame_names.at(i).c_str(), &loopFrames[i]);
}
}
diff --git a/screen_ui.h b/screen_ui.h
index 4319b76ce..b2dcf4aeb 100644
--- a/screen_ui.h
+++ b/screen_ui.h
@@ -20,8 +20,12 @@
#include <pthread.h>
#include <stdio.h>
+#include <string>
+
#include "ui.h"
-#include "minui/minui.h"
+
+// From minui/minui.h.
+struct GRSurface;
// Implementation of RecoveryUI appropriate for devices with a screen
// (shows an icon + a progress bar, text logging, menu, etc.)
@@ -29,24 +33,23 @@ class ScreenRecoveryUI : public RecoveryUI {
public:
ScreenRecoveryUI();
- void Init();
- void SetLocale(const char* locale);
+ bool Init(const std::string& locale) override;
// overall recovery state ("background image")
void SetBackground(Icon icon);
void SetSystemUpdateText(bool security_update);
// progress indicator
- void SetProgressType(ProgressType type);
- void ShowProgress(float portion, float seconds);
- void SetProgress(float fraction);
+ void SetProgressType(ProgressType type) override;
+ void ShowProgress(float portion, float seconds) override;
+ void SetProgress(float fraction) override;
- void SetStage(int current, int max);
+ void SetStage(int current, int max) override;
// text log
- void ShowText(bool visible);
- bool IsTextVisible();
- bool WasTextEverVisible();
+ void ShowText(bool visible) override;
+ bool IsTextVisible() override;
+ bool WasTextEverVisible() override;
// printing messages
void Print(const char* fmt, ...) __printflike(2, 3);
@@ -71,14 +74,10 @@ class ScreenRecoveryUI : public RecoveryUI {
protected:
Icon currentIcon;
- const char* locale;
- bool intro_done;
- int current_frame;
-
// The scale factor from dp to pixels. 1.0 for mdpi, 4.0 for xxxhdpi.
float density_;
- // True if we should use the large layout.
- bool is_large_;
+ // The layout to use.
+ int layout_;
GRSurface* error_icon;
@@ -123,8 +122,11 @@ class ScreenRecoveryUI : public RecoveryUI {
pthread_t progress_thread_;
// Number of intro frames and loop frames in the animation.
- int intro_frames;
- int loop_frames;
+ size_t intro_frames;
+ size_t loop_frames;
+
+ size_t current_frame;
+ bool intro_done;
// Number of frames per sec (default: 30) for both parts of the animation.
int animation_fps;
@@ -134,13 +136,14 @@ class ScreenRecoveryUI : public RecoveryUI {
int char_width_;
int char_height_;
pthread_mutex_t updateMutex;
- bool rtl_locale;
- void draw_background_locked();
- void draw_foreground_locked();
- void draw_screen_locked();
- void update_screen_locked();
- void update_progress_locked();
+ virtual bool InitTextParams();
+
+ virtual void draw_background_locked();
+ virtual void draw_foreground_locked();
+ virtual void draw_screen_locked();
+ virtual void update_screen_locked();
+ virtual void update_progress_locked();
GRSurface* GetCurrentFrame();
GRSurface* GetCurrentText();
@@ -148,8 +151,8 @@ class ScreenRecoveryUI : public RecoveryUI {
static void* ProgressThreadStartRoutine(void* data);
void ProgressThreadLoop();
- void ShowFile(FILE*);
- void PrintV(const char*, bool, va_list);
+ virtual void ShowFile(FILE*);
+ virtual void PrintV(const char*, bool, va_list);
void PutChar(char);
void ClearText();
@@ -158,9 +161,9 @@ class ScreenRecoveryUI : public RecoveryUI {
void LoadLocalizedBitmap(const char* filename, GRSurface** surface);
int PixelsFromDp(int dp);
- int GetAnimationBaseline();
- int GetProgressBaseline();
- int GetTextBaseline();
+ virtual int GetAnimationBaseline();
+ virtual int GetProgressBaseline();
+ virtual int GetTextBaseline();
void DrawHorizontalRule(int* y);
void DrawTextLine(int x, int* y, const char* line, bool bold);
diff --git a/stub_ui.h b/stub_ui.h
new file mode 100644
index 000000000..85dbcfd89
--- /dev/null
+++ b/stub_ui.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RECOVERY_STUB_UI_H
+#define RECOVERY_STUB_UI_H
+
+#include "ui.h"
+
+// Stub implementation of RecoveryUI for devices without screen.
+class StubRecoveryUI : public RecoveryUI {
+ public:
+ StubRecoveryUI() = default;
+
+ void SetBackground(Icon icon) override {}
+ void SetSystemUpdateText(bool security_update) override {}
+
+ // progress indicator
+ void SetProgressType(ProgressType type) override {}
+ void ShowProgress(float portion, float seconds) override {}
+ void SetProgress(float fraction) override {}
+
+ void SetStage(int current, int max) override {}
+
+ // text log
+ void ShowText(bool visible) override {}
+ bool IsTextVisible() override {
+ return false;
+ }
+ bool WasTextEverVisible() override {
+ return false;
+ }
+
+ // printing messages
+ void Print(const char* fmt, ...) override {
+ va_list ap;
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ }
+ void PrintOnScreenOnly(const char* fmt, ...) override {}
+ void ShowFile(const char* filename) override {}
+
+ // menu display
+ void StartMenu(const char* const* headers, const char* const* items,
+ int initial_selection) override {}
+ int SelectMenu(int sel) override {
+ return sel;
+ }
+ void EndMenu() override {}
+};
+
+#endif // RECOVERY_STUB_UI_H
diff --git a/tests/Android.mk b/tests/Android.mk
index 5f6a7ce0c..a7e7b3c35 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -33,9 +33,11 @@ LOCAL_STATIC_LIBRARIES := \
LOCAL_SRC_FILES := \
unit/asn1_decoder_test.cpp \
+ unit/dirutil_test.cpp \
unit/locale_test.cpp \
unit/sysutil_test.cpp \
- unit/zip_test.cpp
+ unit/zip_test.cpp \
+ unit/ziputil_test.cpp
LOCAL_C_INCLUDES := bootable/recovery
LOCAL_SHARED_LIBRARIES := liblog
@@ -47,10 +49,34 @@ LOCAL_CLANG := true
LOCAL_CFLAGS := -Werror
LOCAL_MODULE := recovery_manual_test
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_STATIC_LIBRARIES := libbase
+LOCAL_STATIC_LIBRARIES := \
+ libminui \
+ libbase
LOCAL_SRC_FILES := manual/recovery_test.cpp
-LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_SHARED_LIBRARIES := \
+ liblog \
+ libpng
+
+resource_files := $(call find-files-in-subdirs, bootable/recovery, \
+ "*_text.png", \
+ res-mdpi/images \
+ res-hdpi/images \
+ res-xhdpi/images \
+ res-xxhdpi/images \
+ res-xxxhdpi/images \
+ )
+
+# The resource image files that will go to $OUT/data/nativetest/recovery.
+testimage_out_path := $(TARGET_OUT_DATA)/nativetest/recovery
+GEN := $(addprefix $(testimage_out_path)/, $(resource_files))
+
+$(GEN): PRIVATE_PATH := $(LOCAL_PATH)
+$(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@
+$(GEN): $(testimage_out_path)/% : bootable/recovery/%
+ $(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
include $(BUILD_NATIVE_TEST)
# Component tests
@@ -61,16 +87,20 @@ LOCAL_MODULE := recovery_component_test
LOCAL_C_INCLUDES := bootable/recovery
LOCAL_SRC_FILES := \
component/applypatch_test.cpp \
+ component/bootloader_message_test.cpp \
component/edify_test.cpp \
+ component/imgdiff_test.cpp \
+ component/uncrypt_test.cpp \
component/updater_test.cpp \
component/verifier_test.cpp
+
LOCAL_FORCE_STATIC_EXECUTABLE := true
tune2fs_static_libraries := \
libext2_com_err \
libext2_blkid \
libext2_quota \
- libext2_uuid_static \
+ libext2_uuid \
libext2_e2p \
libext2fs
@@ -78,6 +108,9 @@ LOCAL_STATIC_LIBRARIES := \
libapplypatch_modes \
libapplypatch \
libedify \
+ libimgdiff \
+ libimgpatch \
+ libbsdiff \
libotafault \
libupdater \
libbootloader_message \
@@ -85,11 +118,13 @@ LOCAL_STATIC_LIBRARIES := \
libminui \
libotautil \
libmounts \
+ libdivsufsort \
+ libdivsufsort64 \
libfs_mgr \
liblog \
libselinux \
- libext4_utils_static \
- libsparse_static \
+ libext4_utils \
+ libsparse \
libcrypto_utils \
libcrypto \
libcutils \
@@ -125,3 +160,26 @@ LOCAL_GENERATED_SOURCES += $(GEN)
LOCAL_PICKUP_FILES := $(testdata_continuous_zip_prefix)
include $(BUILD_NATIVE_TEST)
+
+# Host tests
+include $(CLEAR_VARS)
+LOCAL_CFLAGS := -Werror
+LOCAL_MODULE := recovery_host_test
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_C_INCLUDES := bootable/recovery
+LOCAL_SRC_FILES := \
+ component/imgdiff_test.cpp
+LOCAL_STATIC_LIBRARIES := \
+ libimgdiff \
+ libimgpatch \
+ libbsdiff \
+ libziparchive \
+ libbase \
+ libcrypto \
+ libbz \
+ libdivsufsort64 \
+ libdivsufsort \
+ libz
+LOCAL_SHARED_LIBRARIES := \
+ liblog
+include $(BUILD_HOST_NATIVE_TEST)
diff --git a/tests/component/bootloader_message_test.cpp b/tests/component/bootloader_message_test.cpp
new file mode 100644
index 000000000..dbcaf619e
--- /dev/null
+++ b/tests/component/bootloader_message_test.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/strings.h>
+#include <bootloader_message/bootloader_message.h>
+#include <gtest/gtest.h>
+
+#include <string>
+#include <vector>
+
+class BootloaderMessageTest : public ::testing::Test {
+ protected:
+ virtual void TearDown() override {
+ // Clear the BCB.
+ std::string err;
+ ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err;
+ }
+};
+
+TEST_F(BootloaderMessageTest, clear_bootloader_message) {
+ // Clear the BCB.
+ std::string err;
+ ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err;
+
+ // Verify the content.
+ bootloader_message boot;
+ ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err;
+
+ // All the bytes should be cleared.
+ ASSERT_EQ(std::string(sizeof(boot), '\0'),
+ std::string(reinterpret_cast<const char*>(&boot), sizeof(boot)));
+}
+
+TEST_F(BootloaderMessageTest, read_and_write_bootloader_message) {
+ // Write the BCB.
+ bootloader_message boot = {};
+ strlcpy(boot.command, "command", sizeof(boot.command));
+ strlcpy(boot.recovery, "message1\nmessage2\n", sizeof(boot.recovery));
+ strlcpy(boot.status, "status1", sizeof(boot.status));
+
+ std::string err;
+ ASSERT_TRUE(write_bootloader_message(boot, &err)) << "Failed to write BCB: " << err;
+
+ // Read and verify.
+ bootloader_message boot_verify;
+ ASSERT_TRUE(read_bootloader_message(&boot_verify, &err)) << "Failed to read BCB: " << err;
+
+ ASSERT_EQ(std::string(reinterpret_cast<const char*>(&boot), sizeof(boot)),
+ std::string(reinterpret_cast<const char*>(&boot_verify), sizeof(boot_verify)));
+}
+
+TEST_F(BootloaderMessageTest, write_bootloader_message_options) {
+ // Write the options to BCB.
+ std::vector<std::string> options = { "option1", "option2" };
+ std::string err;
+ ASSERT_TRUE(write_bootloader_message(options, &err)) << "Failed to write BCB: " << err;
+
+ // Inject some bytes into boot, which should be overwritten while reading.
+ bootloader_message boot;
+ strlcpy(boot.recovery, "random message", sizeof(boot.recovery));
+ strlcpy(boot.reserved, "reserved bytes", sizeof(boot.reserved));
+
+ ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err;
+
+ // Verify that command and recovery fields should be set.
+ ASSERT_EQ("boot-recovery", std::string(boot.command));
+ std::string expected = "recovery\n" + android::base::Join(options, "\n") + "\n";
+ ASSERT_EQ(expected, std::string(boot.recovery));
+
+ // The rest should be cleared.
+ ASSERT_EQ(std::string(sizeof(boot.status), '\0'), std::string(boot.status, sizeof(boot.status)));
+ ASSERT_EQ(std::string(sizeof(boot.stage), '\0'), std::string(boot.stage, sizeof(boot.stage)));
+ ASSERT_EQ(std::string(sizeof(boot.reserved), '\0'),
+ std::string(boot.reserved, sizeof(boot.reserved)));
+}
+
+TEST_F(BootloaderMessageTest, write_bootloader_message_options_empty) {
+ // Write empty vector.
+ std::vector<std::string> options;
+ std::string err;
+ ASSERT_TRUE(write_bootloader_message(options, &err)) << "Failed to write BCB: " << err;
+
+ // Read and verify.
+ bootloader_message boot;
+ ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err;
+
+ // command and recovery fields should be set.
+ ASSERT_EQ("boot-recovery", std::string(boot.command));
+ ASSERT_EQ("recovery\n", std::string(boot.recovery));
+
+ // The rest should be cleared.
+ ASSERT_EQ(std::string(sizeof(boot.status), '\0'), std::string(boot.status, sizeof(boot.status)));
+ ASSERT_EQ(std::string(sizeof(boot.stage), '\0'), std::string(boot.stage, sizeof(boot.stage)));
+ ASSERT_EQ(std::string(sizeof(boot.reserved), '\0'),
+ std::string(boot.reserved, sizeof(boot.reserved)));
+}
+
+TEST_F(BootloaderMessageTest, write_bootloader_message_options_long) {
+ // Write super long message.
+ std::vector<std::string> options;
+ for (int i = 0; i < 100; i++) {
+ options.push_back("option: " + std::to_string(i));
+ }
+
+ std::string err;
+ ASSERT_TRUE(write_bootloader_message(options, &err)) << "Failed to write BCB: " << err;
+
+ // Read and verify.
+ bootloader_message boot;
+ ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err;
+
+ // Make sure it's long enough.
+ std::string expected = "recovery\n" + android::base::Join(options, "\n") + "\n";
+ ASSERT_GE(expected.size(), sizeof(boot.recovery));
+
+ // command and recovery fields should be set.
+ ASSERT_EQ("boot-recovery", std::string(boot.command));
+ ASSERT_EQ(expected.substr(0, sizeof(boot.recovery) - 1), std::string(boot.recovery));
+ ASSERT_EQ('\0', boot.recovery[sizeof(boot.recovery) - 1]);
+
+ // The rest should be cleared.
+ ASSERT_EQ(std::string(sizeof(boot.status), '\0'), std::string(boot.status, sizeof(boot.status)));
+ ASSERT_EQ(std::string(sizeof(boot.stage), '\0'), std::string(boot.stage, sizeof(boot.stage)));
+ ASSERT_EQ(std::string(sizeof(boot.reserved), '\0'),
+ std::string(boot.reserved, sizeof(boot.reserved)));
+}
+
+TEST_F(BootloaderMessageTest, update_bootloader_message) {
+ // Inject some bytes into boot, which should be not overwritten later.
+ bootloader_message boot;
+ strlcpy(boot.recovery, "random message", sizeof(boot.recovery));
+ strlcpy(boot.reserved, "reserved bytes", sizeof(boot.reserved));
+ std::string err;
+ ASSERT_TRUE(write_bootloader_message(boot, &err)) << "Failed to write BCB: " << err;
+
+ // Update the BCB message.
+ std::vector<std::string> options = { "option1", "option2" };
+ ASSERT_TRUE(update_bootloader_message(options, &err)) << "Failed to update BCB: " << err;
+
+ bootloader_message boot_verify;
+ ASSERT_TRUE(read_bootloader_message(&boot_verify, &err)) << "Failed to read BCB: " << err;
+
+ // Verify that command and recovery fields should be set.
+ ASSERT_EQ("boot-recovery", std::string(boot_verify.command));
+ std::string expected = "recovery\n" + android::base::Join(options, "\n") + "\n";
+ ASSERT_EQ(expected, std::string(boot_verify.recovery));
+
+ // The rest should be intact.
+ ASSERT_EQ(std::string(boot.status), std::string(boot_verify.status));
+ ASSERT_EQ(std::string(boot.stage), std::string(boot_verify.stage));
+ ASSERT_EQ(std::string(boot.reserved), std::string(boot_verify.reserved));
+}
diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp
new file mode 100644
index 000000000..7ad330783
--- /dev/null
+++ b/tests/component/imgdiff_test.cpp
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <applypatch/imgdiff.h>
+#include <applypatch/imgpatch.h>
+#include <gtest/gtest.h>
+#include <ziparchive/zip_writer.h>
+
+#include "applypatch/utils.h"
+
+static ssize_t MemorySink(const unsigned char* data, ssize_t len, void* token) {
+ std::string* s = static_cast<std::string*>(token);
+ s->append(reinterpret_cast<const char*>(data), len);
+ return len;
+}
+
+// Sanity check for the given imgdiff patch header.
+static void verify_patch_header(const std::string& patch, size_t* num_normal, size_t* num_raw,
+ size_t* num_deflate) {
+ const size_t size = patch.size();
+ const char* data = patch.data();
+
+ ASSERT_GE(size, 12U);
+ ASSERT_EQ("IMGDIFF2", std::string(data, 8));
+
+ const int num_chunks = Read4(data + 8);
+ ASSERT_GE(num_chunks, 0);
+
+ size_t normal = 0;
+ size_t raw = 0;
+ size_t deflate = 0;
+
+ size_t pos = 12;
+ for (int i = 0; i < num_chunks; ++i) {
+ ASSERT_LE(pos + 4, size);
+ int type = Read4(data + pos);
+ pos += 4;
+ if (type == CHUNK_NORMAL) {
+ pos += 24;
+ ASSERT_LE(pos, size);
+ normal++;
+ } else if (type == CHUNK_RAW) {
+ ASSERT_LE(pos + 4, size);
+ ssize_t data_len = Read4(data + pos);
+ ASSERT_GT(data_len, 0);
+ pos += 4 + data_len;
+ ASSERT_LE(pos, size);
+ raw++;
+ } else if (type == CHUNK_DEFLATE) {
+ pos += 60;
+ ASSERT_LE(pos, size);
+ deflate++;
+ } else {
+ FAIL() << "Invalid patch type: " << type;
+ }
+ }
+
+ if (num_normal != nullptr) *num_normal = normal;
+ if (num_raw != nullptr) *num_raw = raw;
+ if (num_deflate != nullptr) *num_deflate = deflate;
+}
+
+TEST(ImgdiffTest, invalid_args) {
+ // Insufficient inputs.
+ ASSERT_EQ(2, imgdiff(1, (const char* []){ "imgdiff" }));
+ ASSERT_EQ(2, imgdiff(2, (const char* []){ "imgdiff", "-z" }));
+ ASSERT_EQ(2, imgdiff(2, (const char* []){ "imgdiff", "-b" }));
+ ASSERT_EQ(2, imgdiff(3, (const char* []){ "imgdiff", "-z", "-b" }));
+
+ // Failed to read bonus file.
+ ASSERT_EQ(1, imgdiff(3, (const char* []){ "imgdiff", "-b", "doesntexist" }));
+
+ // Failed to read input files.
+ ASSERT_EQ(1, imgdiff(4, (const char* []){ "imgdiff", "doesntexist", "doesntexist", "output" }));
+ ASSERT_EQ(
+ 1, imgdiff(5, (const char* []){ "imgdiff", "-z", "doesntexist", "doesntexist", "output" }));
+}
+
+TEST(ImgdiffTest, image_mode_smoke) {
+ // Random bytes.
+ const std::string src("abcdefg");
+ TemporaryFile src_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
+
+ const std::string tgt("abcdefgxyz");
+ TemporaryFile tgt_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
+
+ TemporaryFile patch_file;
+ std::vector<const char*> args = {
+ "imgdiff", src_file.path, tgt_file.path, patch_file.path,
+ };
+ ASSERT_EQ(0, imgdiff(args.size(), args.data()));
+
+ // Verify.
+ std::string patch;
+ ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
+
+ // Expect one CHUNK_RAW entry.
+ size_t num_normal;
+ size_t num_raw;
+ size_t num_deflate;
+ verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
+ ASSERT_EQ(0U, num_normal);
+ ASSERT_EQ(0U, num_deflate);
+ ASSERT_EQ(1U, num_raw);
+
+ std::string patched;
+ ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
+ reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
+ MemorySink, &patched));
+ ASSERT_EQ(tgt, patched);
+}
+
+TEST(ImgdiffTest, zip_mode_smoke_store) {
+ // Construct src and tgt zip files.
+ TemporaryFile src_file;
+ FILE* src_file_ptr = fdopen(src_file.fd, "wb");
+ ZipWriter src_writer(src_file_ptr);
+ ASSERT_EQ(0, src_writer.StartEntry("file1.txt", 0)); // Store mode.
+ const std::string src_content("abcdefg");
+ ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size()));
+ ASSERT_EQ(0, src_writer.FinishEntry());
+ ASSERT_EQ(0, src_writer.Finish());
+ ASSERT_EQ(0, fclose(src_file_ptr));
+
+ TemporaryFile tgt_file;
+ FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb");
+ ZipWriter tgt_writer(tgt_file_ptr);
+ ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", 0)); // Store mode.
+ const std::string tgt_content("abcdefgxyz");
+ ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size()));
+ ASSERT_EQ(0, tgt_writer.FinishEntry());
+ ASSERT_EQ(0, tgt_writer.Finish());
+ ASSERT_EQ(0, fclose(tgt_file_ptr));
+
+ // Compute patch.
+ TemporaryFile patch_file;
+ std::vector<const char*> args = {
+ "imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path,
+ };
+ ASSERT_EQ(0, imgdiff(args.size(), args.data()));
+
+ // Verify.
+ std::string tgt;
+ ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
+ std::string src;
+ ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src));
+ std::string patch;
+ ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
+
+ // Expect one CHUNK_RAW entry.
+ size_t num_normal;
+ size_t num_raw;
+ size_t num_deflate;
+ verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
+ ASSERT_EQ(0U, num_normal);
+ ASSERT_EQ(0U, num_deflate);
+ ASSERT_EQ(1U, num_raw);
+
+ std::string patched;
+ ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
+ reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
+ MemorySink, &patched));
+ ASSERT_EQ(tgt, patched);
+}
+
+TEST(ImgdiffTest, zip_mode_smoke_compressed) {
+ // Construct src and tgt zip files.
+ TemporaryFile src_file;
+ FILE* src_file_ptr = fdopen(src_file.fd, "wb");
+ ZipWriter src_writer(src_file_ptr);
+ ASSERT_EQ(0, src_writer.StartEntry("file1.txt", ZipWriter::kCompress));
+ const std::string src_content("abcdefg");
+ ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size()));
+ ASSERT_EQ(0, src_writer.FinishEntry());
+ ASSERT_EQ(0, src_writer.Finish());
+ ASSERT_EQ(0, fclose(src_file_ptr));
+
+ TemporaryFile tgt_file;
+ FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb");
+ ZipWriter tgt_writer(tgt_file_ptr);
+ ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", ZipWriter::kCompress));
+ const std::string tgt_content("abcdefgxyz");
+ ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size()));
+ ASSERT_EQ(0, tgt_writer.FinishEntry());
+ ASSERT_EQ(0, tgt_writer.Finish());
+ ASSERT_EQ(0, fclose(tgt_file_ptr));
+
+ // Compute patch.
+ TemporaryFile patch_file;
+ std::vector<const char*> args = {
+ "imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path,
+ };
+ ASSERT_EQ(0, imgdiff(args.size(), args.data()));
+
+ // Verify.
+ std::string tgt;
+ ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
+ std::string src;
+ ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src));
+ std::string patch;
+ ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
+
+ // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer).
+ size_t num_normal;
+ size_t num_raw;
+ size_t num_deflate;
+ verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
+ ASSERT_EQ(0U, num_normal);
+ ASSERT_EQ(1U, num_deflate);
+ ASSERT_EQ(2U, num_raw);
+
+ std::string patched;
+ ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
+ reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
+ MemorySink, &patched));
+ ASSERT_EQ(tgt, patched);
+}
+
+TEST(ImgdiffTest, image_mode_simple) {
+ // src: "abcdefgh" + gzipped "xyz" (echo -n "xyz" | gzip -f | hd).
+ const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+ 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
+ '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac',
+ '\x02', '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03',
+ '\x00', '\x00', '\x00' };
+ const std::string src(src_data.cbegin(), src_data.cend());
+ TemporaryFile src_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
+
+ // tgt: "abcdefgxyz" + gzipped "xxyyzz".
+ const std::vector<char> tgt_data = {
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z', '\x1f', '\x8b',
+ '\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xa8', '\xac',
+ '\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\x00', '\x00', '\x00'
+ };
+ const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
+ TemporaryFile tgt_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
+
+ TemporaryFile patch_file;
+ std::vector<const char*> args = {
+ "imgdiff", src_file.path, tgt_file.path, patch_file.path,
+ };
+ ASSERT_EQ(0, imgdiff(args.size(), args.data()));
+
+ // Verify.
+ std::string patch;
+ ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
+
+ // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer).
+ size_t num_normal;
+ size_t num_raw;
+ size_t num_deflate;
+ verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
+ ASSERT_EQ(0U, num_normal);
+ ASSERT_EQ(1U, num_deflate);
+ ASSERT_EQ(2U, num_raw);
+
+ std::string patched;
+ ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
+ reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
+ MemorySink, &patched));
+ ASSERT_EQ(tgt, patched);
+}
+
+TEST(ImgdiffTest, image_mode_different_num_chunks) {
+ // src: "abcdefgh" + gzipped "xyz" (echo -n "xyz" | gzip -f | hd) + gzipped "test".
+ const std::vector<char> src_data = {
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\x1f', '\x8b', '\x08',
+ '\x00', '\xc4', '\x1e', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac', '\x02',
+ '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03', '\x00', '\x00', '\x00', '\x1f', '\x8b',
+ '\x08', '\x00', '\xb2', '\x3a', '\x53', '\x58', '\x00', '\x03', '\x2b', '\x49', '\x2d',
+ '\x2e', '\x01', '\x00', '\x0c', '\x7e', '\x7f', '\xd8', '\x04', '\x00', '\x00', '\x00'
+ };
+ const std::string src(src_data.cbegin(), src_data.cend());
+ TemporaryFile src_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
+
+ // tgt: "abcdefgxyz" + gzipped "xxyyzz".
+ const std::vector<char> tgt_data = {
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z', '\x1f', '\x8b',
+ '\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xa8', '\xac',
+ '\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\x00', '\x00', '\x00'
+ };
+ const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
+ TemporaryFile tgt_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
+
+ TemporaryFile patch_file;
+ std::vector<const char*> args = {
+ "imgdiff", src_file.path, tgt_file.path, patch_file.path,
+ };
+ ASSERT_EQ(1, imgdiff(args.size(), args.data()));
+}
+
+TEST(ImgdiffTest, image_mode_merge_chunks) {
+ // src: "abcdefgh" + gzipped "xyz" (echo -n "xyz" | gzip -f | hd).
+ const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+ 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
+ '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac',
+ '\x02', '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03',
+ '\x00', '\x00', '\x00' };
+ const std::string src(src_data.cbegin(), src_data.cend());
+ TemporaryFile src_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
+
+ // tgt: gzipped "xyz" + "abcdefgh".
+ const std::vector<char> tgt_data = {
+ '\x1f', '\x8b', '\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8',
+ '\xa8', '\xac', '\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\x00',
+ '\x00', '\x00', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z'
+ };
+ const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
+ TemporaryFile tgt_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
+
+ // Since a gzipped entry will become CHUNK_RAW (header) + CHUNK_DEFLATE (data) +
+ // CHUNK_RAW (footer), they both should contain the same chunk types after merging.
+
+ TemporaryFile patch_file;
+ std::vector<const char*> args = {
+ "imgdiff", src_file.path, tgt_file.path, patch_file.path,
+ };
+ ASSERT_EQ(0, imgdiff(args.size(), args.data()));
+
+ // Verify.
+ std::string patch;
+ ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
+
+ // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer).
+ size_t num_normal;
+ size_t num_raw;
+ size_t num_deflate;
+ verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
+ ASSERT_EQ(0U, num_normal);
+ ASSERT_EQ(1U, num_deflate);
+ ASSERT_EQ(2U, num_raw);
+
+ std::string patched;
+ ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
+ reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
+ MemorySink, &patched));
+ ASSERT_EQ(tgt, patched);
+}
+
+TEST(ImgdiffTest, image_mode_spurious_magic) {
+ // src: "abcdefgh" + '0x1f8b0b00' + some bytes.
+ const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+ 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
+ '\x53', '\x58', 't', 'e', 's', 't' };
+ const std::string src(src_data.cbegin(), src_data.cend());
+ TemporaryFile src_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
+
+ // tgt: "abcdefgxyz".
+ const std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' };
+ const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
+ TemporaryFile tgt_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
+
+ TemporaryFile patch_file;
+ std::vector<const char*> args = {
+ "imgdiff", src_file.path, tgt_file.path, patch_file.path,
+ };
+ ASSERT_EQ(0, imgdiff(args.size(), args.data()));
+
+ // Verify.
+ std::string patch;
+ ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
+
+ // Expect one CHUNK_RAW (header) entry.
+ size_t num_normal;
+ size_t num_raw;
+ size_t num_deflate;
+ verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
+ ASSERT_EQ(0U, num_normal);
+ ASSERT_EQ(0U, num_deflate);
+ ASSERT_EQ(1U, num_raw);
+
+ std::string patched;
+ ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
+ reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
+ MemorySink, &patched));
+ ASSERT_EQ(tgt, patched);
+}
+
+TEST(ImgdiffTest, image_mode_short_input1) {
+ // src: "abcdefgh" + '0x1f8b0b'.
+ const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', '\x1f', '\x8b', '\x08' };
+ const std::string src(src_data.cbegin(), src_data.cend());
+ TemporaryFile src_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
+
+ // tgt: "abcdefgxyz".
+ const std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' };
+ const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
+ TemporaryFile tgt_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
+
+ TemporaryFile patch_file;
+ std::vector<const char*> args = {
+ "imgdiff", src_file.path, tgt_file.path, patch_file.path,
+ };
+ ASSERT_EQ(0, imgdiff(args.size(), args.data()));
+
+ // Verify.
+ std::string patch;
+ ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
+
+ // Expect one CHUNK_RAW (header) entry.
+ size_t num_normal;
+ size_t num_raw;
+ size_t num_deflate;
+ verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
+ ASSERT_EQ(0U, num_normal);
+ ASSERT_EQ(0U, num_deflate);
+ ASSERT_EQ(1U, num_raw);
+
+ std::string patched;
+ ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
+ reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
+ MemorySink, &patched));
+ ASSERT_EQ(tgt, patched);
+}
+
+TEST(ImgdiffTest, image_mode_short_input2) {
+ // src: "abcdefgh" + '0x1f8b0b00'.
+ const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', '\x1f', '\x8b', '\x08', '\x00' };
+ const std::string src(src_data.cbegin(), src_data.cend());
+ TemporaryFile src_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
+
+ // tgt: "abcdefgxyz".
+ const std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' };
+ const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
+ TemporaryFile tgt_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
+
+ TemporaryFile patch_file;
+ std::vector<const char*> args = {
+ "imgdiff", src_file.path, tgt_file.path, patch_file.path,
+ };
+ ASSERT_EQ(0, imgdiff(args.size(), args.data()));
+
+ // Verify.
+ std::string patch;
+ ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
+
+ // Expect one CHUNK_RAW (header) entry.
+ size_t num_normal;
+ size_t num_raw;
+ size_t num_deflate;
+ verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
+ ASSERT_EQ(0U, num_normal);
+ ASSERT_EQ(0U, num_deflate);
+ ASSERT_EQ(1U, num_raw);
+
+ std::string patched;
+ ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
+ reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
+ MemorySink, &patched));
+ ASSERT_EQ(tgt, patched);
+}
+
+TEST(ImgdiffTest, image_mode_single_entry_long) {
+ // src: "abcdefgh" + '0x1f8b0b00' + some bytes.
+ const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+ 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
+ '\x53', '\x58', 't', 'e', 's', 't' };
+ const std::string src(src_data.cbegin(), src_data.cend());
+ TemporaryFile src_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
+
+ // tgt: "abcdefgxyz" + 200 bytes.
+ std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' };
+ tgt_data.resize(tgt_data.size() + 200);
+
+ const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
+ TemporaryFile tgt_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
+
+ TemporaryFile patch_file;
+ std::vector<const char*> args = {
+ "imgdiff", src_file.path, tgt_file.path, patch_file.path,
+ };
+ ASSERT_EQ(0, imgdiff(args.size(), args.data()));
+
+ // Verify.
+ std::string patch;
+ ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
+
+ // Expect one CHUNK_NORMAL entry, since it's exceeding the 160-byte limit for RAW.
+ size_t num_normal;
+ size_t num_raw;
+ size_t num_deflate;
+ verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
+ ASSERT_EQ(1U, num_normal);
+ ASSERT_EQ(0U, num_deflate);
+ ASSERT_EQ(0U, num_raw);
+
+ std::string patched;
+ ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(),
+ reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
+ MemorySink, &patched));
+ ASSERT_EQ(tgt, patched);
+}
diff --git a/tests/component/uncrypt_test.cpp b/tests/component/uncrypt_test.cpp
new file mode 100644
index 000000000..a554c3e48
--- /dev/null
+++ b/tests/component/uncrypt_test.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+#include <bootloader_message/bootloader_message.h>
+#include <gtest/gtest.h>
+
+static const std::string UNCRYPT_SOCKET = "/dev/socket/uncrypt";
+static const std::string INIT_SVC_SETUP_BCB = "init.svc.setup-bcb";
+static const std::string INIT_SVC_CLEAR_BCB = "init.svc.clear-bcb";
+static const std::string INIT_SVC_UNCRYPT = "init.svc.uncrypt";
+static constexpr int SOCKET_CONNECTION_MAX_RETRY = 30;
+
+class UncryptTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ ASSERT_TRUE(android::base::SetProperty("ctl.stop", "setup-bcb"));
+ ASSERT_TRUE(android::base::SetProperty("ctl.stop", "clear-bcb"));
+ ASSERT_TRUE(android::base::SetProperty("ctl.stop", "uncrypt"));
+
+ bool success = false;
+ for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {
+ std::string setup_bcb = android::base::GetProperty(INIT_SVC_SETUP_BCB, "");
+ std::string clear_bcb = android::base::GetProperty(INIT_SVC_CLEAR_BCB, "");
+ std::string uncrypt = android::base::GetProperty(INIT_SVC_UNCRYPT, "");
+ LOG(INFO) << "setup-bcb: [" << setup_bcb << "] clear-bcb: [" << clear_bcb << "] uncrypt: ["
+ << uncrypt << "]";
+ if (setup_bcb != "running" && clear_bcb != "running" && uncrypt != "running") {
+ success = true;
+ break;
+ }
+ sleep(1);
+ }
+
+ ASSERT_TRUE(success) << "uncrypt service is not available.";
+ }
+};
+
+TEST_F(UncryptTest, setup_bcb) {
+ // Trigger the setup-bcb service.
+ ASSERT_TRUE(android::base::SetProperty("ctl.start", "setup-bcb"));
+
+ // Test tends to be flaky if proceeding immediately ("Transport endpoint is not connected").
+ sleep(1);
+
+ struct sockaddr_un un = {};
+ un.sun_family = AF_UNIX;
+ strlcpy(un.sun_path, UNCRYPT_SOCKET.c_str(), sizeof(un.sun_path));
+
+ int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ ASSERT_NE(-1, sockfd);
+
+ // Connect to the uncrypt socket.
+ bool success = false;
+ for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {
+ if (connect(sockfd, reinterpret_cast<struct sockaddr*>(&un), sizeof(struct sockaddr_un)) != 0) {
+ success = true;
+ break;
+ }
+ sleep(1);
+ }
+ ASSERT_TRUE(success);
+
+ // Send out the BCB message.
+ std::string message = "--update_message=abc value";
+ std::string message_in_bcb = "recovery\n--update_message=abc value\n";
+ int length = static_cast<int>(message.size());
+ int length_out = htonl(length);
+ ASSERT_TRUE(android::base::WriteFully(sockfd, &length_out, sizeof(int)))
+ << "Failed to write length: " << strerror(errno);
+ ASSERT_TRUE(android::base::WriteFully(sockfd, message.data(), length))
+ << "Failed to write message: " << strerror(errno);
+
+ // Check the status code from uncrypt.
+ int status;
+ ASSERT_TRUE(android::base::ReadFully(sockfd, &status, sizeof(int)));
+ ASSERT_EQ(100U, ntohl(status));
+
+ // Ack having received the status code.
+ int code = 0;
+ ASSERT_TRUE(android::base::WriteFully(sockfd, &code, sizeof(int)));
+
+ ASSERT_EQ(0, close(sockfd));
+
+ ASSERT_TRUE(android::base::SetProperty("ctl.stop", "setup-bcb"));
+
+ // Verify the message by reading from BCB directly.
+ bootloader_message boot;
+ std::string err;
+ ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err;
+
+ ASSERT_EQ("boot-recovery", std::string(boot.command));
+ ASSERT_EQ(message_in_bcb, std::string(boot.recovery));
+
+ // The rest of the boot.recovery message should be zero'd out.
+ ASSERT_LE(message_in_bcb.size(), sizeof(boot.recovery));
+ size_t left = sizeof(boot.recovery) - message_in_bcb.size();
+ ASSERT_EQ(std::string(left, '\0'), std::string(&boot.recovery[message_in_bcb.size()], left));
+
+ // Clear the BCB.
+ ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err;
+}
+
+TEST_F(UncryptTest, clear_bcb) {
+ // Trigger the clear-bcb service.
+ ASSERT_TRUE(android::base::SetProperty("ctl.start", "clear-bcb"));
+
+ // Test tends to be flaky if proceeding immediately ("Transport endpoint is not connected").
+ sleep(1);
+
+ struct sockaddr_un un = {};
+ un.sun_family = AF_UNIX;
+ strlcpy(un.sun_path, UNCRYPT_SOCKET.c_str(), sizeof(un.sun_path));
+
+ int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ ASSERT_NE(-1, sockfd);
+
+ // Connect to the uncrypt socket.
+ bool success = false;
+ for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {
+ if (connect(sockfd, reinterpret_cast<struct sockaddr*>(&un), sizeof(struct sockaddr_un)) != 0) {
+ success = true;
+ break;
+ }
+ sleep(1);
+ }
+ ASSERT_TRUE(success);
+
+ // Check the status code from uncrypt.
+ int status;
+ ASSERT_TRUE(android::base::ReadFully(sockfd, &status, sizeof(int)));
+ ASSERT_EQ(100U, ntohl(status));
+
+ // Ack having received the status code.
+ int code = 0;
+ ASSERT_TRUE(android::base::WriteFully(sockfd, &code, sizeof(int)));
+
+ ASSERT_EQ(0, close(sockfd));
+
+ ASSERT_TRUE(android::base::SetProperty("ctl.stop", "clear-bcb"));
+
+ // Verify the content by reading from BCB directly.
+ bootloader_message boot;
+ std::string err;
+ ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err;
+
+ // All the bytes should be cleared.
+ ASSERT_EQ(std::string(sizeof(boot), '\0'),
+ std::string(reinterpret_cast<const char*>(&boot), sizeof(boot)));
+}
diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp
index f31f1f82a..fa5f03134 100644
--- a/tests/component/updater_test.cpp
+++ b/tests/component/updater_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
@@ -22,6 +23,8 @@
#include <android-base/file.h>
#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android-base/test_utils.h>
#include <bootloader_message/bootloader_message.h>
#include <gtest/gtest.h>
@@ -510,3 +513,50 @@ TEST_F(UpdaterTest, set_stage) {
script = "set_stage(\"/dev/full\", \"1/3\")";
expect("", script.c_str(), kNoCause);
}
+
+TEST_F(UpdaterTest, set_progress) {
+ // set_progress() expects one argument.
+ expect(nullptr, "set_progress()", kArgsParsingFailure);
+ expect(nullptr, "set_progress(\"arg1\", \"arg2\")", kArgsParsingFailure);
+
+ // Invalid progress argument.
+ expect(nullptr, "set_progress(\"arg1\")", kArgsParsingFailure);
+ expect(nullptr, "set_progress(\"3x+5\")", kArgsParsingFailure);
+ expect(nullptr, "set_progress(\".3.5\")", kArgsParsingFailure);
+
+ TemporaryFile tf;
+ UpdaterInfo updater_info;
+ updater_info.cmd_pipe = fdopen(tf.fd, "w");
+ expect(".52", "set_progress(\".52\")", kNoCause, &updater_info);
+ fflush(updater_info.cmd_pipe);
+
+ std::string cmd;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &cmd));
+ ASSERT_EQ(android::base::StringPrintf("set_progress %f\n", .52), cmd);
+ // recovery-updater protocol expects 2 tokens ("set_progress <frac>").
+ ASSERT_EQ(2U, android::base::Split(cmd, " ").size());
+}
+
+TEST_F(UpdaterTest, show_progress) {
+ // show_progress() expects two arguments.
+ expect(nullptr, "show_progress()", kArgsParsingFailure);
+ expect(nullptr, "show_progress(\"arg1\")", kArgsParsingFailure);
+ expect(nullptr, "show_progress(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure);
+
+ // Invalid progress arguments.
+ expect(nullptr, "show_progress(\"arg1\", \"arg2\")", kArgsParsingFailure);
+ expect(nullptr, "show_progress(\"3x+5\", \"10\")", kArgsParsingFailure);
+ expect(nullptr, "show_progress(\".3\", \"5a\")", kArgsParsingFailure);
+
+ TemporaryFile tf;
+ UpdaterInfo updater_info;
+ updater_info.cmd_pipe = fdopen(tf.fd, "w");
+ expect(".52", "show_progress(\".52\", \"10\")", kNoCause, &updater_info);
+ fflush(updater_info.cmd_pipe);
+
+ std::string cmd;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &cmd));
+ ASSERT_EQ(android::base::StringPrintf("progress %f %d\n", .52, 10), cmd);
+ // recovery-updater protocol expects 3 tokens ("progress <frac> <secs>").
+ ASSERT_EQ(3U, android::base::Split(cmd, " ").size());
+}
diff --git a/tests/component/verifier_test.cpp b/tests/component/verifier_test.cpp
index 60a78f5c3..b740af96b 100644
--- a/tests/component/verifier_test.cpp
+++ b/tests/component/verifier_test.cpp
@@ -40,38 +40,44 @@
RecoveryUI* ui = NULL;
class MockUI : public RecoveryUI {
- void Init() { }
- void SetStage(int, int) { }
- void SetLocale(const char*) { }
- void SetBackground(Icon /*icon*/) { }
- void SetSystemUpdateText(bool /*security_update*/) { }
-
- void SetProgressType(ProgressType /*determinate*/) { }
- void ShowProgress(float /*portion*/, float /*seconds*/) { }
- void SetProgress(float /*fraction*/) { }
-
- void ShowText(bool /*visible*/) { }
- bool IsTextVisible() { return false; }
- bool WasTextEverVisible() { return false; }
- void Print(const char* fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- }
- void PrintOnScreenOnly(const char* fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- }
- void ShowFile(const char*) { }
-
- void StartMenu(const char* const* /*headers*/,
- const char* const* /*items*/,
- int /*initial_selection*/) { }
- int SelectMenu(int /*sel*/) { return 0; }
- void EndMenu() { }
+ bool Init(const std::string&) override {
+ return true;
+ }
+ void SetStage(int, int) override {}
+ void SetBackground(Icon /*icon*/) override {}
+ void SetSystemUpdateText(bool /*security_update*/) override {}
+
+ void SetProgressType(ProgressType /*determinate*/) override {}
+ void ShowProgress(float /*portion*/, float /*seconds*/) override {}
+ void SetProgress(float /*fraction*/) override {}
+
+ void ShowText(bool /*visible*/) override {}
+ bool IsTextVisible() override {
+ return false;
+ }
+ bool WasTextEverVisible() override {
+ return false;
+ }
+ void Print(const char* fmt, ...) override {
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+ void PrintOnScreenOnly(const char* fmt, ...) override {
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+ void ShowFile(const char*) override {}
+
+ void StartMenu(const char* const* /*headers*/, const char* const* /*items*/,
+ int /*initial_selection*/) override {}
+ int SelectMenu(int /*sel*/) override {
+ return 0;
+ }
+ void EndMenu() override {}
};
void
diff --git a/tests/manual/recovery_test.cpp b/tests/manual/recovery_test.cpp
index e83849546..d36dd331e 100644
--- a/tests/manual/recovery_test.cpp
+++ b/tests/manual/recovery_test.cpp
@@ -14,20 +14,27 @@
* limitations under the License.
*/
-#include <fcntl.h>
+#include <dirent.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <string>
+#include <vector>
#include <android-base/file.h>
+#include <android-base/strings.h>
#include <android/log.h>
#include <gtest/gtest.h>
+#include <png.h>
#include <private/android_logger.h>
+#include "minui/minui.h"
+
static const std::string myFilename = "/data/misc/recovery/inject.txt";
static const std::string myContent = "Hello World\nWelcome to my recovery\n";
+static const std::string kLocale = "zu";
+static const std::string kResourceTestDir = "/data/nativetest/recovery/";
// Failure is expected on systems that do not deliver either the
// recovery-persist or recovery-refresh executables. Tests also require
@@ -79,9 +86,139 @@ TEST(recovery, persist) {
std::string buf;
EXPECT_TRUE(android::base::ReadFileToString(myFilename, &buf));
EXPECT_EQ(myContent, buf);
- if (access(myFilename.c_str(), O_RDONLY) == 0) {
+ if (access(myFilename.c_str(), F_OK) == 0) {
fprintf(stderr, "Removing persistent test data, "
"check if reconstructed on reboot\n");
}
EXPECT_EQ(0, unlink(myFilename.c_str()));
}
+
+std::vector<std::string> image_dir {
+ "res-mdpi/images/",
+ "res-hdpi/images/",
+ "res-xhdpi/images/",
+ "res-xxhdpi/images/",
+ "res-xxxhdpi/images/"
+};
+
+static int png_filter(const dirent* de) {
+ if (de->d_type != DT_REG || !android::base::EndsWith(de->d_name, "_text.png")) {
+ return 0;
+ }
+ return 1;
+}
+
+// Find out all png files to test under /data/nativetest/recovery/.
+static std::vector<std::string> add_files() {
+ std::vector<std::string> files;
+ for (const std::string& str : image_dir) {
+ std::string dir_path = kResourceTestDir + str;
+ dirent** namelist;
+ int n = scandir(dir_path.c_str(), &namelist, png_filter, alphasort);
+ if (n == -1) {
+ printf("Failed to scan dir %s: %s\n", kResourceTestDir.c_str(), strerror(errno));
+ return files;
+ }
+ if (n == 0) {
+ printf("No file is added for test in %s\n", kResourceTestDir.c_str());
+ }
+
+ while (n--) {
+ std::string file_path = dir_path + namelist[n]->d_name;
+ files.push_back(file_path);
+ free(namelist[n]);
+ }
+ free(namelist);
+ }
+ return files;
+}
+
+class ResourceTest : public testing::TestWithParam<std::string> {
+ public:
+ static std::vector<std::string> png_list;
+
+ // Parse a png file and test if it's qualified for the background text image
+ // under recovery.
+ void SetUp() override {
+ std::string file_path = GetParam();
+ fp = fopen(file_path.c_str(), "rb");
+ ASSERT_NE(nullptr, fp);
+
+ unsigned char header[8];
+ size_t bytesRead = fread(header, 1, sizeof(header), fp);
+ ASSERT_EQ(sizeof(header), bytesRead);
+ ASSERT_EQ(0, png_sig_cmp(header, 0, sizeof(header)));
+
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
+ ASSERT_NE(nullptr, png_ptr);
+
+ info_ptr = png_create_info_struct(png_ptr);
+ ASSERT_NE(nullptr, info_ptr);
+
+ 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;
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, nullptr, nullptr,
+ nullptr);
+ ASSERT_EQ(PNG_COLOR_TYPE_GRAY, color_type) << "Recovery expects grayscale PNG file.";
+ ASSERT_LT(static_cast<png_uint_32>(5), width);
+ ASSERT_LT(static_cast<png_uint_32>(0), height);
+ if (bit_depth <= 8) {
+ // 1-, 2-, 4-, or 8-bit gray images: expand to 8-bit gray.
+ png_set_expand_gray_1_2_4_to_8(png_ptr);
+ }
+
+ png_byte channels = png_get_channels(png_ptr, info_ptr);
+ ASSERT_EQ(1, channels) << "Recovery background text images expects 1-channel PNG file.";
+ }
+
+ void TearDown() override {
+ if (png_ptr != nullptr && info_ptr != nullptr) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
+ }
+
+ if (fp != nullptr) {
+ fclose(fp);
+ }
+ }
+
+ protected:
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_uint_32 width, height;
+
+ FILE* fp;
+};
+
+std::vector<std::string> ResourceTest::png_list = add_files();
+
+TEST_P(ResourceTest, ValidateLocale) {
+ std::vector<unsigned char> row(width);
+ for (png_uint_32 y = 0; y < height; ++y) {
+ png_read_row(png_ptr, row.data(), nullptr);
+ int w = (row[1] << 8) | row[0];
+ int h = (row[3] << 8) | row[2];
+ int len = row[4];
+ EXPECT_LT(0, w);
+ EXPECT_LT(0, h);
+ EXPECT_LT(0, len) << "Locale string should be non-empty.";
+ EXPECT_NE(0, row[5]) << "Locale string is missing.";
+
+ ASSERT_GT(height, y + 1 + h) << "Locale: " << kLocale << " is not found in the file.";
+ char* loc = reinterpret_cast<char*>(&row[5]);
+ if (matches_locale(loc, kLocale.c_str())) {
+ EXPECT_TRUE(android::base::StartsWith(loc, kLocale.c_str()));
+ break;
+ } else {
+ for (int i = 0; i < h; ++i, ++y) {
+ png_read_row(png_ptr, row.data(), nullptr);
+ }
+ }
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(BackgroundTextValidation, ResourceTest,
+ ::testing::ValuesIn(ResourceTest::png_list.cbegin(),
+ ResourceTest::png_list.cend()));
diff --git a/tests/unit/dirutil_test.cpp b/tests/unit/dirutil_test.cpp
new file mode 100644
index 000000000..5e2ae4fb5
--- /dev/null
+++ b/tests/unit/dirutil_test.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+#include <otautil/DirUtil.h>
+
+TEST(DirUtilTest, create_invalid) {
+ // Requesting to create an empty dir is invalid.
+ ASSERT_EQ(-1, dirCreateHierarchy("", 0755, nullptr, false, nullptr));
+ ASSERT_EQ(ENOENT, errno);
+
+ // Requesting to strip the name with no slash present.
+ ASSERT_EQ(-1, dirCreateHierarchy("abc", 0755, nullptr, true, nullptr));
+ ASSERT_EQ(ENOENT, errno);
+
+ // Creating a dir that already exists.
+ TemporaryDir td;
+ ASSERT_EQ(0, dirCreateHierarchy(td.path, 0755, nullptr, false, nullptr));
+
+ // "///" is a valid dir.
+ ASSERT_EQ(0, dirCreateHierarchy("///", 0755, nullptr, false, nullptr));
+
+ // Request to create a dir, but a file with the same name already exists.
+ TemporaryFile tf;
+ ASSERT_EQ(-1, dirCreateHierarchy(tf.path, 0755, nullptr, false, nullptr));
+ ASSERT_EQ(ENOTDIR, errno);
+}
+
+TEST(DirUtilTest, create_smoke) {
+ TemporaryDir td;
+ std::string prefix(td.path);
+ std::string path = prefix + "/a/b";
+ constexpr mode_t mode = 0755;
+ ASSERT_EQ(0, dirCreateHierarchy(path.c_str(), mode, nullptr, false, nullptr));
+
+ // Verify.
+ struct stat sb;
+ ASSERT_EQ(0, stat(path.c_str(), &sb)) << strerror(errno);
+ ASSERT_TRUE(S_ISDIR(sb.st_mode));
+ constexpr mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO;
+ ASSERT_EQ(mode, sb.st_mode & mask);
+
+ // Clean up.
+ ASSERT_EQ(0, rmdir((prefix + "/a/b").c_str()));
+ ASSERT_EQ(0, rmdir((prefix + "/a").c_str()));
+}
+
+TEST(DirUtilTest, create_strip_filename) {
+ TemporaryDir td;
+ std::string prefix(td.path);
+ std::string path = prefix + "/a/b";
+ ASSERT_EQ(0, dirCreateHierarchy(path.c_str(), 0755, nullptr, true, nullptr));
+
+ // Verify that "../a" exists but not "../a/b".
+ struct stat sb;
+ ASSERT_EQ(0, stat((prefix + "/a").c_str(), &sb)) << strerror(errno);
+ ASSERT_TRUE(S_ISDIR(sb.st_mode));
+
+ ASSERT_EQ(-1, stat(path.c_str(), &sb));
+ ASSERT_EQ(ENOENT, errno);
+
+ // Clean up.
+ ASSERT_EQ(0, rmdir((prefix + "/a").c_str()));
+}
+
+TEST(DirUtilTest, create_mode_and_timestamp) {
+ TemporaryDir td;
+ std::string prefix(td.path);
+ std::string path = prefix + "/a/b";
+ // Set the timestamp to 8/1/2008.
+ constexpr struct utimbuf timestamp = { 1217592000, 1217592000 };
+ constexpr mode_t mode = 0751;
+ ASSERT_EQ(0, dirCreateHierarchy(path.c_str(), mode, &timestamp, false, nullptr));
+
+ // Verify the mode and timestamp for "../a/b".
+ struct stat sb;
+ ASSERT_EQ(0, stat(path.c_str(), &sb)) << strerror(errno);
+ ASSERT_TRUE(S_ISDIR(sb.st_mode));
+ constexpr mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO;
+ ASSERT_EQ(mode, sb.st_mode & mask);
+
+ timespec time;
+ time.tv_sec = 1217592000;
+ time.tv_nsec = 0;
+
+ ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_atime));
+ ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_mtime));
+
+ // Verify the mode for "../a". Note that the timestamp for intermediate directories (e.g. "../a")
+ // may not be 'timestamp' according to the current implementation.
+ ASSERT_EQ(0, stat((prefix + "/a").c_str(), &sb)) << strerror(errno);
+ ASSERT_TRUE(S_ISDIR(sb.st_mode));
+ ASSERT_EQ(mode, sb.st_mode & mask);
+
+ // Clean up.
+ ASSERT_EQ(0, rmdir((prefix + "/a/b").c_str()));
+ ASSERT_EQ(0, rmdir((prefix + "/a").c_str()));
+}
+
+TEST(DirUtilTest, unlink_invalid) {
+ // File doesn't exist.
+ ASSERT_EQ(-1, dirUnlinkHierarchy("doesntexist"));
+
+ // Nonexistent directory.
+ TemporaryDir td;
+ std::string path(td.path);
+ ASSERT_EQ(-1, dirUnlinkHierarchy((path + "/a").c_str()));
+ ASSERT_EQ(ENOENT, errno);
+}
+
+TEST(DirUtilTest, unlink_smoke) {
+ // Unlink a file.
+ TemporaryFile tf;
+ ASSERT_EQ(0, dirUnlinkHierarchy(tf.path));
+ ASSERT_EQ(-1, access(tf.path, F_OK));
+
+ TemporaryDir td;
+ std::string path(td.path);
+ constexpr mode_t mode = 0700;
+ ASSERT_EQ(0, mkdir((path + "/a").c_str(), mode));
+ ASSERT_EQ(0, mkdir((path + "/a/b").c_str(), mode));
+ ASSERT_EQ(0, mkdir((path + "/a/b/c").c_str(), mode));
+ ASSERT_EQ(0, mkdir((path + "/a/d").c_str(), mode));
+
+ // Remove "../a" recursively.
+ ASSERT_EQ(0, dirUnlinkHierarchy((path + "/a").c_str()));
+
+ // Verify it's gone.
+ ASSERT_EQ(-1, access((path + "/a").c_str(), F_OK));
+}
diff --git a/tests/unit/locale_test.cpp b/tests/unit/locale_test.cpp
index 0e515f8c1..f73235005 100644
--- a/tests/unit/locale_test.cpp
+++ b/tests/unit/locale_test.cpp
@@ -26,4 +26,7 @@ TEST(LocaleTest, Misc) {
EXPECT_TRUE(matches_locale("en", "en_GB"));
EXPECT_FALSE(matches_locale("en_GB", "en"));
EXPECT_FALSE(matches_locale("en_GB", "en_US"));
+ EXPECT_FALSE(matches_locale("en_US", ""));
+ // Empty locale prefix in the PNG file will match the input locale.
+ EXPECT_TRUE(matches_locale("", "en_US"));
}
diff --git a/tests/unit/zip_test.cpp b/tests/unit/zip_test.cpp
index ef0ee4c1d..4a1a49b97 100644
--- a/tests/unit/zip_test.cpp
+++ b/tests/unit/zip_test.cpp
@@ -15,7 +15,6 @@
*/
#include <errno.h>
-#include <fcntl.h>
#include <unistd.h>
#include <memory>
@@ -42,10 +41,10 @@ TEST(ZipTest, ExtractPackageRecursive) {
// Make sure all the files are extracted correctly.
std::string path(td.path);
- ASSERT_EQ(0, access((path + "/a.txt").c_str(), O_RDONLY));
- ASSERT_EQ(0, access((path + "/b.txt").c_str(), O_RDONLY));
- ASSERT_EQ(0, access((path + "/b/c.txt").c_str(), O_RDONLY));
- ASSERT_EQ(0, access((path + "/b/d.txt").c_str(), O_RDONLY));
+ ASSERT_EQ(0, access((path + "/a.txt").c_str(), F_OK));
+ ASSERT_EQ(0, access((path + "/b.txt").c_str(), F_OK));
+ ASSERT_EQ(0, access((path + "/b/c.txt").c_str(), F_OK));
+ ASSERT_EQ(0, access((path + "/b/d.txt").c_str(), F_OK));
// The content of the file is the same as expected.
std::string content1;
@@ -54,7 +53,16 @@ TEST(ZipTest, ExtractPackageRecursive) {
std::string content2;
ASSERT_TRUE(android::base::ReadFileToString(path + "/b/d.txt", &content2));
- ASSERT_EQ(kBTxtContents, content2);
+ ASSERT_EQ(kDTxtContents, content2);
+
+ CloseArchive(handle);
+
+ // Clean up.
+ ASSERT_EQ(0, unlink((path + "/a.txt").c_str()));
+ ASSERT_EQ(0, unlink((path + "/b.txt").c_str()));
+ ASSERT_EQ(0, unlink((path + "/b/c.txt").c_str()));
+ ASSERT_EQ(0, unlink((path + "/b/d.txt").c_str()));
+ ASSERT_EQ(0, rmdir((path + "/b").c_str()));
}
TEST(ZipTest, OpenFromMemory) {
@@ -76,6 +84,7 @@ TEST(ZipTest, OpenFromMemory) {
ASSERT_NE(-1, tmp_binary.fd);
ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd));
+ CloseArchive(handle);
sysReleaseMap(&map);
}
diff --git a/tests/unit/ziputil_test.cpp b/tests/unit/ziputil_test.cpp
new file mode 100644
index 000000000..14e541690
--- /dev/null
+++ b/tests/unit/ziputil_test.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+#include <otautil/ZipUtil.h>
+#include <ziparchive/zip_archive.h>
+
+#include "common/test_constants.h"
+
+TEST(ZipUtilTest, invalid_args) {
+ std::string zip_path = from_testdata_base("ziptest_valid.zip");
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
+
+ // zip_path must be a relative path.
+ ASSERT_FALSE(ExtractPackageRecursive(handle, "/a/b", "/tmp", nullptr, nullptr));
+
+ // dest_path must be an absolute path.
+ ASSERT_FALSE(ExtractPackageRecursive(handle, "a/b", "tmp", nullptr, nullptr));
+ ASSERT_FALSE(ExtractPackageRecursive(handle, "a/b", "", nullptr, nullptr));
+
+ CloseArchive(handle);
+}
+
+TEST(ZipUtilTest, extract_all) {
+ std::string zip_path = from_testdata_base("ziptest_valid.zip");
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
+
+ // Extract the whole package into a temp directory.
+ TemporaryDir td;
+ ExtractPackageRecursive(handle, "", td.path, nullptr, nullptr);
+
+ // Make sure all the files are extracted correctly.
+ std::string path(td.path);
+ ASSERT_EQ(0, access((path + "/a.txt").c_str(), F_OK));
+ ASSERT_EQ(0, access((path + "/b.txt").c_str(), F_OK));
+ ASSERT_EQ(0, access((path + "/b/c.txt").c_str(), F_OK));
+ ASSERT_EQ(0, access((path + "/b/d.txt").c_str(), F_OK));
+
+ // The content of the file is the same as expected.
+ std::string content1;
+ ASSERT_TRUE(android::base::ReadFileToString(path + "/a.txt", &content1));
+ ASSERT_EQ(kATxtContents, content1);
+
+ std::string content2;
+ ASSERT_TRUE(android::base::ReadFileToString(path + "/b/d.txt", &content2));
+ ASSERT_EQ(kDTxtContents, content2);
+
+ // Clean up the temp files under td.
+ ASSERT_EQ(0, unlink((path + "/a.txt").c_str()));
+ ASSERT_EQ(0, unlink((path + "/b.txt").c_str()));
+ ASSERT_EQ(0, unlink((path + "/b/c.txt").c_str()));
+ ASSERT_EQ(0, unlink((path + "/b/d.txt").c_str()));
+ ASSERT_EQ(0, rmdir((path + "/b").c_str()));
+
+ CloseArchive(handle);
+}
+
+TEST(ZipUtilTest, extract_prefix_with_slash) {
+ std::string zip_path = from_testdata_base("ziptest_valid.zip");
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
+
+ // Extract all the entries starting with "b/".
+ TemporaryDir td;
+ ExtractPackageRecursive(handle, "b/", td.path, nullptr, nullptr);
+
+ // Make sure all the files with "b/" prefix are extracted correctly.
+ std::string path(td.path);
+ ASSERT_EQ(0, access((path + "/c.txt").c_str(), F_OK));
+ ASSERT_EQ(0, access((path + "/d.txt").c_str(), F_OK));
+
+ // And the rest are not extracted.
+ ASSERT_EQ(-1, access((path + "/a.txt").c_str(), F_OK));
+ ASSERT_EQ(ENOENT, errno);
+ ASSERT_EQ(-1, access((path + "/b.txt").c_str(), F_OK));
+ ASSERT_EQ(ENOENT, errno);
+
+ // The content of the file is the same as expected.
+ std::string content1;
+ ASSERT_TRUE(android::base::ReadFileToString(path + "/c.txt", &content1));
+ ASSERT_EQ(kCTxtContents, content1);
+
+ std::string content2;
+ ASSERT_TRUE(android::base::ReadFileToString(path + "/d.txt", &content2));
+ ASSERT_EQ(kDTxtContents, content2);
+
+ // Clean up the temp files under td.
+ ASSERT_EQ(0, unlink((path + "/c.txt").c_str()));
+ ASSERT_EQ(0, unlink((path + "/d.txt").c_str()));
+
+ CloseArchive(handle);
+}
+
+TEST(ZipUtilTest, extract_prefix_without_slash) {
+ std::string zip_path = from_testdata_base("ziptest_valid.zip");
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
+
+ // Extract all the file entries starting with "b/".
+ TemporaryDir td;
+ ExtractPackageRecursive(handle, "b", td.path, nullptr, nullptr);
+
+ // Make sure all the files with "b/" prefix are extracted correctly.
+ std::string path(td.path);
+ ASSERT_EQ(0, access((path + "/c.txt").c_str(), F_OK));
+ ASSERT_EQ(0, access((path + "/d.txt").c_str(), F_OK));
+
+ // And the rest are not extracted.
+ ASSERT_EQ(-1, access((path + "/a.txt").c_str(), F_OK));
+ ASSERT_EQ(ENOENT, errno);
+ ASSERT_EQ(-1, access((path + "/b.txt").c_str(), F_OK));
+ ASSERT_EQ(ENOENT, errno);
+
+ // The content of the file is the same as expected.
+ std::string content1;
+ ASSERT_TRUE(android::base::ReadFileToString(path + "/c.txt", &content1));
+ ASSERT_EQ(kCTxtContents, content1);
+
+ std::string content2;
+ ASSERT_TRUE(android::base::ReadFileToString(path + "/d.txt", &content2));
+ ASSERT_EQ(kDTxtContents, content2);
+
+ // Clean up the temp files under td.
+ ASSERT_EQ(0, unlink((path + "/c.txt").c_str()));
+ ASSERT_EQ(0, unlink((path + "/d.txt").c_str()));
+
+ CloseArchive(handle);
+}
+
+TEST(ZipUtilTest, set_timestamp) {
+ std::string zip_path = from_testdata_base("ziptest_valid.zip");
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
+
+ // Set the timestamp to 8/1/2008.
+ constexpr struct utimbuf timestamp = { 1217592000, 1217592000 };
+
+ // Extract all the entries starting with "b/".
+ TemporaryDir td;
+ ExtractPackageRecursive(handle, "b", td.path, &timestamp, nullptr);
+
+ // Make sure all the files with "b/" prefix are extracted correctly.
+ std::string path(td.path);
+ std::string file_c = path + "/c.txt";
+ std::string file_d = path + "/d.txt";
+ ASSERT_EQ(0, access(file_c.c_str(), F_OK));
+ ASSERT_EQ(0, access(file_d.c_str(), F_OK));
+
+ // Verify the timestamp.
+ timespec time;
+ time.tv_sec = 1217592000;
+ time.tv_nsec = 0;
+
+ struct stat sb;
+ ASSERT_EQ(0, stat(file_c.c_str(), &sb)) << strerror(errno);
+ ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_atime));
+ ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_mtime));
+
+ ASSERT_EQ(0, stat(file_d.c_str(), &sb)) << strerror(errno);
+ ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_atime));
+ ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_mtime));
+
+ // Clean up the temp files under td.
+ ASSERT_EQ(0, unlink(file_c.c_str()));
+ ASSERT_EQ(0, unlink(file_d.c_str()));
+
+ CloseArchive(handle);
+}
diff --git a/tools/recovery_l10n/README.md b/tools/recovery_l10n/README.md
index 1554f6618..e9e85d65f 100644
--- a/tools/recovery_l10n/README.md
+++ b/tools/recovery_l10n/README.md
@@ -8,6 +8,9 @@
* For example, we can use Nexus 5 to generate the text images under
res-xxhdpi.
+ * We can set up the maximum width of the final png image in res/layout/main.xml
+ Currently, the image width is 1200px for xxxhdpi, 900px for xxhdpi and
+ 480px for xhdpi/hdpi/mdpi.
* When using the emulator, make sure the NDK version matches the current
repository. Otherwise, the app may not work properly.
diff --git a/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java b/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java
index ac94bde1c..30d45f6ae 100644
--- a/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java
+++ b/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java
@@ -38,6 +38,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.Locale;
@@ -148,11 +149,28 @@ public class Main extends Activity {
mText = (TextView) findViewById(R.id.text);
String[] localeNames = getAssets().getLocales();
- Arrays.sort(localeNames);
+ Arrays.sort(localeNames, new Comparator<String>() {
+ // Override the string comparator so that en is sorted behind en_US.
+ // As a result, en_US will be matched first in recovery.
+ @Override
+ public int compare(String s1, String s2) {
+ if (s1.equals(s2)) {
+ return 0;
+ } else if (s1.startsWith(s2)) {
+ return -1;
+ } else if (s2.startsWith(s1)) {
+ return 1;
+ }
+ return s1.compareTo(s2);
+ }
+ });
+
ArrayList<Locale> locales = new ArrayList<Locale>();
for (String localeName : localeNames) {
Log.i(TAG, "locale = " + localeName);
- locales.add(Locale.forLanguageTag(localeName));
+ if (!localeName.isEmpty()) {
+ locales.add(Locale.forLanguageTag(localeName));
+ }
}
final Runnable seq = buildSequence(locales.toArray(new Locale[0]));
diff --git a/ui.cpp b/ui.cpp
index 78b6e4f1b..5efdc5a48 100644
--- a/ui.cpp
+++ b/ui.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "ui.h"
+
#include <errno.h>
#include <fcntl.h>
#include <linux/input.h>
@@ -28,32 +30,43 @@
#include <time.h>
#include <unistd.h>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
#include <android-base/properties.h>
+#include <android-base/strings.h>
#include <cutils/android_reboot.h>
+#include <minui/minui.h>
#include "common.h"
#include "roots.h"
#include "device.h"
-#include "minui/minui.h"
-#include "screen_ui.h"
-#include "ui.h"
-#define UI_WAIT_KEY_TIMEOUT_SEC 120
+static constexpr int UI_WAIT_KEY_TIMEOUT_SEC = 120;
+static constexpr const char* BRIGHTNESS_FILE = "/sys/class/leds/lcd-backlight/brightness";
+static constexpr const char* MAX_BRIGHTNESS_FILE = "/sys/class/leds/lcd-backlight/max_brightness";
RecoveryUI::RecoveryUI()
- : key_queue_len(0),
- key_last_down(-1),
- key_long_press(false),
- key_down_count(0),
- enable_reboot(true),
- consecutive_power_keys(0),
- last_key(-1),
- has_power_key(false),
- has_up_key(false),
- has_down_key(false) {
- pthread_mutex_init(&key_queue_mutex, nullptr);
- pthread_cond_init(&key_queue_cond, nullptr);
- memset(key_pressed, 0, sizeof(key_pressed));
+ : locale_(""),
+ rtl_locale_(false),
+ brightness_normal_(50),
+ brightness_dimmed_(25),
+ key_queue_len(0),
+ key_last_down(-1),
+ key_long_press(false),
+ key_down_count(0),
+ enable_reboot(true),
+ consecutive_power_keys(0),
+ last_key(-1),
+ has_power_key(false),
+ has_up_key(false),
+ has_down_key(false),
+ screensaver_state_(ScreensaverState::DISABLED) {
+ pthread_mutex_init(&key_queue_mutex, nullptr);
+ pthread_cond_init(&key_queue_cond, nullptr);
+ memset(key_pressed, 0, sizeof(key_pressed));
}
void RecoveryUI::OnKeyDetected(int key_code) {
@@ -66,10 +79,6 @@ void RecoveryUI::OnKeyDetected(int key_code) {
}
}
-int RecoveryUI::InputCallback(int fd, uint32_t epevents, void* data) {
- return reinterpret_cast<RecoveryUI*>(data)->OnInputEvent(fd, epevents);
-}
-
// Reads input events, handles special hot keys, and adds to the key queue.
static void* InputThreadLoop(void*) {
while (true) {
@@ -80,12 +89,54 @@ static void* InputThreadLoop(void*) {
return nullptr;
}
-void RecoveryUI::Init() {
- ev_init(InputCallback, this);
+bool RecoveryUI::InitScreensaver() {
+ // Disabled.
+ if (brightness_normal_ == 0 || brightness_dimmed_ > brightness_normal_) {
+ return false;
+ }
+
+ // Set the initial brightness level based on the max brightness. Note that reading the initial
+ // value from BRIGHTNESS_FILE doesn't give the actual brightness value (bullhead, sailfish), so
+ // we don't have a good way to query the default value.
+ std::string content;
+ if (!android::base::ReadFileToString(MAX_BRIGHTNESS_FILE, &content)) {
+ LOG(WARNING) << "Failed to read max brightness: " << content;
+ return false;
+ }
+
+ unsigned int max_value;
+ if (!android::base::ParseUint(android::base::Trim(content), &max_value)) {
+ LOG(WARNING) << "Failed to parse max brightness: " << content;
+ return false;
+ }
+
+ brightness_normal_value_ = max_value * brightness_normal_ / 100.0;
+ brightness_dimmed_value_ = max_value * brightness_dimmed_ / 100.0;
+ if (!android::base::WriteStringToFile(std::to_string(brightness_normal_value_),
+ BRIGHTNESS_FILE)) {
+ PLOG(WARNING) << "Failed to set brightness";
+ return false;
+ }
+
+ LOG(INFO) << "Brightness: " << brightness_normal_value_ << " (" << brightness_normal_ << "%)";
+ screensaver_state_ = ScreensaverState::NORMAL;
+ return true;
+}
+
+bool RecoveryUI::Init(const std::string& locale) {
+ // Set up the locale info.
+ SetLocale(locale);
+
+ ev_init(std::bind(&RecoveryUI::OnInputEvent, this, std::placeholders::_1, std::placeholders::_2));
- ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));
+ ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));
- pthread_create(&input_thread_, nullptr, InputThreadLoop, nullptr);
+ if (!InitScreensaver()) {
+ LOG(INFO) << "Screensaver disabled";
+ }
+
+ pthread_create(&input_thread_, nullptr, InputThreadLoop, nullptr);
+ return true;
}
int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) {
@@ -216,31 +267,65 @@ void RecoveryUI::EnqueueKey(int key_code) {
}
int RecoveryUI::WaitKey() {
- pthread_mutex_lock(&key_queue_mutex);
+ pthread_mutex_lock(&key_queue_mutex);
+
+ // Time out after UI_WAIT_KEY_TIMEOUT_SEC, unless a USB cable is
+ // plugged in.
+ do {
+ struct timeval now;
+ struct timespec timeout;
+ gettimeofday(&now, nullptr);
+ timeout.tv_sec = now.tv_sec;
+ timeout.tv_nsec = now.tv_usec * 1000;
+ timeout.tv_sec += UI_WAIT_KEY_TIMEOUT_SEC;
+
+ int rc = 0;
+ while (key_queue_len == 0 && rc != ETIMEDOUT) {
+ rc = pthread_cond_timedwait(&key_queue_cond, &key_queue_mutex, &timeout);
+ }
- // Time out after UI_WAIT_KEY_TIMEOUT_SEC, unless a USB cable is
- // plugged in.
- do {
- struct timeval now;
- struct timespec timeout;
- gettimeofday(&now, nullptr);
- timeout.tv_sec = now.tv_sec;
- timeout.tv_nsec = now.tv_usec * 1000;
- timeout.tv_sec += UI_WAIT_KEY_TIMEOUT_SEC;
-
- int rc = 0;
- while (key_queue_len == 0 && rc != ETIMEDOUT) {
- rc = pthread_cond_timedwait(&key_queue_cond, &key_queue_mutex, &timeout);
+ if (screensaver_state_ != ScreensaverState::DISABLED) {
+ if (rc == ETIMEDOUT) {
+ // Lower the brightness level: NORMAL -> DIMMED; DIMMED -> OFF.
+ if (screensaver_state_ == ScreensaverState::NORMAL) {
+ if (android::base::WriteStringToFile(std::to_string(brightness_dimmed_value_),
+ BRIGHTNESS_FILE)) {
+ LOG(INFO) << "Brightness: " << brightness_dimmed_value_ << " (" << brightness_dimmed_
+ << "%)";
+ screensaver_state_ = ScreensaverState::DIMMED;
+ }
+ } else if (screensaver_state_ == ScreensaverState::DIMMED) {
+ if (android::base::WriteStringToFile("0", BRIGHTNESS_FILE)) {
+ LOG(INFO) << "Brightness: 0 (off)";
+ screensaver_state_ = ScreensaverState::OFF;
+ }
+ }
+ } else if (screensaver_state_ != ScreensaverState::NORMAL) {
+ // Drop the first key if it's changing from OFF to NORMAL.
+ if (screensaver_state_ == ScreensaverState::OFF) {
+ if (key_queue_len > 0) {
+ memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len);
+ }
}
- } while (IsUsbConnected() && key_queue_len == 0);
- int key = -1;
- if (key_queue_len > 0) {
- key = key_queue[0];
- memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len);
+ // Reset the brightness to normal.
+ if (android::base::WriteStringToFile(std::to_string(brightness_normal_value_),
+ BRIGHTNESS_FILE)) {
+ screensaver_state_ = ScreensaverState::NORMAL;
+ LOG(INFO) << "Brightness: " << brightness_normal_value_ << " (" << brightness_normal_
+ << "%)";
+ }
+ }
}
- pthread_mutex_unlock(&key_queue_mutex);
- return key;
+ } while (IsUsbConnected() && key_queue_len == 0);
+
+ int key = -1;
+ if (key_queue_len > 0) {
+ key = key_queue[0];
+ memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len);
+ }
+ pthread_mutex_unlock(&key_queue_mutex);
+ return key;
}
bool RecoveryUI::IsUsbConnected() {
@@ -326,7 +411,7 @@ RecoveryUI::KeyAction RecoveryUI::CheckKey(int key, bool is_long_press) {
}
last_key = key;
- return IsTextVisible() ? ENQUEUE : IGNORE;
+ return (IsTextVisible() || screensaver_state_ == ScreensaverState::OFF) ? ENQUEUE : IGNORE;
}
void RecoveryUI::KeyLongPress(int) {
@@ -337,3 +422,23 @@ void RecoveryUI::SetEnableReboot(bool enabled) {
enable_reboot = enabled;
pthread_mutex_unlock(&key_queue_mutex);
}
+
+void RecoveryUI::SetLocale(const std::string& new_locale) {
+ this->locale_ = new_locale;
+ this->rtl_locale_ = false;
+
+ if (!new_locale.empty()) {
+ size_t underscore = new_locale.find('_');
+ // lang has the language prefix prior to '_', or full string if '_' doesn't exist.
+ std::string lang = new_locale.substr(0, underscore);
+
+ // A bit cheesy: keep an explicit list of supported RTL languages.
+ if (lang == "ar" || // Arabic
+ lang == "fa" || // Persian (Farsi)
+ lang == "he" || // Hebrew (new language code)
+ lang == "iw" || // Hebrew (old language code)
+ lang == "ur") { // Urdu
+ rtl_locale_ = true;
+ }
+ }
+}
diff --git a/ui.h b/ui.h
index 82d95a346..823eb6574 100644
--- a/ui.h
+++ b/ui.h
@@ -21,6 +21,8 @@
#include <pthread.h>
#include <time.h>
+#include <string>
+
// Abstract class for controlling the user interface during recovery.
class RecoveryUI {
public:
@@ -28,14 +30,13 @@ class RecoveryUI {
virtual ~RecoveryUI() { }
- // Initialize the object; called before anything else.
- virtual void Init();
+ // Initialize the object; called before anything else. UI texts will be
+ // initialized according to the given locale. Returns true on success.
+ virtual bool Init(const std::string& locale);
+
// Show a stage indicator. Call immediately after Init().
virtual void SetStage(int current, int max) = 0;
- // After calling Init(), you can tell the UI what locale it is operating in.
- virtual void SetLocale(const char* locale) = 0;
-
// Set the overall recovery state ("background image").
enum Icon { NONE, INSTALLING_UPDATE, ERASING, NO_COMMAND, ERROR };
virtual void SetBackground(Icon icon) = 0;
@@ -122,10 +123,21 @@ class RecoveryUI {
// statements will be displayed.
virtual void EndMenu() = 0;
-protected:
+ protected:
void EnqueueKey(int key_code);
-private:
+ // The locale that's used to show the rendered texts.
+ std::string locale_;
+ bool rtl_locale_;
+
+ // The normal and dimmed brightness percentages (default: 50 and 25, which means 50% and 25%
+ // of the max_brightness). Because the absolute values may vary across devices. These two
+ // values can be configured via subclassing. Setting brightness_normal_ to 0 to disable
+ // screensaver.
+ unsigned int brightness_normal_;
+ unsigned int brightness_dimmed_;
+
+ private:
// Key event input queue
pthread_mutex_t key_queue_mutex;
pthread_cond_t key_queue_cond;
@@ -153,8 +165,6 @@ private:
pthread_t input_thread_;
void OnKeyDetected(int key_code);
-
- static int InputCallback(int fd, uint32_t epevents, void* data);
int OnInputEvent(int fd, uint32_t epevents);
void ProcessKey(int key_code, int updown);
@@ -162,6 +172,16 @@ private:
static void* time_key_helper(void* cookie);
void time_key(int key_code, int count);
+
+ void SetLocale(const std::string&);
+
+ enum class ScreensaverState { DISABLED, NORMAL, DIMMED, OFF };
+ ScreensaverState screensaver_state_;
+ // The following two contain the absolute values computed from brightness_normal_ and
+ // brightness_dimmed_ respectively.
+ unsigned int brightness_normal_value_;
+ unsigned int brightness_dimmed_value_;
+ bool InitScreensaver();
};
#endif // RECOVERY_UI_H
diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp
index e1b6a1c20..a06384dd5 100644
--- a/uncrypt/uncrypt.cpp
+++ b/uncrypt/uncrypt.cpp
@@ -118,7 +118,8 @@
#include "error_code.h"
-#define WINDOW_SIZE 5
+static constexpr int WINDOW_SIZE = 5;
+static constexpr int FIBMAP_RETRY_LIMIT = 3;
// uncrypt provides three services: SETUP_BCB, CLEAR_BCB and UNCRYPT.
//
@@ -233,6 +234,26 @@ static bool find_uncrypt_package(const std::string& uncrypt_path_file, std::stri
return true;
}
+static int retry_fibmap(const int fd, const char* name, int* block, const int head_block) {
+ CHECK(block != nullptr);
+ for (size_t i = 0; i < FIBMAP_RETRY_LIMIT; i++) {
+ if (fsync(fd) == -1) {
+ PLOG(ERROR) << "failed to fsync \"" << name << "\"";
+ return kUncryptFileSyncError;
+ }
+ if (ioctl(fd, FIBMAP, block) != 0) {
+ PLOG(ERROR) << "failed to find block " << head_block;
+ return kUncryptIoctlError;
+ }
+ if (*block != 0) {
+ return kUncryptNoError;
+ }
+ sleep(1);
+ }
+ LOG(ERROR) << "fibmap of " << head_block << "always returns 0";
+ return kUncryptIoctlError;
+}
+
static int produce_block_map(const char* path, const char* map_file, const char* blk_dev,
bool encrypted, int socket) {
std::string err;
@@ -314,6 +335,15 @@ static int produce_block_map(const char* path, const char* map_file, const char*
PLOG(ERROR) << "failed to find block " << head_block;
return kUncryptIoctlError;
}
+
+ if (block == 0) {
+ LOG(ERROR) << "failed to find block " << head_block << ", retrying";
+ int error = retry_fibmap(fd, path, &block, head_block);
+ if (error != kUncryptNoError) {
+ return error;
+ }
+ }
+
add_block_to_ranges(ranges, block);
if (encrypted) {
if (write_at_offset(buffers[head].data(), sb.st_blksize, wfd,
@@ -350,6 +380,15 @@ static int produce_block_map(const char* path, const char* map_file, const char*
PLOG(ERROR) << "failed to find block " << head_block;
return kUncryptIoctlError;
}
+
+ if (block == 0) {
+ LOG(ERROR) << "failed to find block " << head_block << ", retrying";
+ int error = retry_fibmap(fd, path, &block, head_block);
+ if (error != kUncryptNoError) {
+ return error;
+ }
+ }
+
add_block_to_ranges(ranges, block);
if (encrypted) {
if (write_at_offset(buffers[head].data(), sb.st_blksize, wfd,
@@ -457,22 +496,23 @@ static int uncrypt(const char* input_path, const char* map_file, const int socke
return 0;
}
-static bool uncrypt_wrapper(const char* input_path, const char* map_file, const int socket) {
- // Initialize the uncrypt error to kUncryptErrorHolder.
+static void log_uncrypt_error_code(UncryptErrorCode error_code) {
if (!android::base::WriteStringToFile(android::base::StringPrintf(
- "uncrypt_error: %d\n", kUncryptErrorHolder), UNCRYPT_STATUS)) {
+ "uncrypt_error: %d\n", error_code), UNCRYPT_STATUS)) {
PLOG(WARNING) << "failed to write to " << UNCRYPT_STATUS;
}
+}
+
+static bool uncrypt_wrapper(const char* input_path, const char* map_file, const int socket) {
+ // Initialize the uncrypt error to kUncryptErrorPlaceholder.
+ log_uncrypt_error_code(kUncryptErrorPlaceholder);
std::string package;
if (input_path == nullptr) {
if (!find_uncrypt_package(UNCRYPT_PATH_FILE, &package)) {
write_status_to_socket(-1, socket);
// Overwrite the error message.
- if (!android::base::WriteStringToFile(android::base::StringPrintf(
- "uncrypt_error: %d\n", kUncryptPackageMissingError), UNCRYPT_STATUS)) {
- PLOG(WARNING) << "failed to write to " << UNCRYPT_STATUS;
- }
+ log_uncrypt_error_code(kUncryptPackageMissingError);
return false;
}
input_path = package.c_str();
@@ -529,7 +569,7 @@ static bool setup_bcb(const int socket) {
std::string content;
content.resize(length);
if (!android::base::ReadFully(socket, &content[0], length)) {
- PLOG(ERROR) << "failed to read the length";
+ PLOG(ERROR) << "failed to read the message";
return false;
}
LOG(INFO) << " received command: [" << content << "] (" << content.size() << ")";
@@ -591,10 +631,7 @@ int main(int argc, char** argv) {
}
if ((fstab = read_fstab()) == nullptr) {
- if (!android::base::WriteStringToFile(android::base::StringPrintf(
- "uncrypt_error: %d\n", kUncryptFstabReadError), UNCRYPT_STATUS)) {
- PLOG(WARNING) << "failed to write to " << UNCRYPT_STATUS;
- }
+ log_uncrypt_error_code(kUncryptFstabReadError);
return 1;
}
@@ -614,30 +651,21 @@ int main(int argc, char** argv) {
android::base::unique_fd service_socket(android_get_control_socket(UNCRYPT_SOCKET.c_str()));
if (service_socket == -1) {
PLOG(ERROR) << "failed to open socket \"" << UNCRYPT_SOCKET << "\"";
- if (!android::base::WriteStringToFile(android::base::StringPrintf(
- "uncrypt_error: %d\n", kUncryptSocketOpenError), UNCRYPT_STATUS)) {
- PLOG(WARNING) << "failed to write to " << UNCRYPT_STATUS;
- }
+ log_uncrypt_error_code(kUncryptSocketOpenError);
return 1;
}
fcntl(service_socket, F_SETFD, FD_CLOEXEC);
if (listen(service_socket, 1) == -1) {
PLOG(ERROR) << "failed to listen on socket " << service_socket.get();
- if (!android::base::WriteStringToFile(android::base::StringPrintf(
- "uncrypt_error: %d\n", kUncryptSocketListenError), UNCRYPT_STATUS)) {
- PLOG(WARNING) << "failed to write to " << UNCRYPT_STATUS;
- }
+ log_uncrypt_error_code(kUncryptSocketListenError);
return 1;
}
android::base::unique_fd socket_fd(accept4(service_socket, nullptr, nullptr, SOCK_CLOEXEC));
if (socket_fd == -1) {
PLOG(ERROR) << "failed to accept on socket " << service_socket.get();
- if (!android::base::WriteStringToFile(android::base::StringPrintf(
- "uncrypt_error: %d\n", kUncryptSocketAcceptError), UNCRYPT_STATUS)) {
- PLOG(WARNING) << "failed to write to " << UNCRYPT_STATUS;
- }
+ log_uncrypt_error_code(kUncryptSocketAcceptError);
return 1;
}
diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp
index e97a3adba..1c9be2d58 100644
--- a/update_verifier/update_verifier.cpp
+++ b/update_verifier/update_verifier.cpp
@@ -28,7 +28,6 @@
*
* The current slot will be marked as having booted successfully if the
* verifier reaches the end after the verification.
- *
*/
#include <errno.h>
@@ -42,9 +41,9 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
+#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-#include <cutils/properties.h>
#include <android/hardware/boot/1.0/IBootControl.h>
using android::sp;
@@ -56,54 +55,53 @@ constexpr auto CARE_MAP_FILE = "/data/ota_package/care_map.txt";
constexpr int BLOCKSIZE = 4096;
static bool read_blocks(const std::string& blk_device_prefix, const std::string& range_str) {
- char slot_suffix[PROPERTY_VALUE_MAX];
- property_get("ro.boot.slot_suffix", slot_suffix, "");
- std::string blk_device = blk_device_prefix + std::string(slot_suffix);
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY)));
- if (fd.get() == -1) {
- PLOG(ERROR) << "Error reading partition " << blk_device;
- return false;
- }
+ std::string slot_suffix = android::base::GetProperty("ro.boot.slot_suffix", "");
+ std::string blk_device = blk_device_prefix + slot_suffix;
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY)));
+ if (fd.get() == -1) {
+ PLOG(ERROR) << "Error reading partition " << blk_device;
+ return false;
+ }
- // For block range string, first integer 'count' equals 2 * total number of valid ranges,
- // followed by 'count' number comma separated integers. Every two integers reprensent a
- // block range with the first number included in range but second number not included.
- // For example '4,64536,65343,74149,74150' represents: [64536,65343) and [74149,74150).
- std::vector<std::string> ranges = android::base::Split(range_str, ",");
- size_t range_count;
- bool status = android::base::ParseUint(ranges[0].c_str(), &range_count);
- if (!status || (range_count == 0) || (range_count % 2 != 0) ||
- (range_count != ranges.size()-1)) {
- LOG(ERROR) << "Error in parsing range string.";
- return false;
- }
+ // For block range string, first integer 'count' equals 2 * total number of valid ranges,
+ // followed by 'count' number comma separated integers. Every two integers reprensent a
+ // block range with the first number included in range but second number not included.
+ // For example '4,64536,65343,74149,74150' represents: [64536,65343) and [74149,74150).
+ std::vector<std::string> ranges = android::base::Split(range_str, ",");
+ size_t range_count;
+ bool status = android::base::ParseUint(ranges[0], &range_count);
+ if (!status || (range_count == 0) || (range_count % 2 != 0) ||
+ (range_count != ranges.size() - 1)) {
+ LOG(ERROR) << "Error in parsing range string.";
+ return false;
+ }
- size_t blk_count = 0;
- for (size_t i = 1; i < ranges.size(); i += 2) {
- unsigned int range_start, range_end;
- bool parse_status = android::base::ParseUint(ranges[i].c_str(), &range_start);
- parse_status = parse_status && android::base::ParseUint(ranges[i+1].c_str(), &range_end);
- if (!parse_status || range_start >= range_end) {
- LOG(ERROR) << "Invalid range pair " << ranges[i] << ", " << ranges[i+1];
- return false;
- }
+ size_t blk_count = 0;
+ for (size_t i = 1; i < ranges.size(); i += 2) {
+ unsigned int range_start, range_end;
+ bool parse_status = android::base::ParseUint(ranges[i], &range_start);
+ parse_status = parse_status && android::base::ParseUint(ranges[i + 1], &range_end);
+ if (!parse_status || range_start >= range_end) {
+ LOG(ERROR) << "Invalid range pair " << ranges[i] << ", " << ranges[i + 1];
+ return false;
+ }
- if (lseek64(fd.get(), static_cast<off64_t>(range_start) * BLOCKSIZE, SEEK_SET) == -1) {
- PLOG(ERROR) << "lseek to " << range_start << " failed";
- return false;
- }
+ if (lseek64(fd.get(), static_cast<off64_t>(range_start) * BLOCKSIZE, SEEK_SET) == -1) {
+ PLOG(ERROR) << "lseek to " << range_start << " failed";
+ return false;
+ }
- size_t size = (range_end - range_start) * BLOCKSIZE;
- std::vector<uint8_t> buf(size);
- if (!android::base::ReadFully(fd.get(), buf.data(), size)) {
- PLOG(ERROR) << "Failed to read blocks " << range_start << " to " << range_end;
- return false;
- }
- blk_count += (range_end - range_start);
+ size_t size = (range_end - range_start) * BLOCKSIZE;
+ std::vector<uint8_t> buf(size);
+ if (!android::base::ReadFully(fd.get(), buf.data(), size)) {
+ PLOG(ERROR) << "Failed to read blocks " << range_start << " to " << range_end;
+ return false;
}
+ blk_count += (range_end - range_start);
+ }
- LOG(INFO) << "Finished reading " << blk_count << " blocks on " << blk_device;
- return true;
+ LOG(INFO) << "Finished reading " << blk_count << " blocks on " << blk_device;
+ return true;
}
static bool verify_image(const std::string& care_map_name) {
@@ -147,7 +145,7 @@ int main(int argc, char** argv) {
LOG(INFO) << "Started with arg " << i << ": " << argv[i];
}
- sp<IBootControl> module = IBootControl::getService("bootctrl");
+ sp<IBootControl> module = IBootControl::getService();
if (module == nullptr) {
LOG(ERROR) << "Error getting bootctrl module.";
return -1;
@@ -160,16 +158,16 @@ int main(int argc, char** argv) {
if (is_successful == BoolResult::FALSE) {
// The current slot has not booted successfully.
- char verity_mode[PROPERTY_VALUE_MAX];
- if (property_get("ro.boot.veritymode", verity_mode, "") == -1) {
+ std::string verity_mode = android::base::GetProperty("ro.boot.veritymode", "");
+ if (verity_mode.empty()) {
LOG(ERROR) << "Failed to get dm-verity mode.";
return -1;
- } else if (strcasecmp(verity_mode, "eio") == 0) {
+ } else if (android::base::EqualsIgnoreCase(verity_mode, "eio")) {
// We shouldn't see verity in EIO mode if the current slot hasn't booted
// successfully before. Therefore, fail the verification when veritymode=eio.
LOG(ERROR) << "Found dm-verity in EIO mode, skip verification.";
return -1;
- } else if (strcmp(verity_mode, "enforcing") != 0) {
+ } else if (verity_mode != "enforcing") {
LOG(ERROR) << "Unexpected dm-verity mode : " << verity_mode << ", expecting enforcing.";
return -1;
} else if (!verify_image(CARE_MAP_FILE)) {
diff --git a/updater/Android.mk b/updater/Android.mk
index 5d328a3fa..ac26bf42d 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -18,7 +18,7 @@ tune2fs_static_libraries := \
libext2_com_err \
libext2_blkid \
libext2_quota \
- libext2_uuid_static \
+ libext2_uuid \
libext2_e2p \
libext2fs
@@ -31,13 +31,13 @@ updater_common_static_libraries := \
libutils \
libmounts \
libotafault \
- libext4_utils_static \
+ libext4_utils \
libfec \
libfec_rs \
libfs_mgr \
liblog \
libselinux \
- libsparse_static \
+ libsparse \
libsquashfs_utils \
libbz \
libz \
diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp
index 7257e2399..6755d78cb 100644
--- a/updater/blockimg.cpp
+++ b/updater/blockimg.cpp
@@ -41,106 +41,104 @@
#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <applypatch/applypatch.h>
+#include <openssl/sha.h>
#include <ziparchive/zip_archive.h>
-#include "applypatch/applypatch.h"
#include "edify/expr.h"
#include "error_code.h"
#include "updater/install.h"
-#include "openssl/sha.h"
#include "ota_io.h"
#include "print_sha1.h"
#include "updater/updater.h"
-static constexpr size_t BLOCKSIZE = 4096;
-
// Set this to 0 to interpret 'erase' transfers to mean do a
// BLKDISCARD ioctl (the normal behavior). Set to 1 to interpret
// erase to mean fill the region with zeroes.
#define DEBUG_ERASE 0
-#define STASH_DIRECTORY_BASE "/cache/recovery"
-#define STASH_DIRECTORY_MODE 0700
-#define STASH_FILE_MODE 0600
+static constexpr size_t BLOCKSIZE = 4096;
+static constexpr const char* STASH_DIRECTORY_BASE = "/cache/recovery";
+static constexpr mode_t STASH_DIRECTORY_MODE = 0700;
+static constexpr mode_t STASH_FILE_MODE = 0600;
struct RangeSet {
- size_t count; // Limit is INT_MAX.
- size_t size;
- std::vector<size_t> pos; // Actual limit is INT_MAX.
+ size_t count; // Limit is INT_MAX.
+ size_t size;
+ std::vector<size_t> pos; // Actual limit is INT_MAX.
};
static CauseCode failure_type = kNoCause;
static bool is_retry = false;
static std::unordered_map<std::string, RangeSet> stash_map;
-static void parse_range(const std::string& range_text, RangeSet& rs) {
+static RangeSet parse_range(const std::string& range_text) {
+ RangeSet rs;
- std::vector<std::string> pieces = android::base::Split(range_text, ",");
- if (pieces.size() < 3) {
- goto err;
- }
+ std::vector<std::string> pieces = android::base::Split(range_text, ",");
+ if (pieces.size() < 3) {
+ goto err;
+ }
- size_t num;
- if (!android::base::ParseUint(pieces[0].c_str(), &num, static_cast<size_t>(INT_MAX))) {
- goto err;
- }
+ size_t num;
+ if (!android::base::ParseUint(pieces[0], &num, static_cast<size_t>(INT_MAX))) {
+ goto err;
+ }
- if (num == 0 || num % 2) {
- goto err; // must be even
- } else if (num != pieces.size() - 1) {
- goto err;
- }
+ if (num == 0 || num % 2) {
+ goto err; // must be even
+ } else if (num != pieces.size() - 1) {
+ goto err;
+ }
- rs.pos.resize(num);
- rs.count = num / 2;
- rs.size = 0;
+ rs.pos.resize(num);
+ rs.count = num / 2;
+ rs.size = 0;
- for (size_t i = 0; i < num; i += 2) {
- if (!android::base::ParseUint(pieces[i+1].c_str(), &rs.pos[i],
- static_cast<size_t>(INT_MAX))) {
- goto err;
- }
-
- if (!android::base::ParseUint(pieces[i+2].c_str(), &rs.pos[i+1],
- static_cast<size_t>(INT_MAX))) {
- goto err;
- }
+ for (size_t i = 0; i < num; i += 2) {
+ if (!android::base::ParseUint(pieces[i + 1], &rs.pos[i], static_cast<size_t>(INT_MAX))) {
+ goto err;
+ }
- if (rs.pos[i] >= rs.pos[i+1]) {
- goto err; // empty or negative range
- }
+ if (!android::base::ParseUint(pieces[i + 2], &rs.pos[i + 1], static_cast<size_t>(INT_MAX))) {
+ goto err;
+ }
- size_t sz = rs.pos[i+1] - rs.pos[i];
- if (rs.size > SIZE_MAX - sz) {
- goto err; // overflow
- }
+ if (rs.pos[i] >= rs.pos[i + 1]) {
+ goto err; // empty or negative range
+ }
- rs.size += sz;
+ size_t sz = rs.pos[i + 1] - rs.pos[i];
+ if (rs.size > SIZE_MAX - sz) {
+ goto err; // overflow
}
- return;
+ rs.size += sz;
+ }
+
+ return rs;
err:
- LOG(ERROR) << "failed to parse range '" << range_text << "'";
- exit(1);
+ LOG(ERROR) << "failed to parse range '" << range_text << "'";
+ exit(1);
}
static bool range_overlaps(const RangeSet& r1, const RangeSet& r2) {
- for (size_t i = 0; i < r1.count; ++i) {
- size_t r1_0 = r1.pos[i * 2];
- size_t r1_1 = r1.pos[i * 2 + 1];
+ for (size_t i = 0; i < r1.count; ++i) {
+ size_t r1_0 = r1.pos[i * 2];
+ size_t r1_1 = r1.pos[i * 2 + 1];
- for (size_t j = 0; j < r2.count; ++j) {
- size_t r2_0 = r2.pos[j * 2];
- size_t r2_1 = r2.pos[j * 2 + 1];
+ for (size_t j = 0; j < r2.count; ++j) {
+ size_t r2_0 = r2.pos[j * 2];
+ size_t r2_1 = r2.pos[j * 2 + 1];
- if (!(r2_0 >= r1_1 || r1_0 >= r2_1)) {
- return true;
- }
- }
+ if (!(r2_0 >= r1_1 || r1_0 >= r2_1)) {
+ return true;
+ }
}
+ }
- return false;
+ return false;
}
static int read_all(int fd, uint8_t* data, size_t size) {
@@ -431,11 +429,10 @@ static int LoadSrcTgtVersion1(CommandParameters& params, RangeSet& tgt, size_t&
}
// <src_range>
- RangeSet src;
- parse_range(params.tokens[params.cpos++], src);
+ RangeSet src = parse_range(params.tokens[params.cpos++]);
// <tgt_range>
- parse_range(params.tokens[params.cpos++], tgt);
+ tgt = parse_range(params.tokens[params.cpos++]);
allocate(src.size * BLOCKSIZE, buffer);
int rc = ReadBlocks(src, buffer, fd);
@@ -509,18 +506,18 @@ static void EnumerateStash(const std::string& dirname, StashCallback callback, v
}
static void UpdateFileSize(const std::string& fn, void* data) {
- if (fn.empty() || !data) {
- return;
- }
+ if (fn.empty() || !data) {
+ return;
+ }
- struct stat sb;
- if (stat(fn.c_str(), &sb) == -1) {
- PLOG(ERROR) << "stat \"" << fn << "\" failed";
- return;
- }
+ struct stat sb;
+ if (stat(fn.c_str(), &sb) == -1) {
+ PLOG(ERROR) << "stat \"" << fn << "\" failed";
+ return;
+ }
- int* size = reinterpret_cast<int*>(data);
- *size += sb.st_size;
+ size_t* size = static_cast<size_t*>(data);
+ *size += sb.st_size;
}
// Deletes the stash directory and all files in it. Assumes that it only
@@ -710,63 +707,67 @@ static int WriteStash(const std::string& base, const std::string& id, int blocks
// hash enough space for the expected amount of blocks we need to store. Returns
// >0 if we created the directory, zero if it existed already, and <0 of failure.
-static int CreateStash(State* state, int maxblocks, const char* blockdev, std::string& base) {
- if (blockdev == nullptr) {
- return -1;
- }
-
- // Stash directory should be different for each partition to avoid conflicts
- // when updating multiple partitions at the same time, so we use the hash of
- // the block device name as the base directory
- uint8_t digest[SHA_DIGEST_LENGTH];
- SHA1(reinterpret_cast<const uint8_t*>(blockdev), strlen(blockdev), digest);
- base = print_sha1(digest);
-
- std::string dirname = GetStashFileName(base, "", "");
- struct stat sb;
- int res = stat(dirname.c_str(), &sb);
-
- if (res == -1 && errno != ENOENT) {
- ErrorAbort(state, kStashCreationFailure, "stat \"%s\" failed: %s\n",
- dirname.c_str(), strerror(errno));
- return -1;
- } else if (res != 0) {
- LOG(INFO) << "creating stash " << dirname;
- res = mkdir(dirname.c_str(), STASH_DIRECTORY_MODE);
-
- if (res != 0) {
- ErrorAbort(state, kStashCreationFailure, "mkdir \"%s\" failed: %s\n",
- dirname.c_str(), strerror(errno));
- return -1;
- }
+static int CreateStash(State* state, size_t maxblocks, const std::string& blockdev,
+ std::string& base) {
+ if (blockdev.empty()) {
+ return -1;
+ }
+
+ // Stash directory should be different for each partition to avoid conflicts
+ // when updating multiple partitions at the same time, so we use the hash of
+ // the block device name as the base directory
+ uint8_t digest[SHA_DIGEST_LENGTH];
+ SHA1(reinterpret_cast<const uint8_t*>(blockdev.data()), blockdev.size(), digest);
+ base = print_sha1(digest);
+
+ std::string dirname = GetStashFileName(base, "", "");
+ struct stat sb;
+ int res = stat(dirname.c_str(), &sb);
+ size_t max_stash_size = maxblocks * BLOCKSIZE;
+
+ if (res == -1 && errno != ENOENT) {
+ ErrorAbort(state, kStashCreationFailure, "stat \"%s\" failed: %s\n", dirname.c_str(),
+ strerror(errno));
+ return -1;
+ } else if (res != 0) {
+ LOG(INFO) << "creating stash " << dirname;
+ res = mkdir(dirname.c_str(), STASH_DIRECTORY_MODE);
- if (CacheSizeCheck(maxblocks * BLOCKSIZE) != 0) {
- ErrorAbort(state, kStashCreationFailure, "not enough space for stash\n");
- return -1;
- }
+ if (res != 0) {
+ ErrorAbort(state, kStashCreationFailure, "mkdir \"%s\" failed: %s\n", dirname.c_str(),
+ strerror(errno));
+ return -1;
+ }
- return 1; // Created directory
+ if (CacheSizeCheck(max_stash_size) != 0) {
+ ErrorAbort(state, kStashCreationFailure, "not enough space for stash (%zu needed)\n",
+ max_stash_size);
+ return -1;
}
- LOG(INFO) << "using existing stash " << dirname;
+ return 1; // Created directory
+ }
- // If the directory already exists, calculate the space already allocated to
- // stash files and check if there's enough for all required blocks. Delete any
- // partially completed stash files first.
+ LOG(INFO) << "using existing stash " << dirname;
- EnumerateStash(dirname, DeletePartial, nullptr);
- int size = 0;
- EnumerateStash(dirname, UpdateFileSize, &size);
+ // If the directory already exists, calculate the space already allocated to
+ // stash files and check if there's enough for all required blocks. Delete any
+ // partially completed stash files first.
- size = maxblocks * BLOCKSIZE - size;
+ EnumerateStash(dirname, DeletePartial, nullptr);
+ size_t existing = 0;
+ EnumerateStash(dirname, UpdateFileSize, &existing);
- if (size > 0 && CacheSizeCheck(size) != 0) {
- ErrorAbort(state, kStashCreationFailure, "not enough space for stash (%d more needed)\n",
- size);
- return -1;
+ if (max_stash_size > existing) {
+ size_t needed = max_stash_size - existing;
+ if (CacheSizeCheck(needed) != 0) {
+ ErrorAbort(state, kStashCreationFailure, "not enough space for stash (%zu more needed)\n",
+ needed);
+ return -1;
}
+ }
- return 0; // Using existing directory
+ return 0; // Using existing directory
}
static int SaveStash(CommandParameters& params, const std::string& base,
@@ -787,8 +788,7 @@ static int SaveStash(CommandParameters& params, const std::string& base,
return 0;
}
- RangeSet src;
- parse_range(params.tokens[params.cpos++], src);
+ RangeSet src = parse_range(params.tokens[params.cpos++]);
allocate(src.size * BLOCKSIZE, buffer);
if (ReadBlocks(src, buffer, fd) == -1) {
@@ -872,7 +872,7 @@ static int LoadSrcTgtVersion2(CommandParameters& params, RangeSet& tgt, size_t&
}
// <tgt_range>
- parse_range(params.tokens[params.cpos++], tgt);
+ tgt = parse_range(params.tokens[params.cpos++]);
// <src_block_count>
const std::string& token = params.tokens[params.cpos++];
@@ -888,8 +888,7 @@ static int LoadSrcTgtVersion2(CommandParameters& params, RangeSet& tgt, size_t&
// no source ranges, only stashes
params.cpos++;
} else {
- RangeSet src;
- parse_range(params.tokens[params.cpos++], src);
+ RangeSet src = parse_range(params.tokens[params.cpos++]);
int res = ReadBlocks(src, buffer, fd);
if (overlap) {
@@ -905,8 +904,7 @@ static int LoadSrcTgtVersion2(CommandParameters& params, RangeSet& tgt, size_t&
return 0;
}
- RangeSet locs;
- parse_range(params.tokens[params.cpos++], locs);
+ RangeSet locs = parse_range(params.tokens[params.cpos++]);
MoveRange(buffer, locs, buffer);
}
@@ -931,8 +929,7 @@ static int LoadSrcTgtVersion2(CommandParameters& params, RangeSet& tgt, size_t&
continue;
}
- RangeSet locs;
- parse_range(tokens[1], locs);
+ RangeSet locs = parse_range(tokens[1]);
MoveRange(buffer, locs, stash);
}
@@ -1116,8 +1113,7 @@ static int PerformCommandZero(CommandParameters& params) {
return -1;
}
- RangeSet tgt;
- parse_range(params.tokens[params.cpos++], tgt);
+ RangeSet tgt = parse_range(params.tokens[params.cpos++]);
LOG(INFO) << " zeroing " << tgt.size << " blocks";
@@ -1160,8 +1156,7 @@ static int PerformCommandNew(CommandParameters& params) {
return -1;
}
- RangeSet tgt;
- parse_range(params.tokens[params.cpos++], tgt);
+ RangeSet tgt = parse_range(params.tokens[params.cpos++]);
if (params.canwrite) {
LOG(INFO) << " writing " << tgt.size << " blocks of new data";
@@ -1316,8 +1311,7 @@ static int PerformCommandErase(CommandParameters& params) {
return -1;
}
- RangeSet tgt;
- parse_range(params.tokens[params.cpos++], tgt);
+ RangeSet tgt = parse_range(params.tokens[params.cpos++]);
if (params.canwrite) {
LOG(INFO) << " erasing " << tgt.size << " blocks";
@@ -1358,7 +1352,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg
CommandParameters params = {};
params.canwrite = !dryrun;
- LOG(INFO) << "performing " << dryrun ? "verification" : "update";
+ LOG(INFO) << "performing " << (dryrun ? "verification" : "update");
if (state->is_retry) {
is_retry = true;
LOG(INFO) << "This update is a retry.";
@@ -1393,8 +1387,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg
return StringValue("");
}
- UpdaterInfo* ui = reinterpret_cast<UpdaterInfo*>(state->cookie);
-
+ UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie);
if (ui == nullptr) {
return StringValue("");
}
@@ -1452,7 +1445,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg
}
// First line in transfer list is the version number
- if (!android::base::ParseInt(lines[0].c_str(), &params.version, 1, 4)) {
+ if (!android::base::ParseInt(lines[0], &params.version, 1, 4)) {
LOG(ERROR) << "unexpected transfer list version [" << lines[0] << "]";
return StringValue("");
}
@@ -1460,8 +1453,8 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg
LOG(INFO) << "blockimg version is " << params.version;
// Second line in transfer list is the total number of blocks we expect to write
- int total_blocks;
- if (!android::base::ParseInt(lines[1].c_str(), &total_blocks, 0)) {
+ size_t total_blocks;
+ if (!android::base::ParseUint(lines[1], &total_blocks)) {
ErrorAbort(state, kArgsParsingFailure, "unexpected block count [%s]\n", lines[1].c_str());
return StringValue("");
}
@@ -1473,23 +1466,23 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg
size_t start = 2;
if (params.version >= 2) {
if (lines.size() < 4) {
- ErrorAbort(state, kArgsParsingFailure, "too few lines in the transfer list [%zu]\n",
- lines.size());
- return StringValue("");
+ ErrorAbort(state, kArgsParsingFailure, "too few lines in the transfer list [%zu]\n",
+ lines.size());
+ return StringValue("");
}
// Third line is how many stash entries are needed simultaneously
LOG(INFO) << "maximum stash entries " << lines[2];
// Fourth line is the maximum number of blocks that will be stashed simultaneously
- int stash_max_blocks;
- if (!android::base::ParseInt(lines[3].c_str(), &stash_max_blocks, 0)) {
+ size_t stash_max_blocks;
+ if (!android::base::ParseUint(lines[3], &stash_max_blocks)) {
ErrorAbort(state, kArgsParsingFailure, "unexpected maximum stash blocks [%s]\n",
lines[3].c_str());
return StringValue("");
}
- int res = CreateStash(state, stash_max_blocks, blockdev_filename->data.c_str(), params.stashbase);
+ int res = CreateStash(state, stash_max_blocks, blockdev_filename->data, params.stashbase);
if (res == -1) {
return StringValue("");
}
@@ -1514,15 +1507,13 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg
// Subsequent lines are all individual transfer commands
for (auto it = lines.cbegin() + start; it != lines.cend(); it++) {
- const std::string& line_str(*it);
- if (line_str.empty()) {
- continue;
- }
+ const std::string& line(*it);
+ if (line.empty()) continue;
- params.tokens = android::base::Split(line_str, " ");
+ params.tokens = android::base::Split(line, " ");
params.cpos = 0;
params.cmdname = params.tokens[params.cpos++].c_str();
- params.cmdline = line_str.c_str();
+ params.cmdline = line.c_str();
if (cmd_map.find(params.cmdname) == cmd_map.end()) {
LOG(ERROR) << "unexpected command [" << params.cmdname << "]";
@@ -1532,7 +1523,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg
const Command* cmd = cmd_map[params.cmdname];
if (cmd->f != nullptr && cmd->f(params) == -1) {
- LOG(ERROR) << "failed to execute command [" << line_str << "]";
+ LOG(ERROR) << "failed to execute command [" << line << "]";
goto pbiudone;
}
@@ -1542,7 +1533,8 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg
PLOG(ERROR) << "fsync failed";
goto pbiudone;
}
- fprintf(cmd_pipe, "set_progress %.4f\n", (double) params.written / total_blocks);
+ fprintf(cmd_pipe, "set_progress %.4f\n",
+ static_cast<double>(params.written) / total_blocks);
fflush(cmd_pipe);
}
}
@@ -1555,7 +1547,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg
LOG(INFO) << "max alloc needed was " << params.buffer.size();
const char* partition = strrchr(blockdev_filename->data.c_str(), '/');
- if (partition != nullptr && *(partition+1) != 0) {
+ if (partition != nullptr && *(partition + 1) != 0) {
fprintf(cmd_pipe, "log bytes_written_%s: %zu\n", partition + 1,
params.written * BLOCKSIZE);
fprintf(cmd_pipe, "log bytes_stashed_%s: %zu\n", partition + 1,
@@ -1707,8 +1699,7 @@ Value* RangeSha1Fn(const char* name, State* state, int /* argc */, Expr* argv[])
return StringValue("");
}
- RangeSet rs;
- parse_range(ranges->data, rs);
+ RangeSet rs = parse_range(ranges->data);
SHA_CTX ctx;
SHA1_Init(&ctx);
@@ -1832,8 +1823,7 @@ Value* BlockImageRecoverFn(const char* name, State* state, int argc, Expr* argv[
return StringValue("");
}
- RangeSet rs;
- parse_range(ranges->data, rs);
+ RangeSet rs = parse_range(ranges->data);
uint8_t buffer[BLOCKSIZE];
diff --git a/updater/install.cpp b/updater/install.cpp
index 3cf38774e..7a8e92f6c 100644
--- a/updater/install.cpp
+++ b/updater/install.cpp
@@ -46,6 +46,8 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <applypatch/applypatch.h>
+#include <bootloader_message/bootloader_message.h>
#include <cutils/android_reboot.h>
#include <ext4_utils/make_ext4fs.h>
#include <ext4_utils/wipe.h>
@@ -54,8 +56,6 @@
#include <selinux/selinux.h>
#include <ziparchive/zip_archive.h>
-#include "applypatch/applypatch.h"
-#include "bootloader.h"
#include "edify/expr.h"
#include "error_code.h"
#include "mounts.h"
@@ -76,7 +76,6 @@ static void uiPrint(State* state, const std::string& buffer) {
for (auto& line : lines) {
if (!line.empty()) {
fprintf(ui->cmd_pipe, "ui_print %s\n", line.c_str());
- fprintf(ui->cmd_pipe, "ui_print\n");
}
}
diff --git a/updater/updater.cpp b/updater/updater.cpp
index 3e624dae7..22c060fcb 100644
--- a/updater/updater.cpp
+++ b/updater/updater.cpp
@@ -100,7 +100,6 @@ int main(int argc, char** argv) {
CloseArchive(za);
return 3;
}
- ota_io_init(za);
ZipString script_name(SCRIPT_NAME);
ZipEntry script_entry;
@@ -166,6 +165,7 @@ int main(int argc, char** argv) {
printf("unexpected argument: %s", argv[4]);
}
}
+ ota_io_init(za, state.is_retry);
std::string result;
bool status = Evaluate(&state, root, &result);
@@ -191,7 +191,6 @@ int main(int argc, char** argv) {
}
fprintf(cmd_pipe, "ui_print %s\n", line.c_str());
}
- fprintf(cmd_pipe, "ui_print\n");
}
if (state.error_code != kNoError) {
diff --git a/wear_ui.cpp b/wear_ui.cpp
index 5433d110b..6c0286558 100644
--- a/wear_ui.cpp
+++ b/wear_ui.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "wear_ui.h"
+
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
@@ -25,14 +27,16 @@
#include <time.h>
#include <unistd.h>
+#include <string>
#include <vector>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/stringprintf.h>
+#include <minui/minui.h>
+
#include "common.h"
#include "device.h"
-#include "wear_ui.h"
-#include "android-base/properties.h"
-#include "android-base/strings.h"
-#include "android-base/stringprintf.h"
// There's only (at most) one of these objects, and global callbacks
// (for pthread_create, and the input event system) need to find it,
@@ -47,32 +51,13 @@ static double now() {
}
WearRecoveryUI::WearRecoveryUI() :
- progress_bar_height(3),
- progress_bar_width(200),
progress_bar_y(259),
outer_height(0),
outer_width(0),
- menu_unusable_rows(0),
- intro_frames(22),
- loop_frames(60),
- animation_fps(30),
- currentIcon(NONE),
- intro_done(false),
- current_frame(0),
- progressBarType(EMPTY),
- progressScopeStart(0),
- progressScopeSize(0),
- progress(0),
- text_cols(0),
- text_rows(0),
- text_col(0),
- text_row(0),
- text_top(0),
- show_text(false),
- show_text_ever(false),
- show_menu(false),
- menu_items(0),
- menu_sel(0) {
+ menu_unusable_rows(0) {
+ intro_frames = 22;
+ loop_frames = 60;
+ animation_fps = 30;
for (size_t i = 0; i < 5; i++)
backgroundIcon[i] = NULL;
@@ -80,16 +65,22 @@ WearRecoveryUI::WearRecoveryUI() :
self = this;
}
+int WearRecoveryUI::GetProgressBaseline() {
+ return progress_bar_y;
+}
+
// Draw background frame on the screen. Does not flip pages.
// Should only be called with updateMutex locked.
-void WearRecoveryUI::draw_background_locked(Icon icon)
+// TODO merge drawing routines with screen_ui
+void WearRecoveryUI::draw_background_locked()
{
+ pagesIdentical = false;
gr_color(0, 0, 0, 255);
gr_fill(0, 0, gr_fb_width(), gr_fb_height());
- if (icon) {
+ if (currentIcon != NONE) {
GRSurface* surface;
- if (icon == INSTALLING_UPDATE || icon == ERASING) {
+ if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) {
if (!intro_done) {
surface = introFrames[current_frame];
} else {
@@ -97,7 +88,7 @@ void WearRecoveryUI::draw_background_locked(Icon icon)
}
}
else {
- surface = backgroundIcon[icon];
+ surface = backgroundIcon[currentIcon];
}
int width = gr_get_width(surface);
@@ -110,36 +101,6 @@ void WearRecoveryUI::draw_background_locked(Icon icon)
}
}
-// Draw the progress bar (if any) on the screen. Does not flip pages.
-// Should only be called with updateMutex locked.
-void WearRecoveryUI::draw_progress_locked()
-{
- if (currentIcon == ERROR) return;
- if (progressBarType != DETERMINATE) return;
-
- int width = progress_bar_width;
- int height = progress_bar_height;
- int dx = (gr_fb_width() - width)/2;
- int dy = progress_bar_y;
-
- float p = progressScopeStart + progress * progressScopeSize;
- int pos = (int) (p * width);
-
- gr_color(0x43, 0x43, 0x43, 0xff);
- gr_fill(dx, dy, dx + width, dy + height);
-
- if (pos > 0) {
- gr_color(0x02, 0xa8, 0xf3, 255);
- if (rtl_locale) {
- // Fill the progress bar from right to left.
- gr_fill(dx + width - pos, dy, dx + width, dy + height);
- } else {
- // Fill the progress bar from left to right.
- gr_fill(dx, dy, dx + pos, dy + height);
- }
- }
-}
-
static const char* HEADERS[] = {
"Swipe up/down to move.",
"Swipe left/right to select.",
@@ -147,13 +108,15 @@ static const char* HEADERS[] = {
NULL
};
+// TODO merge drawing routines with screen_ui
void WearRecoveryUI::draw_screen_locked()
{
- draw_background_locked(currentIcon);
- draw_progress_locked();
char cur_selection_str[50];
- if (show_text) {
+ draw_background_locked();
+ if (!show_text) {
+ draw_foreground_locked();
+ } else {
SetColor(TEXT_FILL);
gr_fill(0, 0, gr_fb_width(), gr_fb_height());
@@ -192,10 +155,12 @@ void WearRecoveryUI::draw_screen_locked()
gr_fill(x, y-2, gr_fb_width()-x, y+char_height_+2);
// white text of selected item
SetColor(MENU_SEL_FG);
- if (menu[i][0]) gr_text(gr_sys_font(), x+4, y, menu[i], 1);
+ if (menu_[i][0]) {
+ gr_text(gr_sys_font(), x + 4, y, menu_[i], 1);
+ }
SetColor(MENU);
- } else if (menu[i][0]) {
- gr_text(gr_sys_font(), x+4, y, menu[i], 0);
+ } else if (menu_[i][0]) {
+ gr_text(gr_sys_font(), x + 4, y, menu_[i], 0);
}
y += char_height_+4;
}
@@ -211,215 +176,99 @@ void WearRecoveryUI::draw_screen_locked()
// screen, the bottom of the menu, or we've displayed the
// entire text buffer.
int ty;
- int row = (text_top+text_rows-1) % text_rows;
+ int row = (text_top_ + text_rows_ - 1) % text_rows_;
size_t count = 0;
for (int ty = gr_fb_height() - char_height_ - outer_height;
- ty > y+2 && count < text_rows;
+ ty > y + 2 && count < text_rows_;
ty -= char_height_, ++count) {
- gr_text(gr_sys_font(), x+4, ty, text[row], 0);
+ gr_text(gr_sys_font(), x+4, ty, text_[row], 0);
--row;
- if (row < 0) row = text_rows-1;
+ if (row < 0) row = text_rows_ - 1;
}
}
}
-void WearRecoveryUI::update_screen_locked()
-{
+// TODO merge drawing routines with screen_ui
+void WearRecoveryUI::update_progress_locked() {
draw_screen_locked();
gr_flip();
}
-// Keeps the progress bar updated, even when the process is otherwise busy.
-void* WearRecoveryUI::progress_thread(void *cookie) {
- self->progress_loop();
- return NULL;
-}
-
-void WearRecoveryUI::progress_loop() {
- double interval = 1.0 / animation_fps;
- for (;;) {
- double start = now();
- pthread_mutex_lock(&updateMutex);
- int redraw = 0;
-
- if ((currentIcon == INSTALLING_UPDATE || currentIcon == ERASING)
- && !show_text) {
- if (!intro_done) {
- if (current_frame >= intro_frames - 1) {
- intro_done = true;
- current_frame = 0;
- } else {
- current_frame++;
- }
- } else {
- current_frame = (current_frame + 1) % loop_frames;
- }
- redraw = 1;
- }
-
- // move the progress bar forward on timed intervals, if configured
- int duration = progressScopeDuration;
- if (progressBarType == DETERMINATE && duration > 0) {
- double elapsed = now() - progressScopeTime;
- float p = 1.0 * elapsed / duration;
- if (p > 1.0) p = 1.0;
- if (p > progress) {
- progress = p;
- redraw = 1;
- }
- }
-
- if (redraw)
- update_screen_locked();
-
- pthread_mutex_unlock(&updateMutex);
- double end = now();
- // minimum of 20ms delay between frames
- double delay = interval - (end-start);
- if (delay < 0.02) delay = 0.02;
- usleep(static_cast<useconds_t>(delay * 1000000));
+bool WearRecoveryUI::InitTextParams() {
+ if (!ScreenRecoveryUI::InitTextParams()) {
+ return false;
}
-}
-void WearRecoveryUI::Init()
-{
- gr_init();
+ text_cols_ = (gr_fb_width() - (outer_width * 2)) / char_width_;
- gr_font_size(gr_sys_font(), &char_width_, &char_height_);
+ if (text_rows_ > kMaxRows) text_rows_ = kMaxRows;
+ if (text_cols_ > kMaxCols) text_cols_ = kMaxCols;
- text_col = text_row = 0;
- text_rows = (gr_fb_height()) / char_height_;
visible_text_rows = (gr_fb_height() - (outer_height * 2)) / char_height_;
- if (text_rows > kMaxRows) text_rows = kMaxRows;
- text_top = 1;
-
- text_cols = (gr_fb_width() - (outer_width * 2)) / char_width_;
- if (text_cols > kMaxCols - 1) text_cols = kMaxCols - 1;
-
- LoadBitmap("icon_installing", &backgroundIcon[INSTALLING_UPDATE]);
- backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE];
- LoadBitmap("icon_error", &backgroundIcon[ERROR]);
- backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR];
-
- introFrames = (GRSurface**)malloc(intro_frames * sizeof(GRSurface*));
- for (int i = 0; i < intro_frames; ++i) {
- char filename[40];
- sprintf(filename, "intro%02d", i);
- LoadBitmap(filename, introFrames + i);
- }
-
- loopFrames = (GRSurface**)malloc(loop_frames * sizeof(GRSurface*));
- for (int i = 0; i < loop_frames; ++i) {
- char filename[40];
- sprintf(filename, "loop%02d", i);
- LoadBitmap(filename, loopFrames + i);
- }
-
- pthread_create(&progress_t, NULL, progress_thread, NULL);
- RecoveryUI::Init();
+ return true;
}
-void WearRecoveryUI::SetBackground(Icon icon)
-{
- pthread_mutex_lock(&updateMutex);
- currentIcon = icon;
- update_screen_locked();
- pthread_mutex_unlock(&updateMutex);
-}
+bool WearRecoveryUI::Init(const std::string& locale) {
+ if (!ScreenRecoveryUI::Init(locale)) {
+ return false;
+ }
-void WearRecoveryUI::SetProgressType(ProgressType type)
-{
- pthread_mutex_lock(&updateMutex);
- if (progressBarType != type) {
- progressBarType = type;
- }
- progressScopeStart = 0;
- progressScopeSize = 0;
- progress = 0;
- update_screen_locked();
- pthread_mutex_unlock(&updateMutex);
-}
+ LoadBitmap("icon_error", &backgroundIcon[ERROR]);
+ backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR];
-void WearRecoveryUI::ShowProgress(float portion, float seconds)
-{
- pthread_mutex_lock(&updateMutex);
- progressBarType = DETERMINATE;
- progressScopeStart += progressScopeSize;
- progressScopeSize = portion;
- progressScopeTime = now();
- progressScopeDuration = seconds;
- progress = 0;
- update_screen_locked();
- pthread_mutex_unlock(&updateMutex);
-}
+ // This leaves backgroundIcon[INSTALLING_UPDATE] and backgroundIcon[ERASING]
+ // as NULL which is fine since draw_background_locked() doesn't use them.
-void WearRecoveryUI::SetProgress(float fraction)
-{
- pthread_mutex_lock(&updateMutex);
- if (fraction < 0.0) fraction = 0.0;
- if (fraction > 1.0) fraction = 1.0;
- if (progressBarType == DETERMINATE && fraction > progress) {
- // Skip updates that aren't visibly different.
- int width = progress_bar_width;
- float scale = width * progressScopeSize;
- if ((int) (progress * scale) != (int) (fraction * scale)) {
- progress = fraction;
- update_screen_locked();
- }
- }
- pthread_mutex_unlock(&updateMutex);
+ return true;
}
-void WearRecoveryUI::SetStage(int current, int max)
-{
-}
-
-void WearRecoveryUI::Print(const char *fmt, ...)
-{
- char buf[256];
- va_list ap;
- va_start(ap, fmt);
- vsnprintf(buf, 256, fmt, ap);
- va_end(ap);
-
- fputs(buf, stdout);
-
- // This can get called before ui_init(), so be careful.
- pthread_mutex_lock(&updateMutex);
- if (text_rows > 0 && text_cols > 0) {
- char *ptr;
- for (ptr = buf; *ptr != '\0'; ++ptr) {
- if (*ptr == '\n' || text_col >= text_cols) {
- text[text_row][text_col] = '\0';
- text_col = 0;
- text_row = (text_row + 1) % text_rows;
- if (text_row == text_top) text_top = (text_top + 1) % text_rows;
- }
- if (*ptr != '\n') text[text_row][text_col++] = *ptr;
- }
- text[text_row][text_col] = '\0';
- update_screen_locked();
+void WearRecoveryUI::SetStage(int current, int max) {}
+
+void WearRecoveryUI::Print(const char* fmt, ...) {
+ char buf[256];
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(buf, 256, fmt, ap);
+ va_end(ap);
+
+ fputs(buf, stdout);
+
+ // This can get called before ui_init(), so be careful.
+ pthread_mutex_lock(&updateMutex);
+ if (text_rows_ > 0 && text_cols_ > 0) {
+ char* ptr;
+ for (ptr = buf; *ptr != '\0'; ++ptr) {
+ if (*ptr == '\n' || text_col_ >= text_cols_) {
+ text_[text_row_][text_col_] = '\0';
+ text_col_ = 0;
+ text_row_ = (text_row_ + 1) % text_rows_;
+ if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_;
+ }
+ if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
}
- pthread_mutex_unlock(&updateMutex);
+ text_[text_row_][text_col_] = '\0';
+ update_screen_locked();
+ }
+ pthread_mutex_unlock(&updateMutex);
}
void WearRecoveryUI::StartMenu(const char* const * headers, const char* const * items,
- int initial_selection) {
+ int initial_selection) {
pthread_mutex_lock(&updateMutex);
- if (text_rows > 0 && text_cols > 0) {
+ if (text_rows_ > 0 && text_cols_ > 0) {
menu_headers_ = headers;
size_t i = 0;
- // "i < text_rows" is removed from the loop termination condition,
+ // "i < text_rows_" is removed from the loop termination condition,
// which is different from the one in ScreenRecoveryUI::StartMenu().
// Because WearRecoveryUI supports scrollable menu, it's fine to have
- // more entries than text_rows. The menu may be truncated otherwise.
+ // more entries than text_rows_. The menu may be truncated otherwise.
// Bug: 23752519
for (; items[i] != nullptr; i++) {
- strncpy(menu[i], items[i], text_cols - 1);
- menu[i][text_cols - 1] = '\0';
+ strncpy(menu_[i], items[i], text_cols_ - 1);
+ menu_[i][text_cols_ - 1] = '\0';
}
menu_items = i;
- show_menu = 1;
+ show_menu = true;
menu_sel = initial_selection;
menu_start = 0;
menu_end = visible_text_rows - 1 - menu_unusable_rows;
@@ -433,7 +282,7 @@ void WearRecoveryUI::StartMenu(const char* const * headers, const char* const *
int WearRecoveryUI::SelectMenu(int sel) {
int old_sel;
pthread_mutex_lock(&updateMutex);
- if (show_menu > 0) {
+ if (show_menu) {
old_sel = menu_sel;
menu_sel = sel;
if (menu_sel < 0) menu_sel = 0;
@@ -452,53 +301,6 @@ int WearRecoveryUI::SelectMenu(int sel) {
return sel;
}
-void WearRecoveryUI::EndMenu() {
- int i;
- pthread_mutex_lock(&updateMutex);
- if (show_menu > 0 && text_rows > 0 && text_cols > 0) {
- show_menu = 0;
- update_screen_locked();
- }
- pthread_mutex_unlock(&updateMutex);
-}
-
-bool WearRecoveryUI::IsTextVisible()
-{
- pthread_mutex_lock(&updateMutex);
- int visible = show_text;
- pthread_mutex_unlock(&updateMutex);
- return visible;
-}
-
-bool WearRecoveryUI::WasTextEverVisible()
-{
- pthread_mutex_lock(&updateMutex);
- int ever_visible = show_text_ever;
- pthread_mutex_unlock(&updateMutex);
- return ever_visible;
-}
-
-void WearRecoveryUI::ShowText(bool visible)
-{
- pthread_mutex_lock(&updateMutex);
- // Don't show text during ota install or factory reset
- if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) {
- pthread_mutex_unlock(&updateMutex);
- return;
- }
- show_text = visible;
- if (show_text) show_text_ever = 1;
- update_screen_locked();
- pthread_mutex_unlock(&updateMutex);
-}
-
-void WearRecoveryUI::Redraw()
-{
- pthread_mutex_lock(&updateMutex);
- update_screen_locked();
- pthread_mutex_unlock(&updateMutex);
-}
-
void WearRecoveryUI::ShowFile(FILE* fp) {
std::vector<off_t> offsets;
offsets.push_back(ftello(fp));
@@ -538,12 +340,12 @@ void WearRecoveryUI::ShowFile(FILE* fp) {
int ch = getc(fp);
if (ch == EOF) {
- text_row = text_top = text_rows - 2;
+ text_row_ = text_top_ = text_rows_ - 2;
show_prompt = true;
} else {
PutChar(ch);
- if (text_col == 0 && text_row >= text_rows - 2) {
- text_top = text_row;
+ if (text_col_ == 0 && text_row_ >= text_rows_ - 2) {
+ text_top_ = text_row_;
show_prompt = true;
}
}
@@ -552,10 +354,10 @@ void WearRecoveryUI::ShowFile(FILE* fp) {
void WearRecoveryUI::PutChar(char ch) {
pthread_mutex_lock(&updateMutex);
- if (ch != '\n') text[text_row][text_col++] = ch;
- if (ch == '\n' || text_col >= text_cols) {
- text_col = 0;
- ++text_row;
+ if (ch != '\n') text_[text_row_][text_col_++] = ch;
+ if (ch == '\n' || text_col_ >= text_cols_) {
+ text_col_ = 0;
+ ++text_row_;
}
pthread_mutex_unlock(&updateMutex);
}
@@ -572,11 +374,11 @@ void WearRecoveryUI::ShowFile(const char* filename) {
void WearRecoveryUI::ClearText() {
pthread_mutex_lock(&updateMutex);
- text_col = 0;
- text_row = 0;
- text_top = 1;
- for (size_t i = 0; i < text_rows; ++i) {
- memset(text[i], 0, text_cols + 1);
+ text_col_ = 0;
+ text_row_ = 0;
+ text_top_ = 1;
+ for (size_t i = 0; i < text_rows_; ++i) {
+ memset(text_[i], 0, text_cols_ + 1);
}
pthread_mutex_unlock(&updateMutex);
}
@@ -597,17 +399,17 @@ void WearRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) {
}
pthread_mutex_lock(&updateMutex);
- if (text_rows > 0 && text_cols > 0) {
+ if (text_rows_ > 0 && text_cols_ > 0) {
for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) {
- if (*ptr == '\n' || text_col >= text_cols) {
- text[text_row][text_col] = '\0';
- text_col = 0;
- text_row = (text_row + 1) % text_rows;
- if (text_row == text_top) text_top = (text_top + 1) % text_rows;
+ if (*ptr == '\n' || text_col_ >= text_cols_) {
+ text_[text_row_][text_col_] = '\0';
+ text_col_ = 0;
+ text_row_ = (text_row_ + 1) % text_rows_;
+ if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_;
}
- if (*ptr != '\n') text[text_row][text_col++] = *ptr;
+ if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
}
- text[text_row][text_col] = '\0';
+ text_[text_row_][text_col_] = '\0';
update_screen_locked();
}
pthread_mutex_unlock(&updateMutex);
diff --git a/wear_ui.h b/wear_ui.h
index e2d6fe072..4cd852f21 100644
--- a/wear_ui.h
+++ b/wear_ui.h
@@ -19,43 +19,28 @@
#include "screen_ui.h"
+#include <string>
+
class WearRecoveryUI : public ScreenRecoveryUI {
public:
WearRecoveryUI();
- void Init();
- // overall recovery state ("background image")
- void SetBackground(Icon icon);
-
- // progress indicator
- void SetProgressType(ProgressType type);
- void ShowProgress(float portion, float seconds);
- void SetProgress(float fraction);
-
- void SetStage(int current, int max);
+ bool Init(const std::string& locale) override;
- // text log
- void ShowText(bool visible);
- bool IsTextVisible();
- bool WasTextEverVisible();
+ void SetStage(int current, int max) override;
// printing messages
- void Print(const char* fmt, ...);
- void PrintOnScreenOnly(const char* fmt, ...) __printflike(2, 3);
- void ShowFile(const char* filename);
- void ShowFile(FILE* fp);
+ void Print(const char* fmt, ...) override;
+ void PrintOnScreenOnly(const char* fmt, ...) override __printflike(2, 3);
+ void ShowFile(const char* filename) override;
+ void ShowFile(FILE* fp) override;
// menu display
void StartMenu(const char* const * headers, const char* const * items,
- int initial_selection);
- int SelectMenu(int sel);
- void EndMenu();
-
- void Redraw();
+ int initial_selection) override;
+ int SelectMenu(int sel) override;
protected:
- int progress_bar_height, progress_bar_width;
-
// progress bar vertical position, it's centered horizontally
int progress_bar_y;
@@ -67,59 +52,34 @@ class WearRecoveryUI : public ScreenRecoveryUI {
// that may otherwise go out of the screen.
int menu_unusable_rows;
- // number of intro frames (default: 22) and loop frames (default: 60)
- int intro_frames;
- int loop_frames;
-
- // Number of frames per sec (default: 30) for both of intro and loop.
- int animation_fps;
+ int GetProgressBaseline() override;
- private:
- Icon currentIcon;
+ bool InitTextParams() override;
- bool intro_done;
+ void update_progress_locked() override;
- int current_frame;
+ void PrintV(const char*, bool, va_list) override;
+ private:
GRSurface* backgroundIcon[5];
- GRSurface* *introFrames;
- GRSurface* *loopFrames;
-
- ProgressType progressBarType;
-
- float progressScopeStart, progressScopeSize, progress;
- double progressScopeTime, progressScopeDuration;
static const int kMaxCols = 96;
static const int kMaxRows = 96;
- // Log text overlay, displayed when a magic key is pressed
- char text[kMaxRows][kMaxCols];
- size_t text_cols, text_rows;
// Number of text rows seen on screen
int visible_text_rows;
- size_t text_col, text_row, text_top;
- bool show_text;
- bool show_text_ever; // has show_text ever been true?
- char menu[kMaxRows][kMaxCols];
- bool show_menu;
const char* const* menu_headers_;
- int menu_items, menu_sel;
int menu_start, menu_end;
pthread_t progress_t;
- private:
- void draw_background_locked(Icon icon);
+ void draw_background_locked() override;
+ void draw_screen_locked() override;
void draw_progress_locked();
- void draw_screen_locked();
- void update_screen_locked();
- static void* progress_thread(void* cookie);
- void progress_loop();
+
void PutChar(char);
void ClearText();
- void PrintV(const char*, bool, va_list);
};
#endif // RECOVERY_WEAR_UI_H