summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.clang-format3
-rw-r--r--Android.mk2
-rw-r--r--applypatch/Android.mk91
-rw-r--r--applypatch/applypatch.cpp13
-rw-r--r--applypatch/imgdiff.cpp79
-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.cpp322
-rw-r--r--recovery.cpp465
-rw-r--r--screen_ui.cpp303
-rw-r--r--screen_ui.h57
-rw-r--r--stub_ui.h65
-rw-r--r--tests/Android.mk36
-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.cpp3
-rw-r--r--tests/unit/dirutil_test.cpp150
-rw-r--r--tests/unit/zip_test.cpp21
-rw-r--r--tests/unit/ziputil_test.cpp191
-rw-r--r--ui.cpp66
-rw-r--r--ui.h21
-rw-r--r--uncrypt/uncrypt.cpp37
-rw-r--r--updater/blockimg.cpp308
-rw-r--r--updater/install.cpp4
-rw-r--r--wear_ui.cpp408
-rw-r--r--wear_ui.h76
37 files changed, 2735 insertions, 1474 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..2943f016b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -159,8 +159,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/applypatch/Android.mk b/applypatch/Android.mk
index fa0fe8a37..ec3c6ee38 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,11 +25,11 @@ 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 \
@@ -42,36 +41,45 @@ 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_STATIC_LIBRARIES := \
+ libcrypto \
+ libbz \
+ libz
LOCAL_CFLAGS := -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_STATIC_LIBRARIES := \
+ libcrypto \
+ libbz \
+ libz
LOCAL_CFLAGS := -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 +95,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 +113,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 \
+ 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 \
+LOCAL_CFLAGS := -Werror
+LOCAL_STATIC_LIBRARIES := \
+ libimgdiff \
+ $(libimgdiff_static_libraries) \
libbz \
- libdivsufsort64 \
libdivsufsort \
- libz
-LOCAL_CFLAGS := -Werror
-LOCAL_FORCE_STATIC_EXECUTABLE := true
+ libdivsufsort64
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 528daf113..62de726a4 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 {
@@ -374,8 +378,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));
@@ -383,19 +386,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;
@@ -403,7 +399,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 &&
@@ -413,8 +409,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.
@@ -502,8 +497,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;
}
@@ -521,10 +515,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;
}
}
@@ -637,7 +629,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) {
@@ -793,10 +789,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;
@@ -812,11 +806,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;
}
@@ -880,12 +874,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");
@@ -897,8 +889,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");
@@ -983,8 +974,7 @@ int main(int argc, char** argv) {
if (zip_mode) {
ImageChunk* src;
if (tgt_chunks[i].type == CHUNK_DEFLATE &&
- (src = FindChunkByName(tgt_chunks[i].filename, src_chunks,
- num_src_chunks))) {
+ (src = FindChunkByName(tgt_chunks[i].filename, src_chunks, num_src_chunks))) {
patch_data[i] = MakePatch(src, tgt_chunks+i, patch_size+i);
} else {
patch_data[i] = MakePatch(src_chunks, tgt_chunks+i, patch_size+i);
@@ -1000,8 +990,7 @@ int main(int argc, char** argv) {
patch_data[i] = MakePatch(src_chunks+i, tgt_chunks+i, patch_size+i);
}
- 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);
}
// Figure out how big the imgdiff file header is going to be, so
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..ae6071f0e 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 = const_cast<unsigned char*>(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..008db4d86 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,6 +34,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/parsedouble.h>
#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -40,7 +43,6 @@
#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) {
@@ -299,158 +301,168 @@ 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);
+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);
+ int pipefd[2];
+ pipe(pipefd);
- 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;
- }
-
- 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") {
+ if (!args.empty()) {
+ ui->PrintOnScreenOnly("%s", args.c_str());
+ } else {
+ ui->PrintOnScreenOnly("\n");
+ }
+ 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 +554,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/recovery.cpp b/recovery.cpp
index 02d460eb9..5888c542a 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,26 +173,9 @@ 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
*/
-static const int MAX_ARG_LENGTH = 4096;
-static const int MAX_ARGS = 100;
-
// open a given path, mounting partitions as necessary
FILE* fopen_path(const char *path, const char *mode) {
if (ensure_path_mounted(path) != 0) {
@@ -310,88 +297,81 @@ static void redirect_stdio(const char* filename) {
// - the actual command line
// - the bootloader control block (one per line, after "recovery")
// - the contents of COMMAND_FILE (one per line)
-static void
-get_args(int *argc, char ***argv) {
- bootloader_message boot = {};
- std::string err;
- if (!read_bootloader_message(&boot, &err)) {
- LOG(ERROR) << err;
- // If fails, leave a zeroed bootloader_message.
- memset(&boot, 0, sizeof(boot));
- }
- stage = strndup(boot.stage, sizeof(boot.stage));
-
- if (boot.command[0] != 0) {
- std::string boot_command = std::string(boot.command, sizeof(boot.command));
- LOG(INFO) << "Boot command: " << boot_command;
- }
-
- if (boot.status[0] != 0) {
- std::string boot_status = std::string(boot.status, sizeof(boot.status));
- LOG(INFO) << "Boot status: " << boot_status;
- }
-
- // --- if arguments weren't supplied, look in the bootloader control block
- if (*argc <= 1) {
- boot.recovery[sizeof(boot.recovery) - 1] = '\0'; // Ensure termination
- const char *arg = strtok(boot.recovery, "\n");
- if (arg != NULL && !strcmp(arg, "recovery")) {
- *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
- (*argv)[0] = strdup(arg);
- for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
- if ((arg = strtok(NULL, "\n")) == NULL) break;
- (*argv)[*argc] = strdup(arg);
- }
- LOG(INFO) << "Got arguments from boot message";
- } else if (boot.recovery[0] != 0) {
- std::string boot_recovery = std::string(boot.recovery, 20);
- LOG(ERROR) << "Bad boot message\n" << "\"" <<boot_recovery << "\"";
- }
- }
-
- // --- if that doesn't work, try the command file (if we have /cache).
- if (*argc <= 1 && has_cache) {
- FILE *fp = fopen_path(COMMAND_FILE, "r");
- if (fp != NULL) {
- char *token;
- char *argv0 = (*argv)[0];
- *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
- (*argv)[0] = argv0; // use the same program name
-
- char buf[MAX_ARG_LENGTH];
- for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
- if (!fgets(buf, sizeof(buf), fp)) break;
- token = strtok(buf, "\r\n");
- if (token != NULL) {
- (*argv)[*argc] = strdup(token); // Strip newline.
- } else {
- --*argc;
- }
- }
-
- check_and_fclose(fp, COMMAND_FILE);
- LOG(INFO) << "Got arguments from " << COMMAND_FILE;
- }
- }
-
- // --> write the arguments we have back into the bootloader control block
- // always boot into recovery after this (until finish_recovery() is called)
- std::vector<std::string> options;
- for (int i = 1; i < *argc; ++i) {
- options.push_back((*argv)[i]);
- }
- if (!write_bootloader_message(options, &err)) {
- LOG(ERROR) << err;
- }
+static std::vector<std::string> get_args(const int argc, char** const argv) {
+ CHECK_GT(argc, 0);
+
+ bootloader_message boot = {};
+ std::string err;
+ if (!read_bootloader_message(&boot, &err)) {
+ LOG(ERROR) << err;
+ // If fails, leave a zeroed bootloader_message.
+ boot = {};
+ }
+ stage = strndup(boot.stage, sizeof(boot.stage));
+
+ if (boot.command[0] != 0) {
+ std::string boot_command = std::string(boot.command, sizeof(boot.command));
+ LOG(INFO) << "Boot command: " << boot_command;
+ }
+
+ if (boot.status[0] != 0) {
+ std::string boot_status = std::string(boot.status, sizeof(boot.status));
+ LOG(INFO) << "Boot status: " << boot_status;
+ }
+
+ std::vector<std::string> args(argv, argv + argc);
+
+ // --- if arguments weren't supplied, look in the bootloader control block
+ if (argc == 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");
+ if (!tokens.empty() && tokens[0] == "recovery") {
+ for (auto it = tokens.begin() + 1; it != tokens.end(); it++) {
+ // Skip empty and '\0'-filled tokens.
+ if (!it->empty() && (*it)[0] != '\0') args.push_back(std::move(*it));
+ }
+ LOG(INFO) << "Got " << args.size() << " arguments from boot message";
+ } else if (boot.recovery[0] != 0) {
+ LOG(ERROR) << "Bad boot message: \"" << boot_recovery << "\"";
+ }
+ }
+
+ // --- if that doesn't work, try the command file (if we have /cache).
+ if (argc == 1 && has_cache) {
+ std::string content;
+ if (ensure_path_mounted(COMMAND_FILE) == 0 &&
+ android::base::ReadFileToString(COMMAND_FILE, &content)) {
+ std::vector<std::string> tokens = android::base::Split(content, "\n");
+ // 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));
+ }
+ LOG(INFO) << "Got " << args.size() << " arguments from " << COMMAND_FILE;
+ }
+ }
+
+ // Write the arguments (excluding the filename in args[0]) back into the
+ // 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 (!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.
@@ -509,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.
@@ -523,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.
- ui->Print("Formatting %s...\n", volume);
+ ensure_path_mounted(volume);
- ensure_path_unmounted(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;
+ }
- int result;
+ std::string data(sb.st_size, '\0');
+ FILE* f = fopen(path.c_str(), "rb");
+ fread(&data[0], 1, data.size(), f);
+ fclose(f);
- 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;
+ log_files.emplace_back(saved_log_file{ path, sb, data });
+ }
}
- FILE* f = fopen(CONVERT_FBE_FILE, "wb");
- if (!f) {
- ui->Print("Failed to convert to file encryption %s\n", strerror(errno));
- return true;
+ }
+ } else {
+ if (errno != ENOENT) {
+ PLOG(ERROR) << "Failed to opendir " << CACHE_LOG_DIR;
+ }
+ }
+ }
+
+ ui->Print("Formatting %s...\n", volume);
+
+ ensure_path_unmounted(volume);
+
+ 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_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;
}
- fclose(f);
- result = format_volume(volume, CONVERT_FBE_DIR);
- remove(CONVERT_FBE_FILE);
- rmdir(CONVERT_FBE_DIR);
+ }
} else {
- result = format_volume(volume);
+ PLOG(ERROR) << "Failed to mount / create " << CACHE_LOG_DIR;
}
- 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;
- }
-
- // 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();
- }
+ // 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);
+ return (result == 0);
}
static int
@@ -798,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");
@@ -816,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) {
@@ -1180,8 +1172,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:
@@ -1357,7 +1355,7 @@ 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;
}
}
@@ -1430,10 +1428,14 @@ int main(int argc, char **argv) {
load_volume_table();
has_cache = volume_for_path(CACHE_ROOT) != nullptr;
- get_args(&argc, &argv);
+ std::vector<std::string> args = get_args(argc, argv);
+ std::vector<char*> args_to_parse(args.size());
+ std::transform(args.cbegin(), args.cend(), args_to_parse.begin(),
+ [](const std::string& arg) { return const_cast<char*>(arg.c_str()); });
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;
@@ -1447,7 +1449,8 @@ int main(int argc, char **argv) {
int arg;
int option_index;
- while ((arg = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) {
+ while ((arg = getopt_long(args_to_parse.size(), args_to_parse.data(), "", OPTIONS,
+ &option_index)) != -1) {
switch (arg) {
case 'n': android::base::ParseInt(optarg, &retry_count, 0); break;
case 'u': update_package = optarg; break;
@@ -1470,12 +1473,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;
}
@@ -1502,8 +1506,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);
@@ -1529,30 +1535,10 @@ int main(int argc, char **argv) {
device->StartRecovery();
printf("Command:");
- for (arg = 0; arg < argc; arg++) {
- printf(" \"%s\"", argv[arg]);
- }
- 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");
- }
+ for (const auto& arg : args) {
+ printf(" \"%s\"", arg.c_str());
}
- printf("\n");
+ printf("\n\n");
property_list(print_property, NULL);
printf("\n");
@@ -1612,9 +1598,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/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..3ad64907e 100644
--- a/screen_ui.h
+++ b/screen_ui.h
@@ -20,6 +20,8 @@
#include <pthread.h>
#include <stdio.h>
+#include <string>
+
#include "ui.h"
#include "minui/minui.h"
@@ -29,24 +31,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 +72,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 +120,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 +134,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 +149,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 +159,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..0aca8c6c7 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
@@ -61,9 +63,13 @@ 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 := \
@@ -78,6 +84,9 @@ LOCAL_STATIC_LIBRARIES := \
libapplypatch_modes \
libapplypatch \
libedify \
+ libimgdiff \
+ libimgpatch \
+ libbsdiff \
libotafault \
libupdater \
libbootloader_message \
@@ -85,6 +94,8 @@ LOCAL_STATIC_LIBRARIES := \
libminui \
libotautil \
libmounts \
+ libdivsufsort \
+ libdivsufsort64 \
libfs_mgr \
liblog \
libselinux \
@@ -125,3 +136,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..e73cb1509 100644
--- a/tests/manual/recovery_test.cpp
+++ b/tests/manual/recovery_test.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
@@ -79,7 +78,7 @@ 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");
}
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/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/ui.cpp b/ui.cpp
index 78b6e4f1b..f31660db4 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,6 +30,8 @@
#include <time.h>
#include <unistd.h>
+#include <string>
+
#include <android-base/properties.h>
#include <cutils/android_reboot.h>
@@ -35,25 +39,25 @@
#include "roots.h"
#include "device.h"
#include "minui/minui.h"
-#include "screen_ui.h"
-#include "ui.h"
#define UI_WAIT_KEY_TIMEOUT_SEC 120
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),
+ 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));
}
void RecoveryUI::OnKeyDetected(int key_code) {
@@ -80,12 +84,16 @@ static void* InputThreadLoop(void*) {
return nullptr;
}
-void RecoveryUI::Init() {
- ev_init(InputCallback, this);
+bool RecoveryUI::Init(const std::string& locale) {
+ // Set up the locale info.
+ SetLocale(locale);
- ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));
+ ev_init(InputCallback, this);
- pthread_create(&input_thread_, nullptr, InputThreadLoop, nullptr);
+ ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));
+
+ pthread_create(&input_thread_, nullptr, InputThreadLoop, nullptr);
+ return true;
}
int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) {
@@ -337,3 +345,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..8493c6f0a 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,14 @@ 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_;
+
+ private:
// Key event input queue
pthread_mutex_t key_queue_mutex;
pthread_cond_t key_queue_cond;
@@ -162,6 +167,8 @@ private:
static void* time_key_helper(void* cookie);
void time_key(int key_code, int count);
+
+ void SetLocale(const std::string&);
};
#endif // RECOVERY_UI_H
diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp
index b23f6be92..a06384dd5 100644
--- a/uncrypt/uncrypt.cpp
+++ b/uncrypt/uncrypt.cpp
@@ -496,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();
@@ -568,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() << ")";
@@ -630,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;
}
@@ -653,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/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..643145447 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"
diff --git a/wear_ui.cpp b/wear_ui.cpp
index 5433d110b..b4c63a5ae 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,11 +27,11 @@
#include <time.h>
#include <unistd.h>
+#include <string>
#include <vector>
#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"
@@ -47,32 +49,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 +63,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 +86,7 @@ void WearRecoveryUI::draw_background_locked(Icon icon)
}
}
else {
- surface = backgroundIcon[icon];
+ surface = backgroundIcon[currentIcon];
}
int width = gr_get_width(surface);
@@ -110,36 +99,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 +106,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 +153,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 +174,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();
-}
-
-void WearRecoveryUI::SetBackground(Icon icon)
-{
- pthread_mutex_lock(&updateMutex);
- currentIcon = icon;
- update_screen_locked();
- pthread_mutex_unlock(&updateMutex);
+ return true;
}
-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);
-}
+bool WearRecoveryUI::Init(const std::string& locale) {
+ if (!ScreenRecoveryUI::Init(locale)) {
+ return false;
+ }
-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);
-}
+ LoadBitmap("icon_error", &backgroundIcon[ERROR]);
+ backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR];
-void WearRecoveryUI::SetProgress(float fraction)
-{
- pthread_mutex_lock(&updateMutex);
- if (fraction < 0.0) fraction = 0.0;
- if (fraction > 1.0) fraction = 1.0;
- if (progressBarType == DETERMINATE && fraction > progress) {
- // Skip updates that aren't visibly different.
- int width = progress_bar_width;
- float scale = width * progressScopeSize;
- if ((int) (progress * scale) != (int) (fraction * scale)) {
- progress = fraction;
- update_screen_locked();
- }
- }
- pthread_mutex_unlock(&updateMutex);
-}
+ // This leaves backgroundIcon[INSTALLING_UPDATE] and backgroundIcon[ERASING]
+ // as NULL which is fine since draw_background_locked() doesn't use them.
-void WearRecoveryUI::SetStage(int current, int max)
-{
+ return true;
}
-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 +280,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 +299,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 +338,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 +352,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 +372,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 +397,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