summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--applypatch/applypatch.cpp105
-rw-r--r--applypatch/applypatch.h12
-rw-r--r--applypatch/main.cpp67
-rw-r--r--install.cpp6
-rw-r--r--install.h3
-rw-r--r--otafault/ota_io.cpp68
-rw-r--r--recovery.cpp47
-rw-r--r--screen_ui.cpp40
-rw-r--r--screen_ui.h14
-rw-r--r--tests/Android.mk13
-rw-r--r--tests/common/test_constants.h25
-rw-r--r--tests/component/applypatch_test.cpp392
-rw-r--r--tests/component/verifier_test.cpp7
-rw-r--r--tests/testdata/new.filebin0 -> 1388877 bytes
-rw-r--r--tests/testdata/old.filebin0 -> 1348051 bytes
-rw-r--r--tests/testdata/patch.bsdiffbin0 -> 57476 bytes
-rw-r--r--updater/install.cpp21
-rw-r--r--updater/updater.cpp7
-rw-r--r--wear_ui.cpp134
-rw-r--r--wear_ui.h19
20 files changed, 743 insertions, 237 deletions
diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp
index 9f5e2f200..9d8a21797 100644
--- a/applypatch/applypatch.cpp
+++ b/applypatch/applypatch.cpp
@@ -56,8 +56,6 @@ static bool mtd_partitions_scanned = false;
//
// Return 0 on success.
int LoadFileContents(const char* filename, FileContents* file) {
- file->data = NULL;
-
// A special 'filename' beginning with "MTD:" or "EMMC:" means to
// load the contents of a partition.
if (strncmp(filename, "MTD:", 4) == 0 ||
@@ -70,31 +68,22 @@ int LoadFileContents(const char* filename, FileContents* file) {
return -1;
}
- file->size = file->st.st_size;
- file->data = nullptr;
-
- std::unique_ptr<unsigned char, decltype(&free)> data(
- static_cast<unsigned char*>(malloc(file->size)), free);
- if (data == nullptr) {
- printf("failed to allocate memory: %s\n", strerror(errno));
- return -1;
- }
-
+ std::vector<unsigned char> data(file->st.st_size);
FILE* f = ota_fopen(filename, "rb");
if (f == NULL) {
printf("failed to open \"%s\": %s\n", filename, strerror(errno));
return -1;
}
- size_t bytes_read = ota_fread(data.get(), 1, file->size, f);
- if (bytes_read != static_cast<size_t>(file->size)) {
- printf("short read of \"%s\" (%zu bytes of %zd)\n", filename, bytes_read, file->size);
- fclose(f);
+ size_t bytes_read = ota_fread(data.data(), 1, data.size(), f);
+ if (bytes_read != data.size()) {
+ printf("short read of \"%s\" (%zu bytes of %zd)\n", filename, bytes_read, data.size());
+ ota_fclose(f);
return -1;
}
ota_fclose(f);
- file->data = data.release();
- SHA1(file->data, file->size, file->sha1);
+ file->data = std::move(data);
+ SHA1(file->data.data(), file->data.size(), file->sha1);
return 0;
}
@@ -193,17 +182,17 @@ static int LoadPartitionContents(const char* filename, FileContents* file) {
uint8_t parsed_sha[SHA_DIGEST_LENGTH];
// Allocate enough memory to hold the largest size.
- file->data = static_cast<unsigned char*>(malloc(size[index[pairs-1]]));
- char* p = (char*)file->data;
- file->size = 0; // # bytes read so far
+ std::vector<unsigned char> data(size[index[pairs-1]]);
+ char* p = reinterpret_cast<char*>(data.data());
+ size_t data_size = 0; // # bytes read so far
bool found = false;
for (size_t i = 0; i < pairs; ++i) {
// Read enough additional bytes to get us up to the next size. (Again,
// we're trying the possibilities in order of increasing size).
- size_t next = size[index[i]] - file->size;
- size_t read = 0;
+ size_t next = size[index[i]] - data_size;
if (next > 0) {
+ size_t read = 0;
switch (type) {
case MTD:
read = mtd_read_data(ctx, p, next);
@@ -216,12 +205,11 @@ static int LoadPartitionContents(const char* filename, FileContents* file) {
if (next != read) {
printf("short read (%zu bytes of %zu) for partition \"%s\"\n",
read, next, partition);
- free(file->data);
- file->data = NULL;
return -1;
}
SHA1_Update(&sha_ctx, p, read);
- file->size += read;
+ data_size += read;
+ p += read;
}
// Duplicate the SHA context and finalize the duplicate so we can
@@ -233,8 +221,6 @@ static int LoadPartitionContents(const char* filename, FileContents* file) {
if (ParseSha1(sha1sum[index[i]].c_str(), parsed_sha) != 0) {
printf("failed to parse sha1 %s in %s\n", sha1sum[index[i]].c_str(), filename);
- free(file->data);
- file->data = NULL;
return -1;
}
@@ -246,8 +232,6 @@ static int LoadPartitionContents(const char* filename, FileContents* file) {
found = true;
break;
}
-
- p += read;
}
switch (type) {
@@ -264,13 +248,13 @@ static int LoadPartitionContents(const char* filename, FileContents* file) {
if (!found) {
// Ran off the end of the list of (size,sha1) pairs without finding a match.
printf("contents of partition \"%s\" didn't match %s\n", partition, filename);
- free(file->data);
- file->data = NULL;
return -1;
}
SHA1_Final(file->sha1, &sha_ctx);
+ data.resize(data_size);
+ file->data = std::move(data);
// Fake some stat() info.
file->st.st_mode = 0644;
file->st.st_uid = 0;
@@ -289,10 +273,10 @@ int SaveFileContents(const char* filename, const FileContents* file) {
return -1;
}
- ssize_t bytes_written = FileSink(file->data, file->size, &fd);
- if (bytes_written != file->size) {
- printf("short write of \"%s\" (%zd bytes of %zd) (%s)\n",
- filename, bytes_written, file->size, strerror(errno));
+ ssize_t bytes_written = FileSink(file->data.data(), file->data.size(), &fd);
+ if (bytes_written != static_cast<ssize_t>(file->data.size())) {
+ printf("short write of \"%s\" (%zd bytes of %zu) (%s)\n",
+ filename, bytes_written, file->data.size(), strerror(errno));
ota_close(fd);
return -1;
}
@@ -543,7 +527,6 @@ int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str,
int applypatch_check(const char* filename, int num_patches,
char** const patch_sha1_str) {
FileContents file;
- file.data = NULL;
// It's okay to specify no sha1s; the check will pass if the
// LoadFileContents is successful. (Useful for reading
@@ -555,9 +538,6 @@ int applypatch_check(const char* filename, int num_patches,
printf("file \"%s\" doesn't have any of expected "
"sha1 sums; checking cache\n", filename);
- free(file.data);
- file.data = NULL;
-
// If the source file is missing or corrupted, it might be because
// we were killed in the middle of patching it. A copy of it
// should have been made in CACHE_TEMP_SOURCE. If that file
@@ -571,12 +551,9 @@ int applypatch_check(const char* filename, int num_patches,
if (FindMatchingPatch(file.sha1, patch_sha1_str, num_patches) < 0) {
printf("cache bits don't match any sha1 for \"%s\"\n", filename);
- free(file.data);
return 1;
}
}
-
- free(file.data);
return 0;
}
@@ -674,8 +651,6 @@ int applypatch(const char* source_filename,
FileContents copy_file;
FileContents source_file;
- copy_file.data = NULL;
- source_file.data = NULL;
const Value* source_patch_value = NULL;
const Value* copy_patch_value = NULL;
@@ -685,22 +660,20 @@ int applypatch(const char* source_filename,
// The early-exit case: the patch was already applied, this file
// has the desired hash, nothing for us to do.
printf("already %s\n", short_sha1(target_sha1).c_str());
- free(source_file.data);
return 0;
}
}
- if (source_file.data == NULL ||
+ if (source_file.data.empty() ||
(target_filename != source_filename &&
strcmp(target_filename, source_filename) != 0)) {
// Need to load the source file: either we failed to load the
// target file, or we did but it's different from the source file.
- free(source_file.data);
- source_file.data = NULL;
+ source_file.data.clear();
LoadFileContents(source_filename, &source_file);
}
- if (source_file.data != NULL) {
+ if (!source_file.data.empty()) {
int to_use = FindMatchingPatch(source_file.sha1, patch_sha1_str, num_patches);
if (to_use >= 0) {
source_patch_value = patch_data[to_use];
@@ -708,8 +681,7 @@ int applypatch(const char* source_filename,
}
if (source_patch_value == NULL) {
- free(source_file.data);
- source_file.data = NULL;
+ source_file.data.clear();
printf("source file is bad; trying copy\n");
if (LoadFileContents(CACHE_TEMP_SOURCE, &copy_file) < 0) {
@@ -726,19 +698,14 @@ int applypatch(const char* source_filename,
if (copy_patch_value == NULL) {
// fail.
printf("copy file doesn't match source SHA-1s either\n");
- free(copy_file.data);
return 1;
}
}
- int result = GenerateTarget(&source_file, source_patch_value,
- &copy_file, copy_patch_value,
- source_filename, target_filename,
- target_sha1, target_size, bonus_data);
- free(source_file.data);
- free(copy_file.data);
-
- return result;
+ return GenerateTarget(&source_file, source_patch_value,
+ &copy_file, copy_patch_value,
+ source_filename, target_filename,
+ target_sha1, target_size, bonus_data);
}
/*
@@ -759,7 +726,6 @@ int applypatch_flash(const char* source_filename, const char* target_filename,
}
FileContents source_file;
- source_file.data = NULL;
std::string target_str(target_filename);
std::vector<std::string> pieces = android::base::Split(target_str, ":");
@@ -777,7 +743,6 @@ int applypatch_flash(const char* source_filename, const char* target_filename,
// The early-exit case: the image was already applied, this partition
// has the desired hash, nothing for us to do.
printf("already %s\n", short_sha1(target_sha1).c_str());
- free(source_file.data);
return 0;
}
@@ -787,18 +752,14 @@ int applypatch_flash(const char* source_filename, const char* target_filename,
printf("source \"%s\" doesn't have expected sha1 sum\n", source_filename);
printf("expected: %s, found: %s\n", short_sha1(target_sha1).c_str(),
short_sha1(source_file.sha1).c_str());
- free(source_file.data);
return 1;
}
}
- if (WriteToPartition(source_file.data, target_size, target_filename) != 0) {
+ if (WriteToPartition(source_file.data.data(), target_size, target_filename) != 0) {
printf("write of copied data to %s failed\n", target_filename);
- free(source_file.data);
return 1;
}
-
- free(source_file.data);
return 0;
}
@@ -867,7 +828,7 @@ static int GenerateTarget(FileContents* source_file,
// We still write the original source to cache, in case
// the partition write is interrupted.
- if (MakeFreeSpaceOnCache(source_file->size) < 0) {
+ if (MakeFreeSpaceOnCache(source_file->data.size()) < 0) {
printf("not enough free space on /cache\n");
return 1;
}
@@ -908,7 +869,7 @@ static int GenerateTarget(FileContents* source_file,
return 1;
}
- if (MakeFreeSpaceOnCache(source_file->size) < 0) {
+ if (MakeFreeSpaceOnCache(source_file->data.size()) < 0) {
printf("not enough free space on /cache\n");
return 1;
}
@@ -951,10 +912,10 @@ static int GenerateTarget(FileContents* source_file,
int result;
if (use_bsdiff) {
- result = ApplyBSDiffPatch(source_to_use->data, source_to_use->size,
+ result = ApplyBSDiffPatch(source_to_use->data.data(), source_to_use->data.size(),
patch, 0, sink, token, &ctx);
} else {
- result = ApplyImagePatch(source_to_use->data, source_to_use->size,
+ result = ApplyImagePatch(source_to_use->data.data(), source_to_use->data.size(),
patch, sink, token, &ctx, bonus_data);
}
diff --git a/applypatch/applypatch.h b/applypatch/applypatch.h
index 14fb490ba..f392c5534 100644
--- a/applypatch/applypatch.h
+++ b/applypatch/applypatch.h
@@ -24,17 +24,11 @@
#include "openssl/sha.h"
#include "edify/expr.h"
-typedef struct _Patch {
+struct FileContents {
uint8_t sha1[SHA_DIGEST_LENGTH];
- const char* patch_filename;
-} Patch;
-
-typedef struct _FileContents {
- uint8_t sha1[SHA_DIGEST_LENGTH];
- unsigned char* data;
- ssize_t size;
+ std::vector<unsigned char> data;
struct stat st;
-} FileContents;
+};
// When there isn't enough room on the target filesystem to hold the
// patched version of the file, we copy the original here and delete
diff --git a/applypatch/main.cpp b/applypatch/main.cpp
index 7606d5d3c..9013760c4 100644
--- a/applypatch/main.cpp
+++ b/applypatch/main.cpp
@@ -46,40 +46,32 @@ static int SpaceMode(int argc, char** argv) {
return CacheSizeCheck(bytes);
}
-// Parse arguments (which should be of the form "<sha1>" or
-// "<sha1>:<filename>" into the new parallel arrays *sha1s and
-// *patches (loading file contents into the patches). Returns true on
+// Parse arguments (which should be of the form "<sha1>:<filename>"
+// into the new parallel arrays *sha1s and *files.Returns true on
// success.
static bool ParsePatchArgs(int argc, char** argv, std::vector<char*>* sha1s,
- std::vector<std::unique_ptr<Value, decltype(&FreeValue)>>* patches) {
+ std::vector<FileContents>* files) {
uint8_t digest[SHA_DIGEST_LENGTH];
for (int i = 0; i < argc; ++i) {
char* colon = strchr(argv[i], ':');
- if (colon != NULL) {
- *colon = '\0';
- ++colon;
+ if (colon == nullptr) {
+ printf("no ':' in patch argument \"%s\"\n", argv[i]);
+ return false;
}
-
+ *colon = '\0';
+ ++colon;
if (ParseSha1(argv[i], digest) != 0) {
printf("failed to parse sha1 \"%s\"\n", argv[i]);
return false;
}
sha1s->push_back(argv[i]);
- if (colon == NULL) {
- patches->emplace_back(nullptr, FreeValue);
- } else {
- FileContents fc;
- if (LoadFileContents(colon, &fc) != 0) {
- return false;
- }
- std::unique_ptr<Value, decltype(&FreeValue)> value(new Value, FreeValue);
- value->type = VAL_BLOB;
- value->size = fc.size;
- value->data = reinterpret_cast<char*>(fc.data);
- patches->push_back(std::move(value));
+ FileContents fc;
+ if (LoadFileContents(colon, &fc) != 0) {
+ return false;
}
+ files->push_back(std::move(fc));
}
return true;
}
@@ -90,17 +82,19 @@ static int FlashMode(const char* src_filename, const char* tgt_filename,
}
static int PatchMode(int argc, char** argv) {
- std::unique_ptr<Value, decltype(&FreeValue)> bonus(nullptr, FreeValue);
+ FileContents bonusFc;
+ Value bonusValue;
+ Value* bonus = nullptr;
+
if (argc >= 3 && strcmp(argv[1], "-b") == 0) {
- FileContents fc;
- if (LoadFileContents(argv[2], &fc) != 0) {
+ if (LoadFileContents(argv[2], &bonusFc) != 0) {
printf("failed to load bonus file %s\n", argv[2]);
return 1;
}
- bonus.reset(new Value);
+ bonus = &bonusValue;
bonus->type = VAL_BLOB;
- bonus->size = fc.size;
- bonus->data = reinterpret_cast<char*>(fc.data);
+ bonus->size = bonusFc.data.size();
+ bonus->data = reinterpret_cast<char*>(bonusFc.data.data());
argc -= 2;
argv += 2;
}
@@ -118,28 +112,29 @@ static int PatchMode(int argc, char** argv) {
// If no <src-sha1>:<patch> is provided, it is in flash mode.
if (argc == 5) {
- if (bonus != NULL) {
+ if (bonus != nullptr) {
printf("bonus file not supported in flash mode\n");
return 1;
}
return FlashMode(argv[1], argv[2], argv[3], target_size);
}
-
-
std::vector<char*> sha1s;
- std::vector<std::unique_ptr<Value, decltype(&FreeValue)>> patches;
- if (!ParsePatchArgs(argc-5, argv+5, &sha1s, &patches)) {
+ std::vector<FileContents> files;
+ if (!ParsePatchArgs(argc-5, argv+5, &sha1s, &files)) {
printf("failed to parse patch args\n");
return 1;
}
-
- std::vector<Value*> patch_ptrs;
- for (const auto& p : patches) {
- patch_ptrs.push_back(p.get());
+ std::vector<Value> patches(files.size());
+ std::vector<Value*> patch_ptrs(files.size());
+ for (size_t i = 0; i < files.size(); ++i) {
+ patches[i].type = VAL_BLOB;
+ patches[i].size = files[i].data.size();
+ patches[i].data = reinterpret_cast<char*>(files[i].data.data());
+ patch_ptrs[i] = &patches[i];
}
return applypatch(argv[1], argv[2], argv[3], target_size,
patch_ptrs.size(), sha1s.data(),
- patch_ptrs.data(), bonus.get());
+ patch_ptrs.data(), bonus);
}
// This program applies binary patches to files in a way that is safe
diff --git a/install.cpp b/install.cpp
index c0d007709..8a82d7bc4 100644
--- a/install.cpp
+++ b/install.cpp
@@ -144,6 +144,7 @@ try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache) {
close(pipefd[1]);
*wipe_cache = false;
+ bool retry_update = false;
char buffer[1024];
FILE* from_child = fdopen(pipefd[0], "r");
@@ -180,6 +181,8 @@ try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache) {
// 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 {
LOGE("unknown command [%s]\n", command);
}
@@ -188,6 +191,9 @@ try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache) {
int status;
waitpid(pid, &status, 0);
+ if (retry_update) {
+ return INSTALL_RETRY;
+ }
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status));
return INSTALL_ERROR;
diff --git a/install.h b/install.h
index f92f061df..fd08e3c49 100644
--- a/install.h
+++ b/install.h
@@ -23,7 +23,8 @@
extern "C" {
#endif
-enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_NONE, INSTALL_SKIPPED };
+enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_NONE, INSTALL_SKIPPED,
+ INSTALL_RETRY };
// Install the package specified by root_path. If INSTALL_SUCCESS is
// returned and *wipe_cache is true on exit, caller should wipe the
// cache partition.
diff --git a/otafault/ota_io.cpp b/otafault/ota_io.cpp
index 02e80f9bf..a37804088 100644
--- a/otafault/ota_io.cpp
+++ b/otafault/ota_io.cpp
@@ -38,6 +38,8 @@ static std::string FaultFileName =
#endif // defined (TARGET_READ_FAULT)
#endif // defined (TARGET_INJECT_FAULTS)
+bool have_eio_error = false;
+
int ota_open(const char* path, int oflags) {
#if defined (TARGET_INJECT_FAULTS)
// Let the caller handle errors; we do not care if open succeeds or fails
@@ -90,12 +92,22 @@ size_t ota_fread(void* ptr, size_t size, size_t nitems, FILE* stream) {
&& FilenameCache[(intptr_t)stream] == FaultFileName) {
FaultFileName = "";
errno = EIO;
+ have_eio_error = true;
return 0;
} else {
- return fread(ptr, size, nitems, stream);
+ size_t status = fread(ptr, size, nitems, stream);
+ // If I/O error occurs, set the retry-update flag.
+ if (status != nitems && errno == EIO) {
+ have_eio_error = true;
+ }
+ return status;
}
#else
- return fread(ptr, size, nitems, stream);
+ size_t status = fread(ptr, size, nitems, stream);
+ if (status != nitems && errno == EIO) {
+ have_eio_error = true;
+ }
+ return status;
#endif
}
@@ -105,12 +117,21 @@ ssize_t ota_read(int fd, void* buf, size_t nbyte) {
&& FilenameCache[fd] == FaultFileName) {
FaultFileName = "";
errno = EIO;
+ have_eio_error = true;
return -1;
} else {
- return read(fd, buf, nbyte);
+ ssize_t status = read(fd, buf, nbyte);
+ if (status == -1 && errno == EIO) {
+ have_eio_error = true;
+ }
+ return status;
}
#else
- return read(fd, buf, nbyte);
+ ssize_t status = read(fd, buf, nbyte);
+ if (status == -1 && errno == EIO) {
+ have_eio_error = true;
+ }
+ return status;
#endif
}
@@ -120,12 +141,21 @@ size_t ota_fwrite(const void* ptr, size_t size, size_t count, FILE* stream) {
&& FilenameCache[(intptr_t)stream] == FaultFileName) {
FaultFileName = "";
errno = EIO;
+ have_eio_error = true;
return 0;
} else {
- return fwrite(ptr, size, count, stream);
+ size_t status = fwrite(ptr, size, count, stream);
+ if (status != count && errno == EIO) {
+ have_eio_error = true;
+ }
+ return status;
}
#else
- return fwrite(ptr, size, count, stream);
+ size_t status = fwrite(ptr, size, count, stream);
+ if (status != count && errno == EIO) {
+ have_eio_error = true;
+ }
+ return status;
#endif
}
@@ -135,12 +165,21 @@ ssize_t ota_write(int fd, const void* buf, size_t nbyte) {
&& FilenameCache[fd] == FaultFileName) {
FaultFileName = "";
errno = EIO;
+ have_eio_error = true;
return -1;
} else {
- return write(fd, buf, nbyte);
+ ssize_t status = write(fd, buf, nbyte);
+ if (status == -1 && errno == EIO) {
+ have_eio_error = true;
+ }
+ return status;
}
#else
- return write(fd, buf, nbyte);
+ ssize_t status = write(fd, buf, nbyte);
+ if (status == -1 && errno == EIO) {
+ have_eio_error = true;
+ }
+ return status;
#endif
}
@@ -151,10 +190,19 @@ int ota_fsync(int fd) {
FaultFileName = "";
errno = EIO;
return -1;
+ have_eio_error = true;
} else {
- return fsync(fd);
+ int status = fsync(fd);
+ if (status == -1 && errno == EIO) {
+ have_eio_error = true;
+ }
+ return status;
}
#else
- return fsync(fd);
+ int status = fsync(fd);
+ if (status == -1 && errno == EIO) {
+ have_eio_error = true;
+ }
+ return status;
#endif
}
diff --git a/recovery.cpp b/recovery.cpp
index fe6793da0..7620f1a00 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -36,6 +36,7 @@
#include <adb.h>
#include <android-base/file.h>
+#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
#include <cutils/android_reboot.h>
#include <cutils/properties.h>
@@ -60,6 +61,7 @@ struct selabel_handle *sehandle;
static const struct option OPTIONS[] = {
{ "send_intent", required_argument, NULL, 'i' },
{ "update_package", required_argument, NULL, 'u' },
+ { "retry_count", required_argument, NULL, 'n' },
{ "wipe_data", no_argument, NULL, 'w' },
{ "wipe_cache", no_argument, NULL, 'c' },
{ "show_text", no_argument, NULL, 't' },
@@ -89,6 +91,7 @@ static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install";
static const char *LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
static const char *LAST_LOG_FILE = "/cache/recovery/last_log";
static const int KEEP_LOG_COUNT = 10;
+static const int EIO_RETRY_COUNT = 2;
static const int BATTERY_READ_TIMEOUT_IN_SEC = 10;
// GmsCore enters recovery mode to install package when having enough battery
// percentage. Normally, the threshold is 40% without charger and 20% with charger.
@@ -1162,6 +1165,29 @@ static bool is_battery_ok() {
}
}
+static void set_retry_bootloader_message(int retry_count, int argc, char** argv) {
+ struct bootloader_message boot {};
+ strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
+ strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
+
+ for (int i = 1; i < argc; ++i) {
+ if (strstr(argv[i], "retry_count") == nullptr) {
+ strlcat(boot.recovery, argv[i], sizeof(boot.recovery));
+ strlcat(boot.recovery, "\n", sizeof(boot.recovery));
+ }
+ }
+
+ // Initialize counter to 1 if it's not in BCB, otherwise increment it by 1.
+ if (retry_count == 0) {
+ strlcat(boot.recovery, "--retry_count=1\n", sizeof(boot.recovery));
+ } else {
+ char buffer[20];
+ snprintf(buffer, sizeof(buffer), "--retry_count=%d\n", retry_count+1);
+ strlcat(boot.recovery, buffer, sizeof(boot.recovery));
+ }
+ set_bootloader_message(&boot);
+}
+
int main(int argc, char **argv) {
// If this binary is started with the single argument "--adbd",
// instead of being the normal recovery binary, it turns into kind
@@ -1197,11 +1223,13 @@ int main(int argc, char **argv) {
bool sideload_auto_reboot = false;
bool just_exit = false;
bool shutdown_after = false;
+ int retry_count = 0;
int arg;
while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {
switch (arg) {
case 'i': send_intent = optarg; break;
+ case 'n': android::base::ParseInt(optarg, &retry_count, 0); break;
case 'u': update_package = optarg; break;
case 'w': should_wipe_data = true; break;
case 'c': should_wipe_cache = true; break;
@@ -1306,7 +1334,24 @@ int main(int argc, char **argv) {
}
if (status != INSTALL_SUCCESS) {
ui->Print("Installation aborted.\n");
-
+ // When I/O error happens, reboot and retry installation EIO_RETRY_COUNT
+ // times before we abandon this OTA update.
+ if (status == INSTALL_RETRY && retry_count < EIO_RETRY_COUNT) {
+ copy_logs();
+ set_retry_bootloader_message(retry_count, argc, argv);
+ // Print retry count on screen.
+ ui->Print("Retry attempt %d\n", retry_count);
+
+ // Reboot and retry the update
+ int ret = property_set(ANDROID_RB_PROPERTY, "reboot,recovery");
+ if (ret < 0) {
+ ui->Print("Reboot failed\n");
+ } else {
+ while (true) {
+ pause();
+ }
+ }
+ }
// If this is an eng or userdebug build, then automatically
// turn the text display on if the script fails so the error
// message is visible.
diff --git a/screen_ui.cpp b/screen_ui.cpp
index dc596314c..3614e7a83 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -40,8 +40,7 @@
#include "screen_ui.h"
#include "ui.h"
-static int char_width;
-static int char_height;
+#define TEXT_INDENT 4
// Return the current time as a double (including fractions of a second).
static double now() {
@@ -51,9 +50,9 @@ static double now() {
}
ScreenRecoveryUI::ScreenRecoveryUI() :
+ currentIcon(NONE),
installingFrame(0),
locale(nullptr),
- rtl_locale(false),
progressBarType(EMPTY),
progressScopeStart(0),
progressScopeSize(0),
@@ -76,7 +75,7 @@ ScreenRecoveryUI::ScreenRecoveryUI() :
installing_frames(-1),
stage(-1),
max_stage(-1),
- currentIcon(NONE) {
+ rtl_locale(false) {
for (int i = 0; i < 5; i++) {
backgroundIcon[i] = nullptr;
@@ -213,14 +212,14 @@ void ScreenRecoveryUI::DrawHorizontalRule(int* y) {
*y += 4;
}
-void ScreenRecoveryUI::DrawTextLine(int* y, const char* line, bool bold) {
- gr_text(4, *y, line, bold);
- *y += char_height + 4;
+void ScreenRecoveryUI::DrawTextLine(int x, int* y, const char* line, bool bold) {
+ gr_text(x, *y, line, bold);
+ *y += char_height_ + 4;
}
-void ScreenRecoveryUI::DrawTextLines(int* y, const char* const* lines) {
+void ScreenRecoveryUI::DrawTextLines(int x, int* y, const char* const* lines) {
for (size_t i = 0; lines != nullptr && lines[i] != nullptr; ++i) {
- DrawTextLine(y, lines[i], false);
+ DrawTextLine(x, y, lines[i], false);
}
}
@@ -251,14 +250,15 @@ void ScreenRecoveryUI::draw_screen_locked() {
property_get("ro.bootimage.build.fingerprint", recovery_fingerprint, "");
SetColor(INFO);
- DrawTextLine(&y, "Android Recovery", true);
+ DrawTextLine(TEXT_INDENT, &y, "Android Recovery", true);
for (auto& chunk : android::base::Split(recovery_fingerprint, ":")) {
- DrawTextLine(&y, chunk.c_str(), false);
+ DrawTextLine(TEXT_INDENT, &y, chunk.c_str(), false);
}
- DrawTextLines(&y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP);
+ DrawTextLines(TEXT_INDENT, &y,
+ HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP);
SetColor(HEADER);
- DrawTextLines(&y, menu_headers_);
+ DrawTextLines(TEXT_INDENT, &y, menu_headers_);
SetColor(MENU);
DrawHorizontalRule(&y);
@@ -267,7 +267,7 @@ void ScreenRecoveryUI::draw_screen_locked() {
if (i == menu_sel) {
// Draw the highlight bar.
SetColor(IsLongPress() ? MENU_SEL_BG_ACTIVE : MENU_SEL_BG);
- gr_fill(0, y - 2, gr_fb_width(), y + char_height + 2);
+ gr_fill(0, y - 2, gr_fb_width(), y + char_height_ + 2);
// Bold white text for the selected item.
SetColor(MENU_SEL_FG);
gr_text(4, y, menu_[i], true);
@@ -275,7 +275,7 @@ void ScreenRecoveryUI::draw_screen_locked() {
} else {
gr_text(4, y, menu_[i], false);
}
- y += char_height + 4;
+ y += char_height_ + 4;
}
DrawHorizontalRule(&y);
}
@@ -286,9 +286,9 @@ void ScreenRecoveryUI::draw_screen_locked() {
SetColor(LOG);
int row = (text_top_ + text_rows_ - 1) % text_rows_;
size_t count = 0;
- for (int ty = gr_fb_height() - char_height;
+ for (int ty = gr_fb_height() - char_height_;
ty >= y && count < text_rows_;
- ty -= char_height, ++count) {
+ ty -= char_height_, ++count) {
gr_text(0, ty, text_[row], false);
--row;
if (row < 0) row = text_rows_ - 1;
@@ -394,9 +394,9 @@ static char** Alloc2d(size_t rows, size_t cols) {
void ScreenRecoveryUI::Init() {
gr_init();
- gr_font_size(&char_width, &char_height);
- text_rows_ = gr_fb_height() / char_height;
- text_cols_ = gr_fb_width() / char_width;
+ gr_font_size(&char_width_, &char_height_);
+ text_rows_ = gr_fb_height() / char_height_;
+ text_cols_ = gr_fb_width() / char_width_;
text_ = Alloc2d(text_rows_, text_cols_ + 1);
file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1);
diff --git a/screen_ui.h b/screen_ui.h
index 386deac2d..9e1b2dfa1 100644
--- a/screen_ui.h
+++ b/screen_ui.h
@@ -68,11 +68,10 @@ class ScreenRecoveryUI : public RecoveryUI {
void SetColor(UIElement e);
private:
+ Icon currentIcon;
int installingFrame;
const char* locale;
- bool rtl_locale;
- pthread_mutex_t updateMutex;
GRSurface* backgroundIcon[5];
GRSurface* backgroundText[5];
GRSurface** installation;
@@ -132,14 +131,17 @@ class ScreenRecoveryUI : public RecoveryUI {
void ClearText();
void DrawHorizontalRule(int* y);
- void DrawTextLine(int* y, const char* line, bool bold);
- void DrawTextLines(int* y, const char* const* lines);
- void LoadBitmap(const char* filename, GRSurface** surface);
void LoadBitmapArray(const char* filename, int* frames, int* fps, GRSurface*** surface);
void LoadLocalizedBitmap(const char* filename, GRSurface** surface);
protected:
- Icon currentIcon;
+ int char_width_;
+ int char_height_;
+ pthread_mutex_t updateMutex;
+ bool rtl_locale;
+ void LoadBitmap(const char* filename, GRSurface** surface);
+ void DrawTextLine(int x, int* y, const char* line, bool bold);
+ void DrawTextLines(int x, int* y, const char* const* lines);
};
#endif // RECOVERY_UI_H
diff --git a/tests/Android.mk b/tests/Android.mk
index 3f3c433eb..9d83c9a49 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -29,21 +29,30 @@ include $(BUILD_NATIVE_TEST)
# Component tests
include $(CLEAR_VARS)
LOCAL_CLANG := true
+LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_MODULE := recovery_component_test
LOCAL_C_INCLUDES := bootable/recovery
-LOCAL_SRC_FILES := component/verifier_test.cpp
+LOCAL_SRC_FILES := \
+ component/verifier_test.cpp \
+ component/applypatch_test.cpp
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_STATIC_LIBRARIES := \
+ libapplypatch \
+ libotafault \
+ libmtdutils \
libbase \
libverifier \
libmincrypt \
+ libcrypto_static \
libminui \
libminzip \
libcutils \
+ libbz \
+ libz \
libc
-testdata_out_path := $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
+testdata_out_path := $(TARGET_OUT_DATA_NATIVE_TESTS)/recovery
testdata_files := $(call find-subdir-files, testdata/*)
GEN := $(addprefix $(testdata_out_path)/, $(testdata_files))
diff --git a/tests/common/test_constants.h b/tests/common/test_constants.h
new file mode 100644
index 000000000..3490f6805
--- /dev/null
+++ b/tests/common/test_constants.h
@@ -0,0 +1,25 @@
+/*
+ * 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 agree 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 _OTA_TEST_CONSTANTS_H
+#define _OTA_TEST_CONSTANTS_H
+
+#if defined(__LP64__)
+#define NATIVE_TEST_PATH "/nativetest64"
+#else
+#define NATIVE_TEST_PATH "/nativetest"
+#endif
+
+#endif
diff --git a/tests/component/applypatch_test.cpp b/tests/component/applypatch_test.cpp
new file mode 100644
index 000000000..b44ddd17c
--- /dev/null
+++ b/tests/component/applypatch_test.cpp
@@ -0,0 +1,392 @@
+/*
+ * 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 agree 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 <fcntl.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/test_utils.h>
+
+#include "applypatch/applypatch.h"
+#include "common/test_constants.h"
+#include "openssl/sha.h"
+#include "print_sha1.h"
+
+static const std::string DATA_PATH = getenv("ANDROID_DATA");
+static const std::string TESTDATA_PATH = "/recovery/testdata";
+static const std::string WORK_FS = "/data";
+
+static std::string sha1sum(const std::string& fname) {
+ uint8_t digest[SHA_DIGEST_LENGTH];
+ std::string data;
+ android::base::ReadFileToString(fname, &data);
+
+ SHA1((const uint8_t*)data.c_str(), data.size(), digest);
+ return print_sha1(digest);
+}
+
+static void mangle_file(const std::string& fname) {
+ FILE* fh = fopen(&fname[0], "w");
+ int r;
+ for (int i=0; i < 1024; i++) {
+ r = rand();
+ fwrite(&r, sizeof(short), 1, fh);
+ }
+ fclose(fh);
+}
+
+static bool file_cmp(std::string& f1, std::string& f2) {
+ std::string c1;
+ std::string c2;
+ android::base::ReadFileToString(f1, &c1);
+ android::base::ReadFileToString(f2, &c2);
+ return c1 == c2;
+}
+
+static std::string from_testdata_base(const std::string fname) {
+ return android::base::StringPrintf("%s%s%s/%s",
+ &DATA_PATH[0],
+ &NATIVE_TEST_PATH[0],
+ &TESTDATA_PATH[0],
+ &fname[0]);
+}
+
+class ApplyPatchTest : public ::testing::Test {
+ public:
+ static void SetUpTestCase() {
+ // set up files
+ old_file = from_testdata_base("old.file");
+ new_file = from_testdata_base("new.file");
+ patch_file = from_testdata_base("patch.bsdiff");
+ rand_file = "/cache/applypatch_test_rand.file";
+ cache_file = "/cache/saved.file";
+
+ // write stuff to rand_file
+ android::base::WriteStringToFile("hello", rand_file);
+
+ // set up SHA constants
+ old_sha1 = sha1sum(old_file);
+ new_sha1 = sha1sum(new_file);
+ srand(time(NULL));
+ bad_sha1_a = android::base::StringPrintf("%040x", rand());
+ bad_sha1_b = android::base::StringPrintf("%040x", rand());
+
+ struct stat st;
+ stat(&new_file[0], &st);
+ new_size = st.st_size;
+ }
+
+ static std::string old_file;
+ static std::string new_file;
+ static std::string rand_file;
+ static std::string cache_file;
+ static std::string patch_file;
+
+ static std::string old_sha1;
+ static std::string new_sha1;
+ static std::string bad_sha1_a;
+ static std::string bad_sha1_b;
+
+ static size_t new_size;
+};
+
+std::string ApplyPatchTest::old_file;
+std::string ApplyPatchTest::new_file;
+
+static void cp(std::string src, std::string tgt) {
+ std::string cmd = android::base::StringPrintf("cp %s %s",
+ &src[0],
+ &tgt[0]);
+ system(&cmd[0]);
+}
+
+static void backup_old() {
+ cp(ApplyPatchTest::old_file, ApplyPatchTest::cache_file);
+}
+
+static void restore_old() {
+ cp(ApplyPatchTest::cache_file, ApplyPatchTest::old_file);
+}
+
+class ApplyPatchCacheTest : public ApplyPatchTest {
+ public:
+ virtual void SetUp() {
+ backup_old();
+ }
+
+ virtual void TearDown() {
+ restore_old();
+ }
+};
+
+class ApplyPatchFullTest : public ApplyPatchCacheTest {
+ public:
+ static void SetUpTestCase() {
+ ApplyPatchTest::SetUpTestCase();
+ unsigned long free_kb = FreeSpaceForFile(&WORK_FS[0]);
+ ASSERT_GE(free_kb * 1024, new_size * 3 / 2);
+ output_f = new TemporaryFile();
+ output_loc = std::string(output_f->path);
+
+ struct FileContents fc;
+
+ ASSERT_EQ(0, LoadFileContents(&rand_file[0], &fc));
+ Value* patch1 = new Value();
+ patch1->type = VAL_BLOB;
+ patch1->size = fc.data.size();
+ patch1->data = static_cast<char*>(malloc(fc.data.size()));
+ memcpy(patch1->data, fc.data.data(), fc.data.size());
+ patches.push_back(patch1);
+
+ ASSERT_EQ(0, LoadFileContents(&patch_file[0], &fc));
+ Value* patch2 = new Value();
+ patch2->type = VAL_BLOB;
+ patch2->size = fc.st.st_size;
+ patch2->data = static_cast<char*>(malloc(fc.data.size()));
+ memcpy(patch2->data, fc.data.data(), fc.data.size());
+ patches.push_back(patch2);
+ }
+ static void TearDownTestCase() {
+ delete output_f;
+ for (auto it = patches.begin(); it != patches.end(); ++it) {
+ free((*it)->data);
+ delete *it;
+ }
+ patches.clear();
+ }
+
+ static std::vector<Value*> patches;
+ static TemporaryFile* output_f;
+ static std::string output_loc;
+};
+
+class ApplyPatchDoubleCacheTest : public ApplyPatchFullTest {
+ public:
+ virtual void SetUp() {
+ ApplyPatchCacheTest::SetUp();
+ cp(cache_file, "/cache/reallysaved.file");
+ }
+
+ virtual void TearDown() {
+ cp("/cache/reallysaved.file", cache_file);
+ ApplyPatchCacheTest::TearDown();
+ }
+};
+
+std::string ApplyPatchTest::rand_file;
+std::string ApplyPatchTest::patch_file;
+std::string ApplyPatchTest::cache_file;
+std::string ApplyPatchTest::old_sha1;
+std::string ApplyPatchTest::new_sha1;
+std::string ApplyPatchTest::bad_sha1_a;
+std::string ApplyPatchTest::bad_sha1_b;
+
+size_t ApplyPatchTest::new_size;
+
+std::vector<Value*> ApplyPatchFullTest::patches;
+TemporaryFile* ApplyPatchFullTest::output_f;
+std::string ApplyPatchFullTest::output_loc;
+
+TEST_F(ApplyPatchTest, CheckModeSingle) {
+ char* s = &old_sha1[0];
+ ASSERT_EQ(0, applypatch_check(&old_file[0], 1, &s));
+}
+
+TEST_F(ApplyPatchTest, CheckModeMultiple) {
+ char* argv[3] = {
+ &bad_sha1_a[0],
+ &old_sha1[0],
+ &bad_sha1_b[0]
+ };
+ ASSERT_EQ(0, applypatch_check(&old_file[0], 3, argv));
+}
+
+TEST_F(ApplyPatchTest, CheckModeFailure) {
+ char* argv[2] = {
+ &bad_sha1_a[0],
+ &bad_sha1_b[0]
+ };
+ ASSERT_NE(0, applypatch_check(&old_file[0], 2, argv));
+}
+
+TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSingle) {
+ mangle_file(old_file);
+ char* s = &old_sha1[0];
+ ASSERT_EQ(0, applypatch_check(&old_file[0], 1, &s));
+}
+
+TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedMultiple) {
+ mangle_file(old_file);
+ char* argv[3] = {
+ &bad_sha1_a[0],
+ &old_sha1[0],
+ &bad_sha1_b[0]
+ };
+ ASSERT_EQ(0, applypatch_check(&old_file[0], 3, argv));
+}
+
+TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedFailure) {
+ mangle_file(old_file);
+ char* argv[2] = {
+ &bad_sha1_a[0],
+ &bad_sha1_b[0]
+ };
+ ASSERT_NE(0, applypatch_check(&old_file[0], 2, argv));
+}
+
+TEST_F(ApplyPatchCacheTest, CheckCacheMissingSingle) {
+ unlink(&old_file[0]);
+ char* s = &old_sha1[0];
+ ASSERT_EQ(0, applypatch_check(&old_file[0], 1, &s));
+}
+
+TEST_F(ApplyPatchCacheTest, CheckCacheMissingMultiple) {
+ unlink(&old_file[0]);
+ char* argv[3] = {
+ &bad_sha1_a[0],
+ &old_sha1[0],
+ &bad_sha1_b[0]
+ };
+ ASSERT_EQ(0, applypatch_check(&old_file[0], 3, argv));
+}
+
+TEST_F(ApplyPatchCacheTest, CheckCacheMissingFailure) {
+ unlink(&old_file[0]);
+ char* argv[2] = {
+ &bad_sha1_a[0],
+ &bad_sha1_b[0]
+ };
+ ASSERT_NE(0, applypatch_check(&old_file[0], 2, argv));
+}
+
+TEST_F(ApplyPatchFullTest, ApplyInPlace) {
+ std::vector<char*> sha1s;
+ sha1s.push_back(&bad_sha1_a[0]);
+ sha1s.push_back(&old_sha1[0]);
+
+ int ap_result = applypatch(&old_file[0],
+ "-",
+ &new_sha1[0],
+ new_size,
+ 2,
+ sha1s.data(),
+ patches.data(),
+ nullptr);
+ ASSERT_EQ(0, ap_result);
+ ASSERT_TRUE(file_cmp(old_file, new_file));
+ // reapply, applypatch is idempotent so it should succeed
+ ap_result = applypatch(&old_file[0],
+ "-",
+ &new_sha1[0],
+ new_size,
+ 2,
+ sha1s.data(),
+ patches.data(),
+ nullptr);
+ ASSERT_EQ(0, ap_result);
+ ASSERT_TRUE(file_cmp(old_file, new_file));
+}
+
+TEST_F(ApplyPatchFullTest, ApplyInNewLocation) {
+ std::vector<char*> sha1s;
+ sha1s.push_back(&bad_sha1_a[0]);
+ sha1s.push_back(&old_sha1[0]);
+ int ap_result = applypatch(&old_file[0],
+ &output_loc[0],
+ &new_sha1[0],
+ new_size,
+ 2,
+ sha1s.data(),
+ patches.data(),
+ nullptr);
+ ASSERT_EQ(0, ap_result);
+ ASSERT_TRUE(file_cmp(output_loc, new_file));
+ ap_result = applypatch(&old_file[0],
+ &output_loc[0],
+ &new_sha1[0],
+ new_size,
+ 2,
+ sha1s.data(),
+ patches.data(),
+ nullptr);
+ ASSERT_EQ(0, ap_result);
+ ASSERT_TRUE(file_cmp(output_loc, new_file));
+}
+
+TEST_F(ApplyPatchFullTest, ApplyCorruptedInNewLocation) {
+ mangle_file(old_file);
+ std::vector<char*> sha1s;
+ sha1s.push_back(&bad_sha1_a[0]);
+ sha1s.push_back(&old_sha1[0]);
+ int ap_result = applypatch(&old_file[0],
+ &output_loc[0],
+ &new_sha1[0],
+ new_size,
+ 2,
+ sha1s.data(),
+ patches.data(),
+ nullptr);
+ ASSERT_EQ(0, ap_result);
+ ASSERT_TRUE(file_cmp(output_loc, new_file));
+ ap_result = applypatch(&old_file[0],
+ &output_loc[0],
+ &new_sha1[0],
+ new_size,
+ 2,
+ sha1s.data(),
+ patches.data(),
+ nullptr);
+ ASSERT_EQ(0, ap_result);
+ ASSERT_TRUE(file_cmp(output_loc, new_file));
+}
+
+TEST_F(ApplyPatchDoubleCacheTest, ApplyDoubleCorruptedInNewLocation) {
+ mangle_file(old_file);
+ mangle_file(cache_file);
+
+ std::vector<char*> sha1s;
+ sha1s.push_back(&bad_sha1_a[0]);
+ sha1s.push_back(&old_sha1[0]);
+ int ap_result = applypatch(&old_file[0],
+ &output_loc[0],
+ &new_sha1[0],
+ new_size,
+ 2,
+ sha1s.data(),
+ patches.data(),
+ nullptr);
+ ASSERT_NE(0, ap_result);
+ ASSERT_FALSE(file_cmp(output_loc, new_file));
+ ap_result = applypatch(&old_file[0],
+ &output_loc[0],
+ &new_sha1[0],
+ new_size,
+ 2,
+ sha1s.data(),
+ patches.data(),
+ nullptr);
+ ASSERT_NE(0, ap_result);
+ ASSERT_FALSE(file_cmp(output_loc, new_file));
+}
diff --git a/tests/component/verifier_test.cpp b/tests/component/verifier_test.cpp
index c54aa111f..73f6ac9c4 100644
--- a/tests/component/verifier_test.cpp
+++ b/tests/component/verifier_test.cpp
@@ -29,18 +29,13 @@
#include <android-base/stringprintf.h>
#include "common.h"
+#include "common/test_constants.h"
#include "mincrypt/sha.h"
#include "mincrypt/sha256.h"
#include "minzip/SysUtil.h"
#include "ui.h"
#include "verifier.h"
-#if defined(__LP64__)
-#define NATIVE_TEST_PATH "/nativetest64"
-#else
-#define NATIVE_TEST_PATH "/nativetest"
-#endif
-
static const char* DATA_PATH = getenv("ANDROID_DATA");
static const char* TESTDATA_PATH = "/recovery/testdata/";
diff --git a/tests/testdata/new.file b/tests/testdata/new.file
new file mode 100644
index 000000000..cdeb8fd50
--- /dev/null
+++ b/tests/testdata/new.file
Binary files differ
diff --git a/tests/testdata/old.file b/tests/testdata/old.file
new file mode 100644
index 000000000..166c8732e
--- /dev/null
+++ b/tests/testdata/old.file
Binary files differ
diff --git a/tests/testdata/patch.bsdiff b/tests/testdata/patch.bsdiff
new file mode 100644
index 000000000..b78d38573
--- /dev/null
+++ b/tests/testdata/patch.bsdiff
Binary files differ
diff --git a/updater/install.cpp b/updater/install.cpp
index a2efc0b97..b7d9e85a2 100644
--- a/updater/install.cpp
+++ b/updater/install.cpp
@@ -1398,21 +1398,22 @@ Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) {
char* filename;
if (ReadArgs(state, argv, 1, &filename) < 0) return NULL;
- Value* v = reinterpret_cast<Value*>(malloc(sizeof(Value)));
+ Value* v = static_cast<Value*>(malloc(sizeof(Value)));
+ if (v == nullptr) {
+ return nullptr;
+ }
v->type = VAL_BLOB;
+ v->size = -1;
+ v->data = nullptr;
FileContents fc;
if (LoadFileContents(filename, &fc) != 0) {
- free(filename);
- v->size = -1;
- v->data = NULL;
- free(fc.data);
- return v;
+ v->data = static_cast<char*>(malloc(fc.data.size()));
+ if (v->data != nullptr) {
+ memcpy(v->data, fc.data.data(), fc.data.size());
+ v->size = fc.data.size();
+ }
}
-
- v->size = fc.size;
- v->data = (char*)fc.data;
-
free(filename);
return v;
}
diff --git a/updater/updater.cpp b/updater/updater.cpp
index 0f22e6d04..ddc01e125 100644
--- a/updater/updater.cpp
+++ b/updater/updater.cpp
@@ -35,6 +35,8 @@
// (Note it's "updateR-script", not the older "update-script".)
#define SCRIPT_NAME "META-INF/com/google/android/updater-script"
+extern bool have_eio_error;
+
struct selabel_handle *sehandle;
int main(int argc, char** argv) {
@@ -139,6 +141,11 @@ int main(int argc, char** argv) {
state.errmsg = NULL;
char* result = Evaluate(&state, root);
+
+ if (have_eio_error) {
+ fprintf(cmd_pipe, "retry_update\n");
+ }
+
if (result == NULL) {
if (state.errmsg == NULL) {
printf("script aborted (no error message)\n");
diff --git a/wear_ui.cpp b/wear_ui.cpp
index ef48b788b..2502313ff 100644
--- a/wear_ui.cpp
+++ b/wear_ui.cpp
@@ -34,9 +34,6 @@
#include "android-base/strings.h"
#include "android-base/stringprintf.h"
-static int char_width;
-static int char_height;
-
// There's only (at most) one of these objects, and global callbacks
// (for pthread_create, and the input event system) need to find it,
// so use a global variable.
@@ -59,9 +56,9 @@ WearRecoveryUI::WearRecoveryUI() :
intro_frames(22),
loop_frames(60),
animation_fps(30),
+ currentIcon(NONE),
intro_done(false),
current_frame(0),
- rtl_locale(false),
progressBarType(EMPTY),
progressScopeStart(0),
progressScopeSize(0),
@@ -80,7 +77,6 @@ WearRecoveryUI::WearRecoveryUI() :
for (size_t i = 0; i < 5; i++)
backgroundIcon[i] = NULL;
- pthread_mutex_init(&updateMutex, NULL);
self = this;
}
@@ -144,41 +140,6 @@ void WearRecoveryUI::draw_progress_locked()
}
}
-void WearRecoveryUI::SetColor(UIElement e) {
- switch (e) {
- case HEADER:
- gr_color(247, 0, 6, 255);
- break;
- case MENU:
- case MENU_SEL_BG:
- gr_color(0, 106, 157, 255);
- break;
- case MENU_SEL_FG:
- gr_color(255, 255, 255, 255);
- break;
- case LOG:
- gr_color(249, 194, 0, 255);
- break;
- case TEXT_FILL:
- gr_color(0, 0, 0, 160);
- break;
- default:
- gr_color(255, 255, 255, 255);
- break;
- }
-}
-
-void WearRecoveryUI::DrawTextLine(int x, int* y, const char* line, bool bold) {
- gr_text(x, *y, line, bold);
- *y += char_height + 4;
-}
-
-void WearRecoveryUI::DrawTextLines(int x, int* y, const char* const* lines) {
- for (size_t i = 0; lines != nullptr && lines[i] != nullptr; ++i) {
- DrawTextLine(x, y, lines[i], false);
- }
-}
-
static const char* HEADERS[] = {
"Swipe up/down to move.",
"Swipe left/right to select.",
@@ -217,7 +178,7 @@ void WearRecoveryUI::draw_screen_locked()
if (menu_items > menu_end - menu_start) {
sprintf(cur_selection_str, "Current item: %d/%d", menu_sel + 1, menu_items);
gr_text(x+4, y, cur_selection_str, 1);
- y += char_height+4;
+ y += char_height_+4;
}
// Menu begins here
@@ -228,7 +189,7 @@ void WearRecoveryUI::draw_screen_locked()
if (i == menu_sel) {
// draw the highlight bar
SetColor(MENU_SEL_BG);
- gr_fill(x, y-2, gr_fb_width()-x, y+char_height+2);
+ 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(x+4, y, menu[i], 1);
@@ -236,7 +197,7 @@ void WearRecoveryUI::draw_screen_locked()
} else {
if (menu[i][0]) gr_text(x+4, y, menu[i], 0);
}
- y += char_height+4;
+ y += char_height_+4;
}
SetColor(MENU);
y += 4;
@@ -252,9 +213,9 @@ void WearRecoveryUI::draw_screen_locked()
int ty;
int row = (text_top+text_rows-1) % text_rows;
size_t count = 0;
- for (int ty = gr_fb_height() - char_height - outer_height;
+ for (int ty = gr_fb_height() - char_height_ - outer_height;
ty > y+2 && count < text_rows;
- ty -= char_height, ++count) {
+ ty -= char_height_, ++count) {
gr_text(x+4, ty, text[row], 0);
--row;
if (row < 0) row = text_rows-1;
@@ -320,26 +281,19 @@ void WearRecoveryUI::progress_loop() {
}
}
-void WearRecoveryUI::LoadBitmap(const char* filename, GRSurface** surface) {
- int result = res_create_display_surface(filename, surface);
- if (result < 0) {
- LOGE("missing bitmap %s\n(Code %d)\n", filename, result);
- }
-}
-
void WearRecoveryUI::Init()
{
gr_init();
- gr_font_size(&char_width, &char_height);
+ gr_font_size(&char_width_, &char_height_);
text_col = text_row = 0;
- text_rows = (gr_fb_height()) / char_height;
- visible_text_rows = (gr_fb_height() - (outer_height * 2)) / char_height;
+ 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;
+ 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]);
@@ -365,6 +319,57 @@ void WearRecoveryUI::Init()
RecoveryUI::Init();
}
+void WearRecoveryUI::SetBackground(Icon icon)
+{
+ pthread_mutex_lock(&updateMutex);
+ currentIcon = icon;
+ update_screen_locked();
+ pthread_mutex_unlock(&updateMutex);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
void WearRecoveryUI::SetStage(int current, int max)
{
}
@@ -447,6 +452,16 @@ 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);
@@ -477,6 +492,13 @@ void WearRecoveryUI::ShowText(bool visible)
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<long> offsets;
offsets.push_back(ftell(fp));
diff --git a/wear_ui.h b/wear_ui.h
index 63a257244..e2d6fe072 100644
--- a/wear_ui.h
+++ b/wear_ui.h
@@ -24,6 +24,13 @@ class WearRecoveryUI : public ScreenRecoveryUI {
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);
@@ -42,9 +49,9 @@ class WearRecoveryUI : public ScreenRecoveryUI {
void StartMenu(const char* const * headers, const char* const * items,
int initial_selection);
int SelectMenu(int sel);
+ void EndMenu();
- enum UIElement { HEADER, MENU, MENU_SEL_BG, MENU_SEL_FG, LOG, TEXT_FILL };
- virtual void SetColor(UIElement e);
+ void Redraw();
protected:
int progress_bar_height, progress_bar_width;
@@ -68,13 +75,12 @@ class WearRecoveryUI : public ScreenRecoveryUI {
int animation_fps;
private:
+ Icon currentIcon;
+
bool intro_done;
int current_frame;
- bool rtl_locale;
-
- pthread_mutex_t updateMutex;
GRSurface* backgroundIcon[5];
GRSurface* *introFrames;
GRSurface* *loopFrames;
@@ -111,11 +117,8 @@ class WearRecoveryUI : public ScreenRecoveryUI {
void update_screen_locked();
static void* progress_thread(void* cookie);
void progress_loop();
- void LoadBitmap(const char* filename, GRSurface** surface);
void PutChar(char);
void ClearText();
- void DrawTextLine(int x, int* y, const char* line, bool bold);
- void DrawTextLines(int x, int* y, const char* const* lines);
void PrintV(const char*, bool, va_list);
};