[automerger skipped] DO NOT MERGE: Initialize the ZipArchive to zero before parsing skipped: e830a51351 skipped: 244aba4412 skipped: 9216bccd16 skipped: 98acdb9fce skipped: bc42a6b0eb am: 35332d7c62
am: 6b13f89f15
Change-Id: Iecb9ede3a76664e15fc2b533b20571c8052ed4ba
diff --git a/Android.mk b/Android.mk
index 95c8823..776e6ea 100644
--- a/Android.mk
+++ b/Android.mk
@@ -59,7 +59,8 @@
libvintf_recovery \
libcrypto_utils \
libcrypto \
- libbase
+ libbase \
+ libziparchive \
include $(BUILD_STATIC_LIBRARY)
@@ -78,7 +79,6 @@
ui.cpp \
vr_ui.cpp \
wear_ui.cpp \
- wear_touch.cpp \
LOCAL_MODULE := recovery
@@ -107,6 +107,36 @@
LOCAL_CFLAGS += -DRECOVERY_UI_MARGIN_WIDTH=0
endif
+ifneq ($(TARGET_RECOVERY_UI_TOUCH_LOW_THRESHOLD),)
+LOCAL_CFLAGS += -DRECOVERY_UI_TOUCH_LOW_THRESHOLD=$(TARGET_RECOVERY_UI_TOUCH_LOW_THRESHOLD)
+else
+LOCAL_CFLAGS += -DRECOVERY_UI_TOUCH_LOW_THRESHOLD=50
+endif
+
+ifneq ($(TARGET_RECOVERY_UI_TOUCH_HIGH_THRESHOLD),)
+LOCAL_CFLAGS += -DRECOVERY_UI_TOUCH_HIGH_THRESHOLD=$(TARGET_RECOVERY_UI_TOUCH_HIGH_THRESHOLD)
+else
+LOCAL_CFLAGS += -DRECOVERY_UI_TOUCH_HIGH_THRESHOLD=90
+endif
+
+ifneq ($(TARGET_RECOVERY_UI_PROGRESS_BAR_BASELINE),)
+LOCAL_CFLAGS += -DRECOVERY_UI_PROGRESS_BAR_BASELINE=$(TARGET_RECOVERY_UI_PROGRESS_BAR_BASELINE)
+else
+LOCAL_CFLAGS += -DRECOVERY_UI_PROGRESS_BAR_BASELINE=259
+endif
+
+ifneq ($(TARGET_RECOVERY_UI_ANIMATION_FPS),)
+LOCAL_CFLAGS += -DRECOVERY_UI_ANIMATION_FPS=$(TARGET_RECOVERY_UI_ANIMATION_FPS)
+else
+LOCAL_CFLAGS += -DRECOVERY_UI_ANIMATION_FPS=30
+endif
+
+ifneq ($(TARGET_RECOVERY_UI_MENU_UNUSABLE_ROWS),)
+LOCAL_CFLAGS += -DRECOVERY_UI_MENU_UNUSABLE_ROWS=$(TARGET_RECOVERY_UI_MENU_UNUSABLE_ROWS)
+else
+LOCAL_CFLAGS += -DRECOVERY_UI_MENU_UNUSABLE_ROWS=9
+endif
+
ifneq ($(TARGET_RECOVERY_UI_VR_STEREO_OFFSET),)
LOCAL_CFLAGS += -DRECOVERY_UI_VR_STEREO_OFFSET=$(TARGET_RECOVERY_UI_VR_STEREO_OFFSET)
else
@@ -203,6 +233,16 @@
LOCAL_CFLAGS := -Werror
include $(BUILD_STATIC_LIBRARY)
+# Wear default device
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := wear_device.cpp
+
+# Should match TARGET_RECOVERY_UI_LIB in BoardConfig.mk.
+LOCAL_MODULE := librecovery_ui_wear
+
+include $(BUILD_STATIC_LIBRARY)
+
# vr headset default device
# ===============================
include $(CLEAR_VARS)
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..09754c6
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,3 @@
+enh+aosp-gerrit@google.com
+tbao@google.com
+xunchang@google.com
diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp
index 41d73ab..fc24064 100644
--- a/applypatch/imgdiff.cpp
+++ b/applypatch/imgdiff.cpp
@@ -693,6 +693,20 @@
continue;
}
+ // The footer contains the size of the uncompressed data. Double-check to make sure that it
+ // matches the size of the data we got when we actually did the decompression.
+ size_t footer_index = pos + raw_data_len + GZIP_FOOTER_LEN - 4;
+ if (sz - footer_index < 4) {
+ printf("Warning: invalid footer position; treating as a nomal chunk\n");
+ continue;
+ }
+ size_t footer_size = get_unaligned<uint32_t>(img->data() + footer_index);
+ if (footer_size != uncompressed_len) {
+ printf("Warning: footer size %zu != decompressed size %zu; treating as a nomal chunk\n",
+ footer_size, uncompressed_len);
+ continue;
+ }
+
ImageChunk body(CHUNK_DEFLATE, pos, img, raw_data_len);
uncompressed_data.resize(uncompressed_len);
body.SetUncompressedData(std::move(uncompressed_data));
@@ -704,17 +718,6 @@
chunks->emplace_back(CHUNK_NORMAL, pos, img, GZIP_FOOTER_LEN);
pos += GZIP_FOOTER_LEN;
-
- // The footer (that we just skipped over) contains the size of
- // the uncompressed data. Double-check to make sure that it
- // matches the size of the data we got when we actually did
- // the decompression.
- size_t footer_size = get_unaligned<uint32_t>(img->data() + pos - 4);
- if (footer_size != body.DataLengthForPatch()) {
- printf("Error: footer size %zu != decompressed size %zu\n", footer_size,
- body.GetRawDataLength());
- return false;
- }
} else {
// Use a normal chunk to take all the contents until the next gzip chunk (or EOF); we expect
// the number of chunks to be small (5 for typical boot and recovery images).
diff --git a/install.cpp b/install.cpp
index a1f2e4f..7fbf5c0 100644
--- a/install.cpp
+++ b/install.cpp
@@ -27,6 +27,7 @@
#include <unistd.h>
#include <algorithm>
+#include <atomic>
#include <chrono>
#include <condition_variable>
#include <functional>
@@ -65,18 +66,14 @@
static std::condition_variable finish_log_temperature;
// This function parses and returns the build.version.incremental
-static int parse_build_number(const std::string& str) {
+static std::string parse_build_number(const std::string& str) {
size_t pos = str.find('=');
if (pos != std::string::npos) {
- std::string num_string = android::base::Trim(str.substr(pos+1));
- int build_number;
- if (android::base::ParseInt(num_string.c_str(), &build_number, 0)) {
- return build_number;
- }
+ return android::base::Trim(str.substr(pos+1));
}
LOG(ERROR) << "Failed to parse build number in " << str;
- return -1;
+ return "";
}
bool read_metadata_from_package(ZipArchiveHandle zip, std::string* metadata) {
@@ -113,14 +110,14 @@
for (const std::string& line : lines) {
std::string str = android::base::Trim(line);
if (android::base::StartsWith(str, "pre-build-incremental")) {
- int source_build = parse_build_number(str);
- if (source_build != -1) {
- log_buffer->push_back(android::base::StringPrintf("source_build: %d", source_build));
+ std::string source_build = parse_build_number(str);
+ if (!source_build.empty()) {
+ log_buffer->push_back("source_build: " + source_build);
}
} else if (android::base::StartsWith(str, "post-build-incremental")) {
- int target_build = parse_build_number(str);
- if (target_build != -1) {
- log_buffer->push_back(android::base::StringPrintf("target_build: %d", target_build));
+ std::string target_build = parse_build_number(str);
+ if (!target_build.empty()) {
+ log_buffer->push_back("target_build: " + target_build);
}
}
}
@@ -268,7 +265,7 @@
}
unlink(binary_path.c_str());
- int fd = creat(binary_path.c_str(), 0755);
+ int fd = open(binary_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0755);
if (fd == -1) {
PLOG(ERROR) << "Failed to create " << binary_path;
return INSTALL_ERROR;
@@ -294,11 +291,12 @@
}
#endif // !AB_OTA_UPDATER
-static void log_max_temperature(int* max_temperature) {
+static void log_max_temperature(int* max_temperature, const std::atomic<bool>& logger_finished) {
CHECK(max_temperature != nullptr);
std::mutex mtx;
std::unique_lock<std::mutex> lck(mtx);
- while (finish_log_temperature.wait_for(lck, 20s) == std::cv_status::timeout) {
+ while (!logger_finished.load() &&
+ finish_log_temperature.wait_for(lck, 20s) == std::cv_status::timeout) {
*max_temperature = std::max(*max_temperature, GetMaxValueFromThermalZone());
}
}
@@ -403,7 +401,8 @@
}
close(pipefd[1]);
- std::thread temperature_logger(log_max_temperature, max_temperature);
+ std::atomic<bool> logger_finished(false);
+ std::thread temperature_logger(log_max_temperature, max_temperature, std::ref(logger_finished));
*wipe_cache = false;
bool retry_update = false;
@@ -467,6 +466,7 @@
int status;
waitpid(pid, &status, 0);
+ logger_finished.store(true);
finish_log_temperature.notify_one();
temperature_logger.join();
diff --git a/minui/Android.mk b/minui/Android.mk
index 4dfc65f..6522fcf 100644
--- a/minui/Android.mk
+++ b/minui/Android.mk
@@ -25,7 +25,7 @@
LOCAL_WHOLE_STATIC_LIBRARIES := \
libadf \
- libdrm \
+ libdrm_platform \
libsync_recovery
LOCAL_STATIC_LIBRARIES := \
diff --git a/minui/events.cpp b/minui/events.cpp
index 0e1fd44..24c2a82 100644
--- a/minui/events.cpp
+++ b/minui/events.cpp
@@ -53,36 +53,37 @@
return (array[bit/BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))) != 0;
}
-int ev_init(ev_callback input_cb) {
- bool epollctlfail = false;
-
+int ev_init(ev_callback input_cb, bool allow_touch_inputs) {
g_epoll_fd = epoll_create(MAX_DEVICES + MAX_MISC_FDS);
if (g_epoll_fd == -1) {
return -1;
}
+ bool epollctlfail = false;
DIR* dir = opendir("/dev/input");
- if (dir != NULL) {
+ if (dir != nullptr) {
dirent* de;
while ((de = readdir(dir))) {
- // Use unsigned long to match ioctl's parameter type.
- unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT
-
- // fprintf(stderr,"/dev/input/%s\n", de->d_name);
if (strncmp(de->d_name, "event", 5)) continue;
int fd = openat(dirfd(dir), de->d_name, O_RDONLY);
if (fd == -1) continue;
+ // Use unsigned long to match ioctl's parameter type.
+ unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT
+
// Read the evbits of the input device.
if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
close(fd);
continue;
}
- // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed.
+ // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed. EV_ABS is also
+ // allowed if allow_touch_inputs is set.
if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) {
- close(fd);
- continue;
+ if (!allow_touch_inputs || !test_bit(EV_ABS, ev_bits)) {
+ close(fd);
+ continue;
+ }
}
epoll_event ev;
@@ -231,3 +232,27 @@
}
}
}
+
+void ev_iterate_touch_inputs(const std::function<void(int)>& action) {
+ for (size_t i = 0; i < ev_dev_count; ++i) {
+ // Use unsigned long to match ioctl's parameter type.
+ unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)] = {}; // NOLINT
+ if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
+ continue;
+ }
+ if (!test_bit(EV_ABS, ev_bits)) {
+ continue;
+ }
+
+ unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)] = {}; // NOLINT
+ if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(EV_ABS, KEY_MAX), key_bits) == -1) {
+ continue;
+ }
+
+ for (int key_code = 0; key_code <= KEY_MAX; ++key_code) {
+ if (test_bit(key_code, key_bits)) {
+ action(key_code);
+ }
+ }
+ }
+}
diff --git a/minui/include/minui/minui.h b/minui/include/minui/minui.h
index 78dd4cb..017ddde 100644
--- a/minui/include/minui/minui.h
+++ b/minui/include/minui/minui.h
@@ -74,10 +74,11 @@
using ev_callback = std::function<int(int fd, uint32_t epevents)>;
using ev_set_key_callback = std::function<int(int code, int value)>;
-int ev_init(ev_callback input_cb);
+int ev_init(ev_callback input_cb, bool allow_touch_inputs = false);
void ev_exit();
int ev_add_fd(int fd, ev_callback cb);
void ev_iterate_available_keys(const std::function<void(int)>& f);
+void ev_iterate_touch_inputs(const std::function<void(int)>& action);
int ev_sync_key_state(const ev_set_key_callback& set_key_cb);
// 'timeout' has the same semantics as poll(2).
diff --git a/minui/resources.cpp b/minui/resources.cpp
index 86c731b..8f8d36d 100644
--- a/minui/resources.cpp
+++ b/minui/resources.cpp
@@ -56,7 +56,7 @@
snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);
resPath[sizeof(resPath)-1] = '\0';
- FILE* fp = fopen(resPath, "rb");
+ FILE* fp = fopen(resPath, "rbe");
if (fp == NULL) {
result = -1;
goto exit;
diff --git a/recovery-persist.cpp b/recovery-persist.cpp
index d706cca..dbce7ff 100644
--- a/recovery-persist.cpp
+++ b/recovery-persist.cpp
@@ -59,21 +59,21 @@
}
static void copy_file(const char* source, const char* destination) {
- FILE* dest_fp = fopen(destination, "w");
- if (dest_fp == nullptr) {
- PLOG(ERROR) << "Can't open " << destination;
- } else {
- FILE* source_fp = fopen(source, "r");
- if (source_fp != nullptr) {
- char buf[4096];
- size_t bytes;
- while ((bytes = fread(buf, 1, sizeof(buf), source_fp)) != 0) {
- fwrite(buf, 1, bytes, dest_fp);
- }
- check_and_fclose(source_fp, source);
- }
- check_and_fclose(dest_fp, destination);
+ FILE* dest_fp = fopen(destination, "we");
+ if (dest_fp == nullptr) {
+ PLOG(ERROR) << "Can't open " << destination;
+ } else {
+ FILE* source_fp = fopen(source, "re");
+ if (source_fp != nullptr) {
+ char buf[4096];
+ size_t bytes;
+ while ((bytes = fread(buf, 1, sizeof(buf), source_fp)) != 0) {
+ fwrite(buf, 1, bytes, dest_fp);
+ }
+ check_and_fclose(source_fp, source);
}
+ check_and_fclose(dest_fp, destination);
+ }
}
static bool rotated = false;
@@ -120,7 +120,7 @@
*/
bool has_cache = false;
static const char mounts_file[] = "/proc/mounts";
- FILE *fp = fopen(mounts_file, "r");
+ FILE* fp = fopen(mounts_file, "re");
if (!fp) {
PLOG(ERROR) << "failed to open " << mounts_file;
} else {
diff --git a/recovery.cpp b/recovery.cpp
index 852f1e8..07bd7b9 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -250,7 +250,7 @@
auto start = std::chrono::steady_clock::now();
// Child logger to actually write to the log file.
- FILE* log_fp = fopen(filename, "a");
+ FILE* log_fp = fopen(filename, "ae");
if (log_fp == nullptr) {
PLOG(ERROR) << "fopen \"" << filename << "\" failed";
close(pipefd[0]);
@@ -419,27 +419,27 @@
static off_t tmplog_offset = 0;
static void copy_log_file(const char* source, const char* destination, bool append) {
- FILE* dest_fp = fopen_path(destination, append ? "a" : "w");
- if (dest_fp == nullptr) {
- PLOG(ERROR) << "Can't open " << destination;
- } else {
- FILE* source_fp = fopen(source, "r");
- if (source_fp != nullptr) {
- if (append) {
- fseeko(source_fp, tmplog_offset, SEEK_SET); // Since last write
- }
- char buf[4096];
- size_t bytes;
- while ((bytes = fread(buf, 1, sizeof(buf), source_fp)) != 0) {
- fwrite(buf, 1, bytes, dest_fp);
- }
- if (append) {
- tmplog_offset = ftello(source_fp);
- }
- check_and_fclose(source_fp, source);
- }
- check_and_fclose(dest_fp, destination);
+ FILE* dest_fp = fopen_path(destination, append ? "ae" : "we");
+ if (dest_fp == nullptr) {
+ PLOG(ERROR) << "Can't open " << destination;
+ } else {
+ FILE* source_fp = fopen(source, "re");
+ if (source_fp != nullptr) {
+ if (append) {
+ fseeko(source_fp, tmplog_offset, SEEK_SET); // Since last write
+ }
+ char buf[4096];
+ size_t bytes;
+ while ((bytes = fread(buf, 1, sizeof(buf), source_fp)) != 0) {
+ fwrite(buf, 1, bytes, dest_fp);
+ }
+ if (append) {
+ tmplog_offset = ftello(source_fp);
+ }
+ check_and_fclose(source_fp, source);
}
+ check_and_fclose(dest_fp, destination);
+ }
}
static void copy_logs() {
@@ -478,40 +478,38 @@
sync();
}
-// clear the recovery command and prepare to boot a (hopefully working) system,
+// Clear the recovery command and prepare to boot a (hopefully working) system,
// copy our log file to cache as well (for the system to read). This function is
// idempotent: call it as many times as you like.
static void finish_recovery() {
- // Save the locale to cache, so if recovery is next started up
- // without a --locale argument (eg, directly from the bootloader)
- // it will use the last-known locale.
- if (!locale.empty() && has_cache) {
- LOG(INFO) << "Saving locale \"" << locale << "\"";
-
- FILE* fp = fopen_path(LOCALE_FILE, "w");
- if (!android::base::WriteStringToFd(locale, fileno(fp))) {
- PLOG(ERROR) << "Failed to save locale to " << LOCALE_FILE;
- }
- check_and_fclose(fp, LOCALE_FILE);
+ // Save the locale to cache, so if recovery is next started up without a '--locale' argument
+ // (e.g., directly from the bootloader) it will use the last-known locale.
+ if (!locale.empty() && has_cache) {
+ LOG(INFO) << "Saving locale \"" << locale << "\"";
+ if (ensure_path_mounted(LOCALE_FILE) != 0) {
+ LOG(ERROR) << "Failed to mount " << LOCALE_FILE;
+ } else if (!android::base::WriteStringToFile(locale, LOCALE_FILE)) {
+ PLOG(ERROR) << "Failed to save locale to " << LOCALE_FILE;
}
+ }
- copy_logs();
+ copy_logs();
- // Reset to normal system boot so recovery won't cycle indefinitely.
- std::string err;
- if (!clear_bootloader_message(&err)) {
- LOG(ERROR) << "Failed to clear BCB message: " << err;
+ // Reset to normal system boot so recovery won't cycle indefinitely.
+ std::string err;
+ if (!clear_bootloader_message(&err)) {
+ LOG(ERROR) << "Failed to clear BCB message: " << err;
+ }
+
+ // Remove the command file, so recovery won't repeat indefinitely.
+ if (has_cache) {
+ if (ensure_path_mounted(COMMAND_FILE) != 0 || (unlink(COMMAND_FILE) && errno != ENOENT)) {
+ LOG(WARNING) << "Can't unlink " << COMMAND_FILE;
}
+ ensure_path_unmounted(CACHE_ROOT);
+ }
- // Remove the command file, so recovery won't repeat indefinitely.
- if (has_cache) {
- if (ensure_path_mounted(COMMAND_FILE) != 0 || (unlink(COMMAND_FILE) && errno != ENOENT)) {
- LOG(WARNING) << "Can't unlink " << COMMAND_FILE;
- }
- ensure_path_unmounted(CACHE_ROOT);
- }
-
- sync(); // For good measure.
+ sync(); // For good measure.
}
struct saved_log_file {
@@ -552,7 +550,7 @@
}
std::string data(sb.st_size, '\0');
- FILE* f = fopen(path.c_str(), "rb");
+ FILE* f = fopen(path.c_str(), "rbe");
fread(&data[0], 1, data.size(), f);
fclose(f);
@@ -580,7 +578,7 @@
ui->Print("Failed to make convert_fbe dir %s\n", strerror(errno));
return true;
}
- FILE* f = fopen(CONVERT_FBE_FILE, "wb");
+ FILE* f = fopen(CONVERT_FBE_FILE, "wbe");
if (!f) {
ui->Print("Failed to convert to file encryption %s\n", strerror(errno));
return true;
@@ -760,12 +758,13 @@
}
static bool prompt_and_wipe_data(Device* device) {
+ // Use a single string and let ScreenRecoveryUI handles the wrapping.
const char* const headers[] = {
- "Can't load Android system. Your data may be corrupt.",
- "If you continue to get this message, you may need to",
- "perform a factory data reset and erase all user data",
+ "Can't load Android system. Your data may be corrupt. "
+ "If you continue to get this message, you may need to "
+ "perform a factory data reset and erase all user data "
"stored on this device.",
- NULL
+ nullptr
};
const char* const items[] = {
"Try again",
@@ -1593,15 +1592,14 @@
ui->Print("Rebooting automatically.\n");
}
} else if (!just_exit) {
- status = INSTALL_NONE; // No command specified
- ui->SetBackground(RecoveryUI::NO_COMMAND);
-
- // http://b/17489952
- // If this is an eng or userdebug build, automatically turn on the
- // text display if no command is specified.
- if (is_ro_debuggable()) {
- ui->ShowText(true);
- }
+ // If this is an eng or userdebug build, automatically turn on the text display if no command
+ // is specified. Note that this should be called before setting the background to avoid
+ // flickering the background image.
+ if (is_ro_debuggable()) {
+ ui->ShowText(true);
+ }
+ status = INSTALL_NONE; // No command specified
+ ui->SetBackground(RecoveryUI::NO_COMMAND);
}
if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) {
diff --git a/roots.cpp b/roots.cpp
index 49f002f..29f55b9 100644
--- a/roots.cpp
+++ b/roots.cpp
@@ -287,8 +287,6 @@
if (result == 0 && directory != nullptr) {
const char* e2fsdroid_argv[] = { "/sbin/e2fsdroid_static",
"-e",
- "-S",
- "/file_contexts",
"-f",
directory,
"-a",
@@ -297,7 +295,7 @@
nullptr };
result = exec_cmd(e2fsdroid_argv[0], const_cast<char**>(e2fsdroid_argv));
- }
+ }
} else { /* Has to be f2fs because we checked earlier. */
char *num_sectors = nullptr;
if (length >= 512 && asprintf(&num_sectors, "%zd", length / 512) <= 0) {
diff --git a/screen_ui.cpp b/screen_ui.cpp
index a7d9c9f..b8f6ea2 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -45,14 +45,15 @@
// Return the current time as a double (including fractions of a second).
static double now() {
- struct timeval tv;
- gettimeofday(&tv, nullptr);
- return tv.tv_sec + tv.tv_usec / 1000000.0;
+ struct timeval tv;
+ gettimeofday(&tv, nullptr);
+ return tv.tv_sec + tv.tv_usec / 1000000.0;
}
ScreenRecoveryUI::ScreenRecoveryUI()
: kMarginWidth(RECOVERY_UI_MARGIN_WIDTH),
kMarginHeight(RECOVERY_UI_MARGIN_HEIGHT),
+ kAnimationFps(RECOVERY_UI_ANIMATION_FPS),
density_(static_cast<float>(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f),
currentIcon(NONE),
progressBarType(EMPTY),
@@ -68,7 +69,7 @@
text_top_(0),
show_text(false),
show_text_ever(false),
- menu_(nullptr),
+ menu_headers_(nullptr),
show_menu(false),
menu_items(0),
menu_sel(0),
@@ -77,30 +78,34 @@
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) {
- return intro_done ? loopFrames[current_frame] : introFrames[current_frame];
- }
- return error_icon;
+GRSurface* ScreenRecoveryUI::GetCurrentFrame() const {
+ if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) {
+ return intro_done ? loopFrames[current_frame] : introFrames[current_frame];
+ }
+ return error_icon;
}
-GRSurface* ScreenRecoveryUI::GetCurrentText() {
- switch (currentIcon) {
- case ERASING: return erasing_text;
- case ERROR: return error_text;
- case INSTALLING_UPDATE: return installing_text;
- case NO_COMMAND: return no_command_text;
- case NONE: abort();
- }
+GRSurface* ScreenRecoveryUI::GetCurrentText() const {
+ switch (currentIcon) {
+ case ERASING:
+ return erasing_text;
+ case ERROR:
+ return error_text;
+ case INSTALLING_UPDATE:
+ return installing_text;
+ case NO_COMMAND:
+ return no_command_text;
+ case NONE:
+ abort();
+ }
}
int ScreenRecoveryUI::PixelsFromDp(int dp) const {
- return dp * density_;
+ return dp * density_;
}
// Here's the intended layout:
@@ -127,17 +132,16 @@
{ 52, 112, }, // LANDSCAPE_LARGE
};
-int ScreenRecoveryUI::GetAnimationBaseline() {
- return GetTextBaseline() - PixelsFromDp(kLayouts[layout_][ICON]) -
- gr_get_height(loopFrames[0]);
+int ScreenRecoveryUI::GetAnimationBaseline() const {
+ return GetTextBaseline() - PixelsFromDp(kLayouts[layout_][ICON]) - gr_get_height(loopFrames[0]);
}
-int ScreenRecoveryUI::GetTextBaseline() {
- return GetProgressBaseline() - PixelsFromDp(kLayouts[layout_][TEXT]) -
- gr_get_height(installing_text);
+int ScreenRecoveryUI::GetTextBaseline() const {
+ return GetProgressBaseline() - PixelsFromDp(kLayouts[layout_][TEXT]) -
+ gr_get_height(installing_text);
}
-int ScreenRecoveryUI::GetProgressBaseline() {
+int ScreenRecoveryUI::GetProgressBaseline() const {
int elements_sum = gr_get_height(loopFrames[0]) + PixelsFromDp(kLayouts[layout_][ICON]) +
gr_get_height(installing_text) + PixelsFromDp(kLayouts[layout_][TEXT]) +
gr_get_height(progressBarFill);
@@ -148,34 +152,33 @@
// Clear the screen and draw the currently selected background icon (if any).
// Should only be called with updateMutex locked.
void ScreenRecoveryUI::draw_background_locked() {
- pagesIdentical = false;
- gr_color(0, 0, 0, 255);
- gr_clear();
+ pagesIdentical = false;
+ gr_color(0, 0, 0, 255);
+ gr_clear();
- if (currentIcon != NONE) {
- if (max_stage != -1) {
- int stage_height = gr_get_height(stageMarkerEmpty);
- int stage_width = gr_get_width(stageMarkerEmpty);
- int x = (gr_fb_width() - max_stage * gr_get_width(stageMarkerEmpty)) / 2;
- int y = gr_fb_height() - stage_height;
- for (int i = 0; i < max_stage; ++i) {
- GRSurface* stage_surface = (i < stage) ? stageMarkerFill : stageMarkerEmpty;
- gr_blit(stage_surface, 0, 0, stage_width, stage_height, x, y);
- x += stage_width;
- }
- }
-
- GRSurface* text_surface = GetCurrentText();
- int text_x = (gr_fb_width() - gr_get_width(text_surface)) / 2;
- int text_y = GetTextBaseline();
- gr_color(255, 255, 255, 255);
- gr_texticon(text_x, text_y, text_surface);
+ if (currentIcon != NONE) {
+ if (max_stage != -1) {
+ int stage_height = gr_get_height(stageMarkerEmpty);
+ int stage_width = gr_get_width(stageMarkerEmpty);
+ int x = (gr_fb_width() - max_stage * gr_get_width(stageMarkerEmpty)) / 2;
+ int y = gr_fb_height() - stage_height;
+ for (int i = 0; i < max_stage; ++i) {
+ GRSurface* stage_surface = (i < stage) ? stageMarkerFill : stageMarkerEmpty;
+ gr_blit(stage_surface, 0, 0, stage_width, stage_height, x, y);
+ x += stage_width;
+ }
}
+
+ GRSurface* text_surface = GetCurrentText();
+ int text_x = (gr_fb_width() - gr_get_width(text_surface)) / 2;
+ int text_y = GetTextBaseline();
+ gr_color(255, 255, 255, 255);
+ gr_texticon(text_x, text_y, text_surface);
+ }
}
-// Draws the animation and progress bar (if any) on the screen.
-// Does not flip pages.
-// Should only be called with updateMutex locked.
+// Draws the animation and progress bar (if any) on the screen. Does not flip pages. Should only be
+// called with updateMutex locked.
void ScreenRecoveryUI::draw_foreground_locked() {
if (currentIcon != NONE) {
GRSurface* frame = GetCurrentFrame();
@@ -223,67 +226,95 @@
}
}
-void ScreenRecoveryUI::SetColor(UIElement e) {
- switch (e) {
- case INFO:
- gr_color(249, 194, 0, 255);
- break;
- 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_BG_ACTIVE:
- gr_color(0, 156, 100, 255);
- break;
- case MENU_SEL_FG:
- gr_color(255, 255, 255, 255);
- break;
- case LOG:
- gr_color(196, 196, 196, 255);
- break;
- case TEXT_FILL:
- gr_color(0, 0, 0, 160);
- break;
- default:
- gr_color(255, 255, 255, 255);
- break;
- }
+void ScreenRecoveryUI::SetColor(UIElement e) const {
+ switch (e) {
+ case INFO:
+ gr_color(249, 194, 0, 255);
+ break;
+ 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_BG_ACTIVE:
+ gr_color(0, 156, 100, 255);
+ break;
+ case MENU_SEL_FG:
+ gr_color(255, 255, 255, 255);
+ break;
+ case LOG:
+ gr_color(196, 196, 196, 255);
+ break;
+ case TEXT_FILL:
+ gr_color(0, 0, 0, 160);
+ break;
+ default:
+ gr_color(255, 255, 255, 255);
+ break;
+ }
}
-void ScreenRecoveryUI::DrawHorizontalRule(int* y) {
- SetColor(MENU);
- *y += 4;
- gr_fill(0, *y, gr_fb_width(), *y + 2);
- *y += 4;
+int ScreenRecoveryUI::DrawHorizontalRule(int y) const {
+ gr_fill(0, y + 4, gr_fb_width(), y + 6);
+ return 8;
}
void ScreenRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) const {
- gr_fill(x, y, x + width, y + height);
+ gr_fill(x, y, x + width, y + height);
}
-void ScreenRecoveryUI::DrawTextLine(int x, int* y, const char* line, bool bold) const {
- gr_text(gr_sys_font(), x, *y, line, bold);
- *y += char_height_ + 4;
+int ScreenRecoveryUI::DrawTextLine(int x, int y, const char* line, bool bold) const {
+ gr_text(gr_sys_font(), x, y, line, bold);
+ return char_height_ + 4;
}
-void ScreenRecoveryUI::DrawTextLines(int x, int* y, const char* const* lines) const {
- for (size_t i = 0; lines != nullptr && lines[i] != nullptr; ++i) {
- DrawTextLine(x, y, lines[i], false);
+int ScreenRecoveryUI::DrawTextLines(int x, int y, const char* const* lines) const {
+ int offset = 0;
+ for (size_t i = 0; lines != nullptr && lines[i] != nullptr; ++i) {
+ offset += DrawTextLine(x, y + offset, lines[i], false);
+ }
+ return offset;
+}
+
+int ScreenRecoveryUI::DrawWrappedTextLines(int x, int y, const char* const* lines) const {
+ int offset = 0;
+ for (size_t i = 0; lines != nullptr && lines[i] != nullptr; ++i) {
+ // The line will be wrapped if it exceeds text_cols_.
+ std::string line(lines[i]);
+ size_t next_start = 0;
+ while (next_start < line.size()) {
+ std::string sub = line.substr(next_start, text_cols_ + 1);
+ if (sub.size() <= text_cols_) {
+ next_start += sub.size();
+ } else {
+ // Line too long and must be wrapped to text_cols_ columns.
+ size_t last_space = sub.find_last_of(" \t\n");
+ if (last_space == std::string::npos) {
+ // No space found, just draw as much as we can
+ sub.resize(text_cols_);
+ next_start += text_cols_;
+ } else {
+ sub.resize(last_space);
+ next_start += last_space + 1;
+ }
+ }
+ offset += DrawTextLine(x, y + offset, sub.c_str(), false);
}
+ }
+ return offset;
}
static const char* REGULAR_HELP[] = {
- "Use volume up/down and power.",
- NULL
+ "Use volume up/down and power.",
+ NULL
};
static const char* LONG_PRESS_HELP[] = {
- "Any button cycles highlight.",
- "Long-press activates.",
- NULL
+ "Any button cycles highlight.",
+ "Long-press activates.",
+ NULL
};
// Redraws everything on the screen. Does not flip pages. Should only be called with updateMutex
@@ -294,29 +325,30 @@
draw_foreground_locked();
return;
}
+
gr_color(0, 0, 0, 255);
gr_clear();
- static constexpr int TEXT_INDENT = 4;
- int x = TEXT_INDENT + kMarginWidth;
int y = kMarginHeight;
if (show_menu) {
- std::string recovery_fingerprint =
- android::base::GetProperty("ro.bootimage.build.fingerprint", "");
+ static constexpr int kMenuIndent = 4;
+ int x = kMarginWidth + kMenuIndent;
SetColor(INFO);
- DrawTextLine(x, &y, "Android Recovery", true);
+ y += DrawTextLine(x, y, "Android Recovery", true);
+ std::string recovery_fingerprint =
+ android::base::GetProperty("ro.bootimage.build.fingerprint", "");
for (const auto& chunk : android::base::Split(recovery_fingerprint, ":")) {
- DrawTextLine(x, &y, chunk.c_str(), false);
+ y += DrawTextLine(x, y, chunk.c_str(), false);
}
- DrawTextLines(x, &y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP);
+ y += DrawTextLines(x, y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP);
SetColor(HEADER);
- DrawTextLines(x, &y, menu_headers_);
+ // Ignore kMenuIndent, which is not taken into account by text_cols_.
+ y += DrawWrappedTextLines(kMarginWidth, y, menu_headers_);
SetColor(MENU);
- DrawHorizontalRule(&y);
- y += 4;
+ y += DrawHorizontalRule(y) + 4;
for (int i = 0; i < menu_items; ++i) {
if (i == menu_sel) {
// Draw the highlight bar.
@@ -324,13 +356,13 @@
DrawHighlightBar(0, y - 2, gr_fb_width(), char_height_ + 4);
// Bold white text for the selected item.
SetColor(MENU_SEL_FG);
- DrawTextLine(x, &y, menu_[i], true);
+ y += DrawTextLine(x, y, menu_[i].c_str(), true);
SetColor(MENU);
} else {
- DrawTextLine(x, &y, menu_[i], false);
+ y += DrawTextLine(x, y, menu_[i].c_str(), false);
}
}
- DrawHorizontalRule(&y);
+ y += DrawHorizontalRule(y);
}
// Display from the bottom up, until we hit the top of the screen, the bottom of the menu, or
@@ -338,10 +370,9 @@
SetColor(LOG);
int row = (text_top_ + text_rows_ - 1) % text_rows_;
size_t count = 0;
- for (int ty = gr_fb_height() - kMarginHeight - char_height_;
- ty >= y && count < text_rows_; ty -= char_height_, ++count) {
- int temp_y = ty;
- DrawTextLine(x, &temp_y, text_[row], false);
+ for (int ty = gr_fb_height() - kMarginHeight - char_height_; ty >= y && count < text_rows_;
+ ty -= char_height_, ++count) {
+ DrawTextLine(kMarginWidth, ty, text_[row], false);
--row;
if (row < 0) row = text_rows_ - 1;
}
@@ -350,81 +381,81 @@
// Redraw everything on the screen and flip the screen (make it visible).
// Should only be called with updateMutex locked.
void ScreenRecoveryUI::update_screen_locked() {
- draw_screen_locked();
- gr_flip();
+ draw_screen_locked();
+ gr_flip();
}
// Updates only the progress bar, if possible, otherwise redraws the screen.
// Should only be called with updateMutex locked.
void ScreenRecoveryUI::update_progress_locked() {
- if (show_text || !pagesIdentical) {
- draw_screen_locked(); // Must redraw the whole screen
- pagesIdentical = true;
- } else {
- draw_foreground_locked(); // Draw only the progress bar and overlays
- }
- gr_flip();
+ if (show_text || !pagesIdentical) {
+ draw_screen_locked(); // Must redraw the whole screen
+ pagesIdentical = true;
+ } else {
+ draw_foreground_locked(); // Draw only the progress bar and overlays
+ }
+ gr_flip();
}
// Keeps the progress bar updated, even when the process is otherwise busy.
void* ScreenRecoveryUI::ProgressThreadStartRoutine(void* data) {
- reinterpret_cast<ScreenRecoveryUI*>(data)->ProgressThreadLoop();
- return nullptr;
+ reinterpret_cast<ScreenRecoveryUI*>(data)->ProgressThreadLoop();
+ return nullptr;
}
void ScreenRecoveryUI::ProgressThreadLoop() {
- double interval = 1.0 / animation_fps;
- while (true) {
- double start = now();
- pthread_mutex_lock(&updateMutex);
+ double interval = 1.0 / kAnimationFps;
+ while (true) {
+ double start = now();
+ pthread_mutex_lock(&updateMutex);
- bool redraw = false;
+ bool redraw = false;
- // update the installation animation, if active
- // skip this if we have a text overlay (too expensive to update)
- 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 = true;
+ // update the installation animation, if active
+ // skip this if we have a text overlay (too expensive to update)
+ 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;
+ }
- // 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 = true;
- }
- }
-
- if (redraw) update_progress_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));
+ redraw = true;
}
+
+ // 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 = true;
+ }
+ }
+
+ if (redraw) update_progress_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));
+ }
}
void ScreenRecoveryUI::LoadBitmap(const char* filename, GRSurface** surface) {
- int result = res_create_display_surface(filename, surface);
- if (result < 0) {
- LOG(ERROR) << "couldn't load bitmap " << filename << " (error " << result << ")";
- }
+ int result = res_create_display_surface(filename, surface);
+ if (result < 0) {
+ LOG(ERROR) << "couldn't load bitmap " << filename << " (error " << result << ")";
+ }
}
void ScreenRecoveryUI::LoadLocalizedBitmap(const char* filename, GRSurface** surface) {
@@ -435,22 +466,22 @@
}
static char** Alloc2d(size_t rows, size_t cols) {
- char** result = new char*[rows];
- for (size_t i = 0; i < rows; ++i) {
- result[i] = new char[cols];
- memset(result[i], 0, cols);
- }
- return result;
+ char** result = new char*[rows];
+ for (size_t i = 0; i < rows; ++i) {
+ result[i] = new char[cols];
+ memset(result[i], 0, cols);
+ }
+ return result;
}
// Choose the right background string to display during update.
void ScreenRecoveryUI::SetSystemUpdateText(bool security_update) {
- if (security_update) {
- LoadLocalizedBitmap("installing_security_text", &installing_text);
- } else {
- LoadLocalizedBitmap("installing_text", &installing_text);
- }
- Redraw();
+ if (security_update) {
+ LoadLocalizedBitmap("installing_security_text", &installing_text);
+ } else {
+ LoadLocalizedBitmap("installing_text", &installing_text);
+ }
+ Redraw();
}
bool ScreenRecoveryUI::InitTextParams() {
@@ -477,7 +508,6 @@
text_ = Alloc2d(text_rows_, text_cols_ + 1);
file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1);
- menu_ = Alloc2d(text_rows_, text_cols_ + 1);
text_col_ = text_row_ = 0;
text_top_ = 1;
@@ -506,309 +536,308 @@
}
void ScreenRecoveryUI::LoadAnimation() {
- 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;
+ 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, 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);
- }
+ while ((de = readdir(dir.get())) != nullptr) {
+ 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();
+ 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();
+ // 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());
+ 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 (size_t i = 0; i < intro_frames; i++) {
- LoadBitmap(intro_frame_names.at(i).c_str(), &introFrames[i]);
- }
+ introFrames = new GRSurface*[intro_frames];
+ 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 (size_t i = 0; i < loop_frames; i++) {
- LoadBitmap(loop_frame_names.at(i).c_str(), &loopFrames[i]);
- }
+ loopFrames = new GRSurface*[loop_frames];
+ for (size_t i = 0; i < loop_frames; i++) {
+ LoadBitmap(loop_frame_names.at(i).c_str(), &loopFrames[i]);
+ }
}
void ScreenRecoveryUI::SetBackground(Icon icon) {
- pthread_mutex_lock(&updateMutex);
+ pthread_mutex_lock(&updateMutex);
- currentIcon = icon;
- update_screen_locked();
+ currentIcon = icon;
+ update_screen_locked();
- pthread_mutex_unlock(&updateMutex);
+ pthread_mutex_unlock(&updateMutex);
}
void ScreenRecoveryUI::SetProgressType(ProgressType type) {
- pthread_mutex_lock(&updateMutex);
- if (progressBarType != type) {
- progressBarType = type;
- }
- progressScopeStart = 0;
- progressScopeSize = 0;
- progress = 0;
- update_progress_locked();
- pthread_mutex_unlock(&updateMutex);
+ pthread_mutex_lock(&updateMutex);
+ if (progressBarType != type) {
+ progressBarType = type;
+ }
+ progressScopeStart = 0;
+ progressScopeSize = 0;
+ progress = 0;
+ update_progress_locked();
+ pthread_mutex_unlock(&updateMutex);
}
void ScreenRecoveryUI::ShowProgress(float portion, float seconds) {
- pthread_mutex_lock(&updateMutex);
- progressBarType = DETERMINATE;
- progressScopeStart += progressScopeSize;
- progressScopeSize = portion;
- progressScopeTime = now();
- progressScopeDuration = seconds;
- progress = 0;
- update_progress_locked();
- pthread_mutex_unlock(&updateMutex);
+ pthread_mutex_lock(&updateMutex);
+ progressBarType = DETERMINATE;
+ progressScopeStart += progressScopeSize;
+ progressScopeSize = portion;
+ progressScopeTime = now();
+ progressScopeDuration = seconds;
+ progress = 0;
+ update_progress_locked();
+ pthread_mutex_unlock(&updateMutex);
}
void ScreenRecoveryUI::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 = gr_get_width(progressBarEmpty);
- float scale = width * progressScopeSize;
- if ((int) (progress * scale) != (int) (fraction * scale)) {
- progress = fraction;
- update_progress_locked();
- }
+ 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 = gr_get_width(progressBarEmpty);
+ float scale = width * progressScopeSize;
+ if ((int)(progress * scale) != (int)(fraction * scale)) {
+ progress = fraction;
+ update_progress_locked();
}
- pthread_mutex_unlock(&updateMutex);
+ }
+ pthread_mutex_unlock(&updateMutex);
}
void ScreenRecoveryUI::SetStage(int current, int max) {
- pthread_mutex_lock(&updateMutex);
- stage = current;
- max_stage = max;
- pthread_mutex_unlock(&updateMutex);
+ pthread_mutex_lock(&updateMutex);
+ stage = current;
+ max_stage = max;
+ pthread_mutex_unlock(&updateMutex);
}
void ScreenRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) {
- std::string str;
- android::base::StringAppendV(&str, fmt, ap);
+ std::string str;
+ android::base::StringAppendV(&str, fmt, ap);
- if (copy_to_stdout) {
- fputs(str.c_str(), stdout);
- }
+ if (copy_to_stdout) {
+ fputs(str.c_str(), stdout);
+ }
- pthread_mutex_lock(&updateMutex);
- 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_[text_row_][text_col_++] = *ptr;
- }
+ pthread_mutex_lock(&updateMutex);
+ 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';
- update_screen_locked();
+ 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 ScreenRecoveryUI::Print(const char* fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- PrintV(fmt, true, ap);
- va_end(ap);
+ va_list ap;
+ va_start(ap, fmt);
+ PrintV(fmt, true, ap);
+ va_end(ap);
}
void ScreenRecoveryUI::PrintOnScreenOnly(const char *fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- PrintV(fmt, false, ap);
- va_end(ap);
+ va_list ap;
+ va_start(ap, fmt);
+ PrintV(fmt, false, ap);
+ va_end(ap);
}
void ScreenRecoveryUI::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_;
+ 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 (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_;
- }
- pthread_mutex_unlock(&updateMutex);
+ if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_;
+ }
+ pthread_mutex_unlock(&updateMutex);
}
void ScreenRecoveryUI::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);
- }
- pthread_mutex_unlock(&updateMutex);
+ 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);
+ }
+ pthread_mutex_unlock(&updateMutex);
}
void ScreenRecoveryUI::ShowFile(FILE* fp) {
- std::vector<off_t> offsets;
- offsets.push_back(ftello(fp));
- ClearText();
+ std::vector<off_t> offsets;
+ offsets.push_back(ftello(fp));
+ ClearText();
- struct stat sb;
- fstat(fileno(fp), &sb);
+ struct stat sb;
+ fstat(fileno(fp), &sb);
- bool show_prompt = false;
- while (true) {
- if (show_prompt) {
- PrintOnScreenOnly("--(%d%% of %d bytes)--",
- static_cast<int>(100 * (double(ftello(fp)) / double(sb.st_size))),
- static_cast<int>(sb.st_size));
- Redraw();
- while (show_prompt) {
- show_prompt = false;
- int key = WaitKey();
- if (key == KEY_POWER || key == KEY_ENTER) {
- return;
- } else if (key == KEY_UP || key == KEY_VOLUMEUP) {
- if (offsets.size() <= 1) {
- show_prompt = true;
- } else {
- offsets.pop_back();
- fseek(fp, offsets.back(), SEEK_SET);
- }
- } else {
- if (feof(fp)) {
- return;
- }
- offsets.push_back(ftello(fp));
- }
- }
- ClearText();
- }
-
- int ch = getc(fp);
- if (ch == EOF) {
- while (text_row_ < text_rows_ - 1) PutChar('\n');
+ bool show_prompt = false;
+ while (true) {
+ if (show_prompt) {
+ PrintOnScreenOnly("--(%d%% of %d bytes)--",
+ static_cast<int>(100 * (double(ftello(fp)) / double(sb.st_size))),
+ static_cast<int>(sb.st_size));
+ Redraw();
+ while (show_prompt) {
+ show_prompt = false;
+ int key = WaitKey();
+ if (key == KEY_POWER || key == KEY_ENTER) {
+ return;
+ } else if (key == KEY_UP || key == KEY_VOLUMEUP) {
+ if (offsets.size() <= 1) {
show_prompt = true;
+ } else {
+ offsets.pop_back();
+ fseek(fp, offsets.back(), SEEK_SET);
+ }
} else {
- PutChar(ch);
- if (text_col_ == 0 && text_row_ >= text_rows_ - 1) {
- show_prompt = true;
- }
+ if (feof(fp)) {
+ return;
+ }
+ offsets.push_back(ftello(fp));
}
+ }
+ ClearText();
}
+
+ int ch = getc(fp);
+ if (ch == EOF) {
+ while (text_row_ < text_rows_ - 1) PutChar('\n');
+ show_prompt = true;
+ } else {
+ PutChar(ch);
+ if (text_col_ == 0 && text_row_ >= text_rows_ - 1) {
+ show_prompt = true;
+ }
+ }
+ }
}
void ScreenRecoveryUI::ShowFile(const char* filename) {
- FILE* fp = fopen_path(filename, "re");
- if (fp == nullptr) {
- Print(" Unable to open %s: %s\n", filename, strerror(errno));
- return;
- }
+ FILE* fp = fopen_path(filename, "re");
+ if (fp == nullptr) {
+ Print(" Unable to open %s: %s\n", filename, strerror(errno));
+ return;
+ }
- char** old_text = text_;
- size_t old_text_col = text_col_;
- size_t old_text_row = text_row_;
- size_t old_text_top = text_top_;
+ char** old_text = text_;
+ size_t old_text_col = text_col_;
+ size_t old_text_row = text_row_;
+ size_t old_text_top = text_top_;
- // Swap in the alternate screen and clear it.
- text_ = file_viewer_text_;
- ClearText();
+ // Swap in the alternate screen and clear it.
+ text_ = file_viewer_text_;
+ ClearText();
- ShowFile(fp);
- fclose(fp);
+ ShowFile(fp);
+ fclose(fp);
- text_ = old_text;
- text_col_ = old_text_col;
- text_row_ = old_text_row;
- text_top_ = old_text_top;
+ text_ = old_text;
+ text_col_ = old_text_col;
+ text_row_ = old_text_row;
+ text_top_ = old_text_top;
}
-void ScreenRecoveryUI::StartMenu(const char* const * headers, const char* const * items,
+void ScreenRecoveryUI::StartMenu(const char* const* headers, const char* const* items,
int initial_selection) {
- pthread_mutex_lock(&updateMutex);
- if (text_rows_ > 0 && text_cols_ > 0) {
- menu_headers_ = headers;
- size_t i = 0;
- for (; i < text_rows_ && items[i] != nullptr; ++i) {
- strncpy(menu_[i], items[i], text_cols_ - 1);
- menu_[i][text_cols_ - 1] = '\0';
- }
- menu_items = i;
- show_menu = true;
- menu_sel = initial_selection;
- update_screen_locked();
+ pthread_mutex_lock(&updateMutex);
+ if (text_rows_ > 0 && text_cols_ > 0) {
+ menu_headers_ = headers;
+ menu_.clear();
+ for (size_t i = 0; i < text_rows_ && items[i] != nullptr; ++i) {
+ menu_.emplace_back(std::string(items[i], strnlen(items[i], text_cols_ - 1)));
}
- pthread_mutex_unlock(&updateMutex);
+ menu_items = static_cast<int>(menu_.size());
+ show_menu = true;
+ menu_sel = initial_selection;
+ update_screen_locked();
+ }
+ pthread_mutex_unlock(&updateMutex);
}
int ScreenRecoveryUI::SelectMenu(int sel) {
- pthread_mutex_lock(&updateMutex);
- if (show_menu) {
- int old_sel = menu_sel;
- menu_sel = sel;
+ pthread_mutex_lock(&updateMutex);
+ if (show_menu) {
+ int old_sel = menu_sel;
+ menu_sel = sel;
- // Wrap at top and bottom.
- if (menu_sel < 0) menu_sel = menu_items - 1;
- if (menu_sel >= menu_items) menu_sel = 0;
+ // Wrap at top and bottom.
+ if (menu_sel < 0) menu_sel = menu_items - 1;
+ if (menu_sel >= menu_items) menu_sel = 0;
- sel = menu_sel;
- if (menu_sel != old_sel) update_screen_locked();
- }
- pthread_mutex_unlock(&updateMutex);
- return sel;
+ sel = menu_sel;
+ if (menu_sel != old_sel) update_screen_locked();
+ }
+ pthread_mutex_unlock(&updateMutex);
+ return sel;
}
void ScreenRecoveryUI::EndMenu() {
- pthread_mutex_lock(&updateMutex);
- if (show_menu && text_rows_ > 0 && text_cols_ > 0) {
- show_menu = false;
- update_screen_locked();
- }
- pthread_mutex_unlock(&updateMutex);
+ pthread_mutex_lock(&updateMutex);
+ if (show_menu && text_rows_ > 0 && text_cols_ > 0) {
+ show_menu = false;
+ update_screen_locked();
+ }
+ pthread_mutex_unlock(&updateMutex);
}
bool ScreenRecoveryUI::IsTextVisible() {
- pthread_mutex_lock(&updateMutex);
- int visible = show_text;
- pthread_mutex_unlock(&updateMutex);
- return visible;
+ pthread_mutex_lock(&updateMutex);
+ int visible = show_text;
+ pthread_mutex_unlock(&updateMutex);
+ return visible;
}
bool ScreenRecoveryUI::WasTextEverVisible() {
- pthread_mutex_lock(&updateMutex);
- int ever_visible = show_text_ever;
- pthread_mutex_unlock(&updateMutex);
- return ever_visible;
+ pthread_mutex_lock(&updateMutex);
+ int ever_visible = show_text_ever;
+ pthread_mutex_unlock(&updateMutex);
+ return ever_visible;
}
void ScreenRecoveryUI::ShowText(bool visible) {
- pthread_mutex_lock(&updateMutex);
- show_text = visible;
- if (show_text) show_text_ever = true;
- update_screen_locked();
- pthread_mutex_unlock(&updateMutex);
+ pthread_mutex_lock(&updateMutex);
+ show_text = visible;
+ if (show_text) show_text_ever = true;
+ update_screen_locked();
+ pthread_mutex_unlock(&updateMutex);
}
void ScreenRecoveryUI::Redraw() {
- pthread_mutex_lock(&updateMutex);
- update_screen_locked();
- pthread_mutex_unlock(&updateMutex);
+ pthread_mutex_lock(&updateMutex);
+ update_screen_locked();
+ pthread_mutex_unlock(&updateMutex);
}
void ScreenRecoveryUI::KeyLongPress(int) {
- // Redraw so that if we're in the menu, the highlight
- // will change color to indicate a successful long press.
- Redraw();
+ // Redraw so that if we're in the menu, the highlight
+ // will change color to indicate a successful long press.
+ Redraw();
}
diff --git a/screen_ui.h b/screen_ui.h
index 2500575..8231a2b 100644
--- a/screen_ui.h
+++ b/screen_ui.h
@@ -21,6 +21,7 @@
#include <stdio.h>
#include <string>
+#include <vector>
#include "ui.h"
@@ -30,152 +31,166 @@
// Implementation of RecoveryUI appropriate for devices with a screen
// (shows an icon + a progress bar, text logging, menu, etc.)
class ScreenRecoveryUI : public RecoveryUI {
- public:
- ScreenRecoveryUI();
+ public:
+ ScreenRecoveryUI();
- bool Init(const std::string& locale) override;
+ bool Init(const std::string& locale) override;
- // overall recovery state ("background image")
- void SetBackground(Icon icon);
- void SetSystemUpdateText(bool security_update);
+ // overall recovery state ("background image")
+ 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;
+ // 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;
+ void SetStage(int current, int max) override;
- // text log
- void ShowText(bool visible) override;
- bool IsTextVisible() override;
- bool WasTextEverVisible() override;
+ // text log
+ void ShowText(bool visible) override;
+ bool IsTextVisible() override;
+ bool WasTextEverVisible() override;
- // printing messages
- void Print(const char* fmt, ...) __printflike(2, 3);
- void PrintOnScreenOnly(const char* fmt, ...) __printflike(2, 3);
- void ShowFile(const char* filename);
+ // printing messages
+ void Print(const char* fmt, ...) override __printflike(2, 3);
+ void PrintOnScreenOnly(const char* fmt, ...) override __printflike(2, 3);
+ void ShowFile(const char* filename) override;
- // menu display
- void StartMenu(const char* const * headers, const char* const * items,
- int initial_selection);
- int SelectMenu(int sel);
- void EndMenu();
+ // menu display
+ void StartMenu(const char* const* headers, const char* const* items,
+ int initial_selection) override;
+ int SelectMenu(int sel) override;
+ void EndMenu() override;
- void KeyLongPress(int);
+ void KeyLongPress(int) override;
- void Redraw();
+ void Redraw();
- enum UIElement {
- HEADER, MENU, MENU_SEL_BG, MENU_SEL_BG_ACTIVE, MENU_SEL_FG, LOG, TEXT_FILL, INFO
- };
- void SetColor(UIElement e);
+ enum UIElement {
+ HEADER,
+ MENU,
+ MENU_SEL_BG,
+ MENU_SEL_BG_ACTIVE,
+ MENU_SEL_FG,
+ LOG,
+ TEXT_FILL,
+ INFO
+ };
+ void SetColor(UIElement e) const;
- protected:
- // The margin that we don't want to use for showing texts (e.g. round screen, or screen with
- // rounded corners).
- const int kMarginWidth;
- const int kMarginHeight;
+ protected:
+ // The margin that we don't want to use for showing texts (e.g. round screen, or screen with
+ // rounded corners).
+ const int kMarginWidth;
+ const int kMarginHeight;
- // The scale factor from dp to pixels. 1.0 for mdpi, 4.0 for xxxhdpi.
- const float density_;
+ // Number of frames per sec (default: 30) for both parts of the animation.
+ const int kAnimationFps;
- Icon currentIcon;
+ // The scale factor from dp to pixels. 1.0 for mdpi, 4.0 for xxxhdpi.
+ const float density_;
- // The layout to use.
- int layout_;
+ Icon currentIcon;
- GRSurface* error_icon;
+ // The layout to use.
+ int layout_;
- GRSurface* erasing_text;
- GRSurface* error_text;
- GRSurface* installing_text;
- GRSurface* no_command_text;
+ GRSurface* error_icon;
- GRSurface** introFrames;
- GRSurface** loopFrames;
+ GRSurface* erasing_text;
+ GRSurface* error_text;
+ GRSurface* installing_text;
+ GRSurface* no_command_text;
- GRSurface* progressBarEmpty;
- GRSurface* progressBarFill;
- GRSurface* stageMarkerEmpty;
- GRSurface* stageMarkerFill;
+ GRSurface** introFrames;
+ GRSurface** loopFrames;
- ProgressType progressBarType;
+ GRSurface* progressBarEmpty;
+ GRSurface* progressBarFill;
+ GRSurface* stageMarkerEmpty;
+ GRSurface* stageMarkerFill;
- float progressScopeStart, progressScopeSize, progress;
- double progressScopeTime, progressScopeDuration;
+ ProgressType progressBarType;
- // true when both graphics pages are the same (except for the progress bar).
- bool pagesIdentical;
+ float progressScopeStart, progressScopeSize, progress;
+ double progressScopeTime, progressScopeDuration;
- size_t text_cols_, text_rows_;
+ // true when both graphics pages are the same (except for the progress bar).
+ bool pagesIdentical;
- // Log text overlay, displayed when a magic key is pressed.
- char** text_;
- size_t text_col_, text_row_, text_top_;
+ size_t text_cols_, text_rows_;
- bool show_text;
- bool show_text_ever; // has show_text ever been true?
+ // Log text overlay, displayed when a magic key is pressed.
+ char** text_;
+ size_t text_col_, text_row_, text_top_;
- char** menu_;
- const char* const* menu_headers_;
- bool show_menu;
- int menu_items, menu_sel;
+ bool show_text;
+ bool show_text_ever; // has show_text ever been true?
- // An alternate text screen, swapped with 'text_' when we're viewing a log file.
- char** file_viewer_text_;
+ std::vector<std::string> menu_;
+ const char* const* menu_headers_;
+ bool show_menu;
+ int menu_items, menu_sel;
- pthread_t progress_thread_;
+ // An alternate text screen, swapped with 'text_' when we're viewing a log file.
+ char** file_viewer_text_;
- // Number of intro frames and loop frames in the animation.
- size_t intro_frames;
- size_t loop_frames;
+ pthread_t progress_thread_;
- size_t current_frame;
- bool intro_done;
+ // Number of intro frames and loop frames in the animation.
+ size_t intro_frames;
+ size_t loop_frames;
- // Number of frames per sec (default: 30) for both parts of the animation.
- int animation_fps;
+ size_t current_frame;
+ bool intro_done;
- int stage, max_stage;
+ int stage, max_stage;
- int char_width_;
- int char_height_;
+ int char_width_;
+ int char_height_;
- pthread_mutex_t updateMutex;
+ pthread_mutex_t updateMutex;
- virtual bool InitTextParams();
+ 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();
+ 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();
+ GRSurface* GetCurrentFrame() const;
+ GRSurface* GetCurrentText() const;
- static void* ProgressThreadStartRoutine(void* data);
- void ProgressThreadLoop();
+ static void* ProgressThreadStartRoutine(void* data);
+ void ProgressThreadLoop();
- virtual void ShowFile(FILE*);
- virtual void PrintV(const char*, bool, va_list);
- void PutChar(char);
- void ClearText();
+ virtual void ShowFile(FILE*);
+ virtual void PrintV(const char*, bool, va_list);
+ void PutChar(char);
+ void ClearText();
- void LoadAnimation();
- void LoadBitmap(const char* filename, GRSurface** surface);
- void LoadLocalizedBitmap(const char* filename, GRSurface** surface);
+ void LoadAnimation();
+ void LoadBitmap(const char* filename, GRSurface** surface);
+ void LoadLocalizedBitmap(const char* filename, GRSurface** surface);
- int PixelsFromDp(int dp) const;
- virtual int GetAnimationBaseline();
- virtual int GetProgressBaseline();
- virtual int GetTextBaseline();
+ int PixelsFromDp(int dp) const;
+ virtual int GetAnimationBaseline() const;
+ virtual int GetProgressBaseline() const;
+ virtual int GetTextBaseline() const;
- virtual void DrawHorizontalRule(int* y);
- virtual void DrawHighlightBar(int x, int y, int width, int height) const;
- virtual void DrawTextLine(int x, int* y, const char* line, bool bold) const;
- void DrawTextLines(int x, int* y, const char* const* lines) const;
+ // Draws a highlight bar at (x, y) - (x + width, y + height).
+ virtual void DrawHighlightBar(int x, int y, int width, int height) const;
+ // Draws a horizontal rule at Y. Returns the offset it should be moving along Y-axis.
+ virtual int DrawHorizontalRule(int y) const;
+ // Draws a line of text. Returns the offset it should be moving along Y-axis.
+ virtual int DrawTextLine(int x, int y, const char* line, bool bold) const;
+ // Draws multiple text lines. Returns the offset it should be moving along Y-axis.
+ int DrawTextLines(int x, int y, const char* const* lines) const;
+ // Similar to DrawTextLines() to draw multiple text lines, but additionally wraps long lines.
+ // Returns the offset it should be moving along Y-axis.
+ int DrawWrappedTextLines(int x, int y, const char* const* lines) const;
};
#endif // RECOVERY_UI_H
diff --git a/tests/Android.mk b/tests/Android.mk
index 346873d..f2497b8 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -111,7 +111,8 @@
component/update_verifier_test.cpp \
component/verifier_test.cpp
-LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_SHARED_LIBRARIES := \
+ libhidlbase
tune2fs_static_libraries := \
libext2_com_err \
@@ -160,6 +161,7 @@
libfec_rs \
libsquashfs_utils \
libcutils \
+ libbrotli \
$(tune2fs_static_libraries)
testdata_files := $(call find-subdir-files, testdata/*)
diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp
index 6f5960b..bf25aeb 100644
--- a/tests/component/imgdiff_test.cpp
+++ b/tests/component/imgdiff_test.cpp
@@ -328,6 +328,39 @@
verify_patched_image(src, patch, tgt);
}
+TEST(ImgdiffTest, image_mode_bad_gzip) {
+ // Modify the uncompressed length in the gzip footer.
+ 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',
+ '\xff', '\xff', '\xff' };
+ const std::string src(src_data.cbegin(), src_data.cend());
+ TemporaryFile src_file;
+ ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
+
+ // Modify the uncompressed length in the gzip footer.
+ 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', '\xff', '\xff', '\xff'
+ };
+ 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));
+ verify_patched_image(src, patch, tgt);
+}
+
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 = {
diff --git a/tests/component/update_verifier_test.cpp b/tests/component/update_verifier_test.cpp
index 5fc7ef6..b04e118 100644
--- a/tests/component/update_verifier_test.cpp
+++ b/tests/component/update_verifier_test.cpp
@@ -81,3 +81,16 @@
ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path));
ASSERT_FALSE(verify_image(temp_file.path));
}
+
+TEST_F(UpdateVerifierTest, verify_image_legacy_care_map) {
+ // This test relies on dm-verity support.
+ if (!verity_supported) {
+ GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support.";
+ return;
+ }
+
+ TemporaryFile temp_file;
+ std::string content = "/dev/block/bootdevice/by-name/system\n2,1,0";
+ ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path));
+ ASSERT_TRUE(verify_image(temp_file.path));
+}
diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp
index 35e87fd..6c341c1 100644
--- a/tests/component/updater_test.cpp
+++ b/tests/component/updater_test.cpp
@@ -15,10 +15,12 @@
*/
#include <stdio.h>
+#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include <algorithm>
#include <memory>
#include <string>
#include <vector>
@@ -29,6 +31,7 @@
#include <android-base/strings.h>
#include <android-base/test_utils.h>
#include <bootloader_message/bootloader_message.h>
+#include <brotli/encode.h>
#include <bsdiff.h>
#include <gtest/gtest.h>
#include <ziparchive/zip_archive.h>
@@ -224,102 +227,6 @@
expect("", script6.c_str(), kNoCause);
}
-TEST_F(UpdaterTest, package_extract_dir) {
- // package_extract_dir expects 2 arguments.
- expect(nullptr, "package_extract_dir()", kArgsParsingFailure);
- expect(nullptr, "package_extract_dir(\"arg1\")", kArgsParsingFailure);
- expect(nullptr, "package_extract_dir(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure);
-
- std::string zip_path = from_testdata_base("ziptest_valid.zip");
- ZipArchiveHandle handle;
- ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
-
- // Need to set up the ziphandle.
- UpdaterInfo updater_info;
- updater_info.package_zip = handle;
-
- // Extract "b/c.txt" and "b/d.txt" with package_extract_dir("b", "<dir>").
- TemporaryDir td;
- std::string temp_dir(td.path);
- std::string script("package_extract_dir(\"b\", \"" + temp_dir + "\")");
- expect("t", script.c_str(), kNoCause, &updater_info);
-
- // Verify.
- std::string data;
- std::string file_c = temp_dir + "/c.txt";
- ASSERT_TRUE(android::base::ReadFileToString(file_c, &data));
- ASSERT_EQ(kCTxtContents, data);
-
- std::string file_d = temp_dir + "/d.txt";
- ASSERT_TRUE(android::base::ReadFileToString(file_d, &data));
- ASSERT_EQ(kDTxtContents, data);
-
- // Modify the contents in order to retry. It's expected to be overwritten.
- ASSERT_TRUE(android::base::WriteStringToFile("random", file_c));
- ASSERT_TRUE(android::base::WriteStringToFile("random", file_d));
-
- // Extract again and verify.
- expect("t", script.c_str(), kNoCause, &updater_info);
-
- ASSERT_TRUE(android::base::ReadFileToString(file_c, &data));
- ASSERT_EQ(kCTxtContents, data);
- ASSERT_TRUE(android::base::ReadFileToString(file_d, &data));
- ASSERT_EQ(kDTxtContents, data);
-
- // Clean up the temp files under td.
- ASSERT_EQ(0, unlink(file_c.c_str()));
- ASSERT_EQ(0, unlink(file_d.c_str()));
-
- // Extracting "b/" (with slash) should give the same result.
- script = "package_extract_dir(\"b/\", \"" + temp_dir + "\")";
- expect("t", script.c_str(), kNoCause, &updater_info);
-
- ASSERT_TRUE(android::base::ReadFileToString(file_c, &data));
- ASSERT_EQ(kCTxtContents, data);
- ASSERT_TRUE(android::base::ReadFileToString(file_d, &data));
- ASSERT_EQ(kDTxtContents, data);
-
- ASSERT_EQ(0, unlink(file_c.c_str()));
- ASSERT_EQ(0, unlink(file_d.c_str()));
-
- // Extracting "" is allowed. The entries will carry the path name.
- script = "package_extract_dir(\"\", \"" + temp_dir + "\")";
- expect("t", script.c_str(), kNoCause, &updater_info);
-
- std::string file_a = temp_dir + "/a.txt";
- ASSERT_TRUE(android::base::ReadFileToString(file_a, &data));
- ASSERT_EQ(kATxtContents, data);
- std::string file_b = temp_dir + "/b.txt";
- ASSERT_TRUE(android::base::ReadFileToString(file_b, &data));
- ASSERT_EQ(kBTxtContents, data);
- std::string file_b_c = temp_dir + "/b/c.txt";
- ASSERT_TRUE(android::base::ReadFileToString(file_b_c, &data));
- ASSERT_EQ(kCTxtContents, data);
- std::string file_b_d = temp_dir + "/b/d.txt";
- ASSERT_TRUE(android::base::ReadFileToString(file_b_d, &data));
- ASSERT_EQ(kDTxtContents, data);
-
- ASSERT_EQ(0, unlink(file_a.c_str()));
- ASSERT_EQ(0, unlink(file_b.c_str()));
- ASSERT_EQ(0, unlink(file_b_c.c_str()));
- ASSERT_EQ(0, unlink(file_b_d.c_str()));
- ASSERT_EQ(0, rmdir((temp_dir + "/b").c_str()));
-
- // Extracting non-existent entry should still give "t".
- script = "package_extract_dir(\"doesntexist\", \"" + temp_dir + "\")";
- expect("t", script.c_str(), kNoCause, &updater_info);
-
- // Only relative zip_path is allowed.
- script = "package_extract_dir(\"/b\", \"" + temp_dir + "\")";
- expect("", script.c_str(), kNoCause, &updater_info);
-
- // Only absolute dest_path is allowed.
- script = "package_extract_dir(\"b\", \"path\")";
- expect("", script.c_str(), kNoCause, &updater_info);
-
- CloseArchive(handle);
-}
-
// TODO: Test extracting to block device.
TEST_F(UpdaterTest, package_extract_file) {
// package_extract_file expects 1 or 2 arguments.
@@ -578,7 +485,7 @@
UpdaterInfo updater_info;
updater_info.package_zip = handle;
TemporaryFile temp_pipe;
- updater_info.cmd_pipe = fopen(temp_pipe.path, "wb");
+ updater_info.cmd_pipe = fopen(temp_pipe.path, "wbe");
updater_info.package_zip_addr = map.addr;
updater_info.package_zip_len = map.length;
@@ -654,7 +561,7 @@
UpdaterInfo updater_info;
updater_info.package_zip = handle;
TemporaryFile temp_pipe;
- updater_info.cmd_pipe = fopen(temp_pipe.path, "wb");
+ updater_info.cmd_pipe = fopen(temp_pipe.path, "wbe");
updater_info.package_zip_addr = map.addr;
updater_info.package_zip_len = map.length;
@@ -672,4 +579,79 @@
std::string script_exact_data = "block_image_update(\"" + std::string(update_file.path) +
R"(", package_extract_file("transfer_list"), "exact_new_data", "patch_data"))";
expect("t", script_exact_data.c_str(), kNoCause, &updater_info);
+ CloseArchive(handle);
+}
+
+TEST_F(UpdaterTest, brotli_new_data) {
+ // Create a zip file with new_data.
+ TemporaryFile zip_file;
+ FILE* zip_file_ptr = fdopen(zip_file.fd, "wb");
+ ZipWriter zip_writer(zip_file_ptr);
+
+ // Add a brotli compressed new data entry.
+ ASSERT_EQ(0, zip_writer.StartEntry("new.dat.br", 0));
+
+ auto generator = []() { return rand() % 128; };
+ // Generate 100 blocks of random data.
+ std::string brotli_new_data;
+ brotli_new_data.reserve(4096 * 100);
+ generate_n(back_inserter(brotli_new_data), 4096 * 100, generator);
+
+ size_t encoded_size = BrotliEncoderMaxCompressedSize(brotli_new_data.size());
+ std::vector<uint8_t> encoded_data(encoded_size);
+ ASSERT_TRUE(BrotliEncoderCompress(
+ BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, brotli_new_data.size(),
+ reinterpret_cast<const uint8_t*>(brotli_new_data.data()), &encoded_size, encoded_data.data()));
+
+ ASSERT_EQ(0, zip_writer.WriteBytes(encoded_data.data(), encoded_size));
+ ASSERT_EQ(0, zip_writer.FinishEntry());
+ // Add a dummy patch data.
+ ASSERT_EQ(0, zip_writer.StartEntry("patch_data", 0));
+ ASSERT_EQ(0, zip_writer.FinishEntry());
+
+ // Write a few small chunks of new data, then a large chunk, and finally a few small chunks.
+ // This helps us to catch potential short writes.
+ std::vector<std::string> transfer_list = {
+ "4",
+ "100",
+ "0",
+ "0",
+ "new 2,0,1",
+ "new 2,1,2",
+ "new 4,2,50,50,97",
+ "new 2,97,98",
+ "new 2,98,99",
+ "new 2,99,100",
+ };
+ ASSERT_EQ(0, zip_writer.StartEntry("transfer_list", 0));
+ std::string commands = android::base::Join(transfer_list, '\n');
+ ASSERT_EQ(0, zip_writer.WriteBytes(commands.data(), commands.size()));
+ ASSERT_EQ(0, zip_writer.FinishEntry());
+ ASSERT_EQ(0, zip_writer.Finish());
+ ASSERT_EQ(0, fclose(zip_file_ptr));
+
+ MemMapping map;
+ ASSERT_TRUE(map.MapFile(zip_file.path));
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle));
+
+ // Set up the handler, command_pipe, patch offset & length.
+ UpdaterInfo updater_info;
+ updater_info.package_zip = handle;
+ TemporaryFile temp_pipe;
+ updater_info.cmd_pipe = fopen(temp_pipe.path, "wb");
+ updater_info.package_zip_addr = map.addr;
+ updater_info.package_zip_len = map.length;
+
+ // Check if we can decompress the new data correctly.
+ TemporaryFile update_file;
+ std::string script_new_data =
+ "block_image_update(\"" + std::string(update_file.path) +
+ R"(", package_extract_file("transfer_list"), "new.dat.br", "patch_data"))";
+ expect("t", script_new_data.c_str(), kNoCause, &updater_info);
+
+ std::string updated_content;
+ ASSERT_TRUE(android::base::ReadFileToString(update_file.path, &updated_content));
+ ASSERT_EQ(brotli_new_data, updated_content);
+ CloseArchive(handle);
}
diff --git a/tests/manual/recovery_test.cpp b/tests/manual/recovery_test.cpp
index d36dd33..92c6ef2 100644
--- a/tests/manual/recovery_test.cpp
+++ b/tests/manual/recovery_test.cpp
@@ -141,7 +141,7 @@
// under recovery.
void SetUp() override {
std::string file_path = GetParam();
- fp = fopen(file_path.c_str(), "rb");
+ fp = fopen(file_path.c_str(), "rbe");
ASSERT_NE(nullptr, fp);
unsigned char header[8];
diff --git a/tools/recovery_l10n/res/values-en-rCA/strings.xml b/tools/recovery_l10n/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..dc75c23
--- /dev/null
+++ b/tools/recovery_l10n/res/values-en-rCA/strings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="recovery_installing" msgid="2013591905463558223">"Installing system update"</string>
+ <string name="recovery_erasing" msgid="7334826894904037088">"Erasing"</string>
+ <string name="recovery_no_command" msgid="4465476568623024327">"No command"</string>
+ <string name="recovery_error" msgid="5748178989622716736">"Error!"</string>
+ <string name="recovery_installing_security" msgid="9184031299717114342">"Installing security update"</string>
+</resources>
diff --git a/tools/recovery_l10n/res/values-en-rXC/strings.xml b/tools/recovery_l10n/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..2d528b3
--- /dev/null
+++ b/tools/recovery_l10n/res/values-en-rXC/strings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="recovery_installing" msgid="2013591905463558223">"Installing system update"</string>
+ <string name="recovery_erasing" msgid="7334826894904037088">"Erasing"</string>
+ <string name="recovery_no_command" msgid="4465476568623024327">"No command"</string>
+ <string name="recovery_error" msgid="5748178989622716736">"Error!"</string>
+ <string name="recovery_installing_security" msgid="9184031299717114342">"Installing security update"</string>
+</resources>
diff --git a/tools/recovery_l10n/res/values-hi/strings.xml b/tools/recovery_l10n/res/values-hi/strings.xml
index a8a876e..65d0033 100644
--- a/tools/recovery_l10n/res/values-hi/strings.xml
+++ b/tools/recovery_l10n/res/values-hi/strings.xml
@@ -3,7 +3,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="recovery_installing" msgid="2013591905463558223">"सिस्टम अपडेट इंस्टॉल किया जा रहा है"</string>
<string name="recovery_erasing" msgid="7334826894904037088">"मिटाया जा रहा है"</string>
- <string name="recovery_no_command" msgid="4465476568623024327">"कोई आदेश नहीं"</string>
+ <string name="recovery_no_command" msgid="4465476568623024327">"कोई निर्देश नहीं मिला"</string>
<string name="recovery_error" msgid="5748178989622716736">"गड़बड़ी!"</string>
<string name="recovery_installing_security" msgid="9184031299717114342">"सुरक्षा अपडेट इंस्टॉल किया जा रहा है"</string>
</resources>
diff --git a/tools/recovery_l10n/res/values-mr/strings.xml b/tools/recovery_l10n/res/values-mr/strings.xml
index 8cf86f7..5f82033 100644
--- a/tools/recovery_l10n/res/values-mr/strings.xml
+++ b/tools/recovery_l10n/res/values-mr/strings.xml
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="recovery_installing" msgid="2013591905463558223">"सिस्टम अद्यतन स्थापित करीत आहे"</string>
+ <string name="recovery_installing" msgid="2013591905463558223">"सिस्टम अपडेट इंस्टॉल करत आहे"</string>
<string name="recovery_erasing" msgid="7334826894904037088">"मिटवत आहे"</string>
- <string name="recovery_no_command" msgid="4465476568623024327">"कोणताही आदेश नाही"</string>
- <string name="recovery_error" msgid="5748178989622716736">"त्रुटी!"</string>
- <string name="recovery_installing_security" msgid="9184031299717114342">"सुरक्षा अद्यतन स्थापित करीत आहे"</string>
+ <string name="recovery_no_command" msgid="4465476568623024327">"कोणतीही कमांड नाही"</string>
+ <string name="recovery_error" msgid="5748178989622716736">"एरर!"</string>
+ <string name="recovery_installing_security" msgid="9184031299717114342">"सुरक्षा अपडेट इंस्टॉल करत आहे"</string>
</resources>
diff --git a/tools/recovery_l10n/res/values-pa/strings.xml b/tools/recovery_l10n/res/values-pa/strings.xml
index 8564c9c..27972d1 100644
--- a/tools/recovery_l10n/res/values-pa/strings.xml
+++ b/tools/recovery_l10n/res/values-pa/strings.xml
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="recovery_installing" msgid="2013591905463558223">"ਸਿਸਟਮ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"</string>
+ <string name="recovery_installing" msgid="2013591905463558223">"ਸਿਸਟਮ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
<string name="recovery_erasing" msgid="7334826894904037088">"ਮਿਟਾਈ ਜਾ ਰਹੀ ਹੈ"</string>
- <string name="recovery_no_command" msgid="4465476568623024327">"ਕੋਈ ਕਮਾਂਡ ਨਹੀਂ"</string>
+ <string name="recovery_no_command" msgid="4465476568623024327">"ਕੋਈ ਆਦੇਸ਼ ਨਹੀਂ"</string>
<string name="recovery_error" msgid="5748178989622716736">"ਅਸ਼ੁੱਧੀ!"</string>
- <string name="recovery_installing_security" msgid="9184031299717114342">"ਸੁਰੱਖਿਆ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"</string>
+ <string name="recovery_installing_security" msgid="9184031299717114342">"ਸੁਰੱਖਿਆ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
</resources>
diff --git a/tools/recovery_l10n/res/values-te/strings.xml b/tools/recovery_l10n/res/values-te/strings.xml
index cfb02c9..e35c82b 100644
--- a/tools/recovery_l10n/res/values-te/strings.xml
+++ b/tools/recovery_l10n/res/values-te/strings.xml
@@ -4,6 +4,6 @@
<string name="recovery_installing" msgid="2013591905463558223">"సిస్టమ్ నవీకరణను ఇన్స్టాల్ చేస్తోంది"</string>
<string name="recovery_erasing" msgid="7334826894904037088">"డేటాను తొలగిస్తోంది"</string>
<string name="recovery_no_command" msgid="4465476568623024327">"ఆదేశం లేదు"</string>
- <string name="recovery_error" msgid="5748178989622716736">"లోపం సంభవించింది!"</string>
+ <string name="recovery_error" msgid="5748178989622716736">"ఎర్రర్ సంభవించింది!"</string>
<string name="recovery_installing_security" msgid="9184031299717114342">"భద్రతా నవీకరణను ఇన్స్టాల్ చేస్తోంది"</string>
</resources>
diff --git a/ui.cpp b/ui.cpp
index cad7449..e80d7ed 100644
--- a/ui.cpp
+++ b/ui.cpp
@@ -54,6 +54,9 @@
rtl_locale_(false),
brightness_normal_(50),
brightness_dimmed_(25),
+ touch_screen_allowed_(false),
+ kTouchLowThreshold(RECOVERY_UI_TOUCH_LOW_THRESHOLD),
+ kTouchHighThreshold(RECOVERY_UI_TOUCH_HIGH_THRESHOLD),
key_queue_len(0),
key_last_down(-1),
key_long_press(false),
@@ -64,6 +67,9 @@
has_power_key(false),
has_up_key(false),
has_down_key(false),
+ has_touch_screen(false),
+ touch_slot_(0),
+ is_bootreason_recovery_ui_(false),
screensaver_state_(ScreensaverState::DISABLED) {
pthread_mutex_init(&key_queue_mutex, nullptr);
pthread_cond_init(&key_queue_cond, nullptr);
@@ -71,23 +77,25 @@
}
void RecoveryUI::OnKeyDetected(int key_code) {
- if (key_code == KEY_POWER) {
- has_power_key = true;
- } else if (key_code == KEY_DOWN || key_code == KEY_VOLUMEDOWN) {
- has_down_key = true;
- } else if (key_code == KEY_UP || key_code == KEY_VOLUMEUP) {
- has_up_key = true;
- }
+ if (key_code == KEY_POWER) {
+ has_power_key = true;
+ } else if (key_code == KEY_DOWN || key_code == KEY_VOLUMEDOWN) {
+ has_down_key = true;
+ } else if (key_code == KEY_UP || key_code == KEY_VOLUMEUP) {
+ has_up_key = true;
+ } else if (key_code == ABS_MT_POSITION_X || key_code == ABS_MT_POSITION_Y) {
+ has_touch_screen = true;
+ }
}
// Reads input events, handles special hot keys, and adds to the key queue.
static void* InputThreadLoop(void*) {
- while (true) {
- if (!ev_wait(-1)) {
- ev_dispatch();
- }
+ while (true) {
+ if (!ev_wait(-1)) {
+ ev_dispatch();
}
- return nullptr;
+ }
+ return nullptr;
}
bool RecoveryUI::InitScreensaver() {
@@ -128,10 +136,28 @@
// Set up the locale info.
SetLocale(locale);
- ev_init(std::bind(&RecoveryUI::OnInputEvent, this, std::placeholders::_1, std::placeholders::_2));
+ ev_init(std::bind(&RecoveryUI::OnInputEvent, this, std::placeholders::_1, std::placeholders::_2),
+ touch_screen_allowed_);
ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));
+ if (touch_screen_allowed_) {
+ ev_iterate_touch_inputs(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));
+
+ // Parse /proc/cmdline to determine if it's booting into recovery with a bootreason of
+ // "recovery_ui". This specific reason is set by some (wear) bootloaders, to allow an easier way
+ // to turn on text mode. It will only be set if the recovery boot is triggered from fastboot, or
+ // with 'adb reboot recovery'. Note that this applies to all build variants. Otherwise the text
+ // mode will be turned on automatically on debuggable builds, even without a swipe.
+ std::string cmdline;
+ if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
+ is_bootreason_recovery_ui_ = cmdline.find("bootreason=recovery_ui") != std::string::npos;
+ } else {
+ // Non-fatal, and won't affect Init() result.
+ PLOG(WARNING) << "Failed to read /proc/cmdline";
+ }
+ }
+
if (!InitScreensaver()) {
LOG(INFO) << "Screensaver disabled";
}
@@ -140,40 +166,157 @@
return true;
}
+void RecoveryUI::OnTouchDetected(int dx, int dy) {
+ enum SwipeDirection { UP, DOWN, RIGHT, LEFT } direction;
+
+ // We only consider a valid swipe if:
+ // - the delta along one axis is below kTouchLowThreshold;
+ // - and the delta along the other axis is beyond kTouchHighThreshold.
+ if (abs(dy) < kTouchLowThreshold && abs(dx) > kTouchHighThreshold) {
+ direction = dx < 0 ? SwipeDirection::LEFT : SwipeDirection::RIGHT;
+ } else if (abs(dx) < kTouchLowThreshold && abs(dy) > kTouchHighThreshold) {
+ direction = dy < 0 ? SwipeDirection::UP : SwipeDirection::DOWN;
+ } else {
+ LOG(DEBUG) << "Ignored " << dx << " " << dy << " (low: " << kTouchLowThreshold
+ << ", high: " << kTouchHighThreshold << ")";
+ return;
+ }
+
+ // Allow turning on text mode with any swipe, if bootloader has set a bootreason of recovery_ui.
+ if (is_bootreason_recovery_ui_ && !IsTextVisible()) {
+ ShowText(true);
+ return;
+ }
+
+ LOG(DEBUG) << "Swipe direction=" << direction;
+ switch (direction) {
+ case SwipeDirection::UP:
+ ProcessKey(KEY_UP, 1); // press up key
+ ProcessKey(KEY_UP, 0); // and release it
+ break;
+
+ case SwipeDirection::DOWN:
+ ProcessKey(KEY_DOWN, 1); // press down key
+ ProcessKey(KEY_DOWN, 0); // and release it
+ break;
+
+ case SwipeDirection::LEFT:
+ case SwipeDirection::RIGHT:
+ ProcessKey(KEY_POWER, 1); // press power key
+ ProcessKey(KEY_POWER, 0); // and release it
+ break;
+ };
+}
+
int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) {
- struct input_event ev;
- if (ev_get_input(fd, epevents, &ev) == -1) {
- return -1;
- }
+ struct input_event ev;
+ if (ev_get_input(fd, epevents, &ev) == -1) {
+ return -1;
+ }
- if (ev.type == EV_SYN) {
- return 0;
- } else if (ev.type == EV_REL) {
- if (ev.code == REL_Y) {
- // accumulate the up or down motion reported by
- // the trackball. When it exceeds a threshold
- // (positive or negative), fake an up/down
- // key event.
- rel_sum += ev.value;
- if (rel_sum > 3) {
- ProcessKey(KEY_DOWN, 1); // press down key
- ProcessKey(KEY_DOWN, 0); // and release it
- rel_sum = 0;
- } else if (rel_sum < -3) {
- ProcessKey(KEY_UP, 1); // press up key
- ProcessKey(KEY_UP, 0); // and release it
- rel_sum = 0;
- }
- }
- } else {
- rel_sum = 0;
- }
+ // Touch inputs handling.
+ //
+ // We handle the touch inputs by tracking the position changes between initial contacting and
+ // upon lifting. touch_start_X/Y record the initial positions, with touch_finger_down set. Upon
+ // detecting the lift, we unset touch_finger_down and detect a swipe based on position changes.
+ //
+ // Per the doc Multi-touch Protocol at below, there are two protocols.
+ // https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt
+ //
+ // The main difference between the stateless type A protocol and the stateful type B slot protocol
+ // lies in the usage of identifiable contacts to reduce the amount of data sent to userspace. The
+ // slot protocol (i.e. type B) sends ABS_MT_TRACKING_ID with a unique id on initial contact, and
+ // sends ABS_MT_TRACKING_ID -1 upon lifting the contact. Protocol A doesn't send
+ // ABS_MT_TRACKING_ID -1 on lifting, but the driver may additionally report BTN_TOUCH event.
+ //
+ // For protocol A, we rely on BTN_TOUCH to recognize lifting, while for protocol B we look for
+ // ABS_MT_TRACKING_ID being -1.
+ //
+ // Touch input events will only be available if touch_screen_allowed_ is set.
- if (ev.type == EV_KEY && ev.code <= KEY_MAX) {
- ProcessKey(ev.code, ev.value);
+ if (ev.type == EV_SYN) {
+ if (touch_screen_allowed_ && ev.code == SYN_REPORT) {
+ // There might be multiple SYN_REPORT events. We should only detect a swipe after lifting the
+ // contact.
+ if (touch_finger_down_ && !touch_swiping_) {
+ touch_start_X_ = touch_X_;
+ touch_start_Y_ = touch_Y_;
+ touch_swiping_ = true;
+ } else if (!touch_finger_down_ && touch_swiping_) {
+ touch_swiping_ = false;
+ OnTouchDetected(touch_X_ - touch_start_X_, touch_Y_ - touch_start_Y_);
+ }
}
-
return 0;
+ }
+
+ if (ev.type == EV_REL) {
+ if (ev.code == REL_Y) {
+ // accumulate the up or down motion reported by
+ // the trackball. When it exceeds a threshold
+ // (positive or negative), fake an up/down
+ // key event.
+ rel_sum += ev.value;
+ if (rel_sum > 3) {
+ ProcessKey(KEY_DOWN, 1); // press down key
+ ProcessKey(KEY_DOWN, 0); // and release it
+ rel_sum = 0;
+ } else if (rel_sum < -3) {
+ ProcessKey(KEY_UP, 1); // press up key
+ ProcessKey(KEY_UP, 0); // and release it
+ rel_sum = 0;
+ }
+ }
+ } else {
+ rel_sum = 0;
+ }
+
+ if (touch_screen_allowed_ && ev.type == EV_ABS) {
+ if (ev.code == ABS_MT_SLOT) {
+ touch_slot_ = ev.value;
+ }
+ // Ignore other fingers.
+ if (touch_slot_ > 0) return 0;
+
+ switch (ev.code) {
+ case ABS_MT_POSITION_X:
+ touch_X_ = ev.value;
+ touch_finger_down_ = true;
+ break;
+
+ case ABS_MT_POSITION_Y:
+ touch_Y_ = ev.value;
+ touch_finger_down_ = true;
+ break;
+
+ case ABS_MT_TRACKING_ID:
+ // Protocol B: -1 marks lifting the contact.
+ if (ev.value < 0) touch_finger_down_ = false;
+ break;
+ }
+ return 0;
+ }
+
+ if (ev.type == EV_KEY && ev.code <= KEY_MAX) {
+ if (touch_screen_allowed_) {
+ if (ev.code == BTN_TOUCH) {
+ // A BTN_TOUCH with value 1 indicates the start of contact (protocol A), with 0 means
+ // lifting the contact.
+ touch_finger_down_ = (ev.value == 1);
+ }
+
+ // Intentionally ignore BTN_TOUCH and BTN_TOOL_FINGER, which would otherwise trigger
+ // additional scrolling (because in ScreenRecoveryUI::ShowFile(), we consider keys other than
+ // KEY_POWER and KEY_UP as KEY_DOWN).
+ if (ev.code == BTN_TOUCH || ev.code == BTN_TOOL_FINGER) {
+ return 0;
+ }
+ }
+
+ ProcessKey(ev.code, ev.value);
+ }
+
+ return 0;
}
// Process a key-up or -down event. A key is "registered" when it is
@@ -189,82 +332,84 @@
//
// updown == 1 for key down events; 0 for key up events
void RecoveryUI::ProcessKey(int key_code, int updown) {
- bool register_key = false;
- bool long_press = false;
- bool reboot_enabled;
+ bool register_key = false;
+ bool long_press = false;
+ bool reboot_enabled;
- pthread_mutex_lock(&key_queue_mutex);
- key_pressed[key_code] = updown;
- if (updown) {
- ++key_down_count;
- key_last_down = key_code;
- key_long_press = false;
- key_timer_t* info = new key_timer_t;
- info->ui = this;
- info->key_code = key_code;
- info->count = key_down_count;
- pthread_t thread;
- pthread_create(&thread, nullptr, &RecoveryUI::time_key_helper, info);
- pthread_detach(thread);
- } else {
- if (key_last_down == key_code) {
- long_press = key_long_press;
- register_key = true;
- }
- key_last_down = -1;
+ pthread_mutex_lock(&key_queue_mutex);
+ key_pressed[key_code] = updown;
+ if (updown) {
+ ++key_down_count;
+ key_last_down = key_code;
+ key_long_press = false;
+ key_timer_t* info = new key_timer_t;
+ info->ui = this;
+ info->key_code = key_code;
+ info->count = key_down_count;
+ pthread_t thread;
+ pthread_create(&thread, nullptr, &RecoveryUI::time_key_helper, info);
+ pthread_detach(thread);
+ } else {
+ if (key_last_down == key_code) {
+ long_press = key_long_press;
+ register_key = true;
}
- reboot_enabled = enable_reboot;
- pthread_mutex_unlock(&key_queue_mutex);
+ key_last_down = -1;
+ }
+ reboot_enabled = enable_reboot;
+ pthread_mutex_unlock(&key_queue_mutex);
- if (register_key) {
- switch (CheckKey(key_code, long_press)) {
- case RecoveryUI::IGNORE:
- break;
+ if (register_key) {
+ switch (CheckKey(key_code, long_press)) {
+ case RecoveryUI::IGNORE:
+ break;
- case RecoveryUI::TOGGLE:
- ShowText(!IsTextVisible());
- break;
+ case RecoveryUI::TOGGLE:
+ ShowText(!IsTextVisible());
+ break;
- case RecoveryUI::REBOOT:
- if (reboot_enabled) {
- reboot("reboot,");
- while (true) { pause(); }
- }
- break;
-
- case RecoveryUI::ENQUEUE:
- EnqueueKey(key_code);
- break;
+ case RecoveryUI::REBOOT:
+ if (reboot_enabled) {
+ reboot("reboot,");
+ while (true) {
+ pause();
+ }
}
+ break;
+
+ case RecoveryUI::ENQUEUE:
+ EnqueueKey(key_code);
+ break;
}
+ }
}
void* RecoveryUI::time_key_helper(void* cookie) {
- key_timer_t* info = static_cast<key_timer_t*>(cookie);
- info->ui->time_key(info->key_code, info->count);
- delete info;
- return nullptr;
+ key_timer_t* info = static_cast<key_timer_t*>(cookie);
+ info->ui->time_key(info->key_code, info->count);
+ delete info;
+ return nullptr;
}
void RecoveryUI::time_key(int key_code, int count) {
- usleep(750000); // 750 ms == "long"
- bool long_press = false;
- pthread_mutex_lock(&key_queue_mutex);
- if (key_last_down == key_code && key_down_count == count) {
- long_press = key_long_press = true;
- }
- pthread_mutex_unlock(&key_queue_mutex);
- if (long_press) KeyLongPress(key_code);
+ usleep(750000); // 750 ms == "long"
+ bool long_press = false;
+ pthread_mutex_lock(&key_queue_mutex);
+ if (key_last_down == key_code && key_down_count == count) {
+ long_press = key_long_press = true;
+ }
+ pthread_mutex_unlock(&key_queue_mutex);
+ if (long_press) KeyLongPress(key_code);
}
void RecoveryUI::EnqueueKey(int key_code) {
- pthread_mutex_lock(&key_queue_mutex);
- const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]);
- if (key_queue_len < queue_max) {
- key_queue[key_queue_len++] = key_code;
- pthread_cond_signal(&key_queue_cond);
- }
- pthread_mutex_unlock(&key_queue_mutex);
+ pthread_mutex_lock(&key_queue_mutex);
+ const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]);
+ if (key_queue_len < queue_max) {
+ key_queue[key_queue_len++] = key_code;
+ pthread_cond_signal(&key_queue_cond);
+ }
+ pthread_mutex_unlock(&key_queue_mutex);
}
int RecoveryUI::WaitKey() {
@@ -330,98 +475,104 @@
}
bool RecoveryUI::IsUsbConnected() {
- int fd = open("/sys/class/android_usb/android0/state", O_RDONLY);
- if (fd < 0) {
- printf("failed to open /sys/class/android_usb/android0/state: %s\n",
- strerror(errno));
- return 0;
- }
+ int fd = open("/sys/class/android_usb/android0/state", O_RDONLY);
+ if (fd < 0) {
+ printf("failed to open /sys/class/android_usb/android0/state: %s\n", strerror(errno));
+ return 0;
+ }
- char buf;
- // USB is connected if android_usb state is CONNECTED or CONFIGURED.
- int connected = (TEMP_FAILURE_RETRY(read(fd, &buf, 1)) == 1) && (buf == 'C');
- if (close(fd) < 0) {
- printf("failed to close /sys/class/android_usb/android0/state: %s\n",
- strerror(errno));
- }
- return connected;
+ char buf;
+ // USB is connected if android_usb state is CONNECTED or CONFIGURED.
+ int connected = (TEMP_FAILURE_RETRY(read(fd, &buf, 1)) == 1) && (buf == 'C');
+ if (close(fd) < 0) {
+ printf("failed to close /sys/class/android_usb/android0/state: %s\n", strerror(errno));
+ }
+ return connected;
}
bool RecoveryUI::IsKeyPressed(int key) {
- pthread_mutex_lock(&key_queue_mutex);
- int pressed = key_pressed[key];
- pthread_mutex_unlock(&key_queue_mutex);
- return pressed;
+ pthread_mutex_lock(&key_queue_mutex);
+ int pressed = key_pressed[key];
+ pthread_mutex_unlock(&key_queue_mutex);
+ return pressed;
}
bool RecoveryUI::IsLongPress() {
- pthread_mutex_lock(&key_queue_mutex);
- bool result = key_long_press;
- pthread_mutex_unlock(&key_queue_mutex);
- return result;
+ pthread_mutex_lock(&key_queue_mutex);
+ bool result = key_long_press;
+ pthread_mutex_unlock(&key_queue_mutex);
+ return result;
}
bool RecoveryUI::HasThreeButtons() {
- return has_power_key && has_up_key && has_down_key;
+ return has_power_key && has_up_key && has_down_key;
+}
+
+bool RecoveryUI::HasPowerKey() const {
+ return has_power_key;
+}
+
+bool RecoveryUI::HasTouchScreen() const {
+ return has_touch_screen;
}
void RecoveryUI::FlushKeys() {
- pthread_mutex_lock(&key_queue_mutex);
- key_queue_len = 0;
- pthread_mutex_unlock(&key_queue_mutex);
+ pthread_mutex_lock(&key_queue_mutex);
+ key_queue_len = 0;
+ pthread_mutex_unlock(&key_queue_mutex);
}
RecoveryUI::KeyAction RecoveryUI::CheckKey(int key, bool is_long_press) {
+ pthread_mutex_lock(&key_queue_mutex);
+ key_long_press = false;
+ pthread_mutex_unlock(&key_queue_mutex);
+
+ // If we have power and volume up keys, that chord is the signal to toggle the text display.
+ if (HasThreeButtons() || (HasPowerKey() && HasTouchScreen() && touch_screen_allowed_)) {
+ if ((key == KEY_VOLUMEUP || key == KEY_UP) && IsKeyPressed(KEY_POWER)) {
+ return TOGGLE;
+ }
+ } else {
+ // Otherwise long press of any button toggles to the text display,
+ // and there's no way to toggle back (but that's pretty useless anyway).
+ if (is_long_press && !IsTextVisible()) {
+ return TOGGLE;
+ }
+
+ // Also, for button-limited devices, a long press is translated to KEY_ENTER.
+ if (is_long_press && IsTextVisible()) {
+ EnqueueKey(KEY_ENTER);
+ return IGNORE;
+ }
+ }
+
+ // Press power seven times in a row to reboot.
+ if (key == KEY_POWER) {
pthread_mutex_lock(&key_queue_mutex);
- key_long_press = false;
+ bool reboot_enabled = enable_reboot;
pthread_mutex_unlock(&key_queue_mutex);
- // If we have power and volume up keys, that chord is the signal to toggle the text display.
- if (HasThreeButtons()) {
- if (key == KEY_VOLUMEUP && IsKeyPressed(KEY_POWER)) {
- return TOGGLE;
- }
- } else {
- // Otherwise long press of any button toggles to the text display,
- // and there's no way to toggle back (but that's pretty useless anyway).
- if (is_long_press && !IsTextVisible()) {
- return TOGGLE;
- }
-
- // Also, for button-limited devices, a long press is translated to KEY_ENTER.
- if (is_long_press && IsTextVisible()) {
- EnqueueKey(KEY_ENTER);
- return IGNORE;
- }
+ if (reboot_enabled) {
+ ++consecutive_power_keys;
+ if (consecutive_power_keys >= 7) {
+ return REBOOT;
+ }
}
+ } else {
+ consecutive_power_keys = 0;
+ }
- // Press power seven times in a row to reboot.
- if (key == KEY_POWER) {
- pthread_mutex_lock(&key_queue_mutex);
- bool reboot_enabled = enable_reboot;
- pthread_mutex_unlock(&key_queue_mutex);
-
- if (reboot_enabled) {
- ++consecutive_power_keys;
- if (consecutive_power_keys >= 7) {
- return REBOOT;
- }
- }
- } else {
- consecutive_power_keys = 0;
- }
-
- last_key = key;
- return (IsTextVisible() || screensaver_state_ == ScreensaverState::OFF) ? ENQUEUE : IGNORE;
+ last_key = key;
+ return (IsTextVisible() || screensaver_state_ == ScreensaverState::OFF) ? ENQUEUE : IGNORE;
}
void RecoveryUI::KeyLongPress(int) {
}
void RecoveryUI::SetEnableReboot(bool enabled) {
- pthread_mutex_lock(&key_queue_mutex);
- enable_reboot = enabled;
- pthread_mutex_unlock(&key_queue_mutex);
+ pthread_mutex_lock(&key_queue_mutex);
+ enable_reboot = enabled;
+ pthread_mutex_unlock(&key_queue_mutex);
}
void RecoveryUI::SetLocale(const std::string& new_locale) {
diff --git a/ui.h b/ui.h
index 823eb65..3d9afec 100644
--- a/ui.h
+++ b/ui.h
@@ -25,163 +25,180 @@
// Abstract class for controlling the user interface during recovery.
class RecoveryUI {
- public:
- RecoveryUI();
+ public:
+ RecoveryUI();
- virtual ~RecoveryUI() { }
+ virtual ~RecoveryUI() {}
- // 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);
+ // Initializes 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;
+ // Shows a stage indicator. Called immediately after Init().
+ virtual void SetStage(int current, int max) = 0;
- // Set the overall recovery state ("background image").
- enum Icon { NONE, INSTALLING_UPDATE, ERASING, NO_COMMAND, ERROR };
- virtual void SetBackground(Icon icon) = 0;
- virtual void SetSystemUpdateText(bool security_update) = 0;
+ // Sets the overall recovery state ("background image").
+ enum Icon { NONE, INSTALLING_UPDATE, ERASING, NO_COMMAND, ERROR };
+ virtual void SetBackground(Icon icon) = 0;
+ virtual void SetSystemUpdateText(bool security_update) = 0;
- // --- progress indicator ---
- enum ProgressType { EMPTY, INDETERMINATE, DETERMINATE };
- virtual void SetProgressType(ProgressType determinate) = 0;
+ // --- progress indicator ---
+ enum ProgressType { EMPTY, INDETERMINATE, DETERMINATE };
+ virtual void SetProgressType(ProgressType determinate) = 0;
- // Show a progress bar and define the scope of the next operation:
- // portion - fraction of the progress bar the next operation will use
- // seconds - expected time interval (progress bar moves at this minimum rate)
- virtual void ShowProgress(float portion, float seconds) = 0;
+ // Shows a progress bar and define the scope of the next operation:
+ // portion - fraction of the progress bar the next operation will use
+ // seconds - expected time interval (progress bar moves at this minimum rate)
+ virtual void ShowProgress(float portion, float seconds) = 0;
- // Set progress bar position (0.0 - 1.0 within the scope defined
- // by the last call to ShowProgress).
- virtual void SetProgress(float fraction) = 0;
+ // Sets progress bar position (0.0 - 1.0 within the scope defined by the last call to
+ // ShowProgress).
+ virtual void SetProgress(float fraction) = 0;
- // --- text log ---
+ // --- text log ---
- virtual void ShowText(bool visible) = 0;
+ virtual void ShowText(bool visible) = 0;
- virtual bool IsTextVisible() = 0;
+ virtual bool IsTextVisible() = 0;
- virtual bool WasTextEverVisible() = 0;
+ virtual bool WasTextEverVisible() = 0;
- // Write a message to the on-screen log (shown if the user has
- // toggled on the text display). Print() will also dump the message
- // to stdout / log file, while PrintOnScreenOnly() not.
- virtual void Print(const char* fmt, ...) __printflike(2, 3) = 0;
- virtual void PrintOnScreenOnly(const char* fmt, ...) __printflike(2, 3) = 0;
+ // Writes a message to the on-screen log (shown if the user has toggled on the text display).
+ // Print() will also dump the message to stdout / log file, while PrintOnScreenOnly() not.
+ virtual void Print(const char* fmt, ...) __printflike(2, 3) = 0;
+ virtual void PrintOnScreenOnly(const char* fmt, ...) __printflike(2, 3) = 0;
- virtual void ShowFile(const char* filename) = 0;
+ virtual void ShowFile(const char* filename) = 0;
- // --- key handling ---
+ // --- key handling ---
- // Wait for a key and return it. May return -1 after timeout.
- virtual int WaitKey();
+ // Waits for a key and return it. May return -1 after timeout.
+ virtual int WaitKey();
- virtual bool IsKeyPressed(int key);
- virtual bool IsLongPress();
+ virtual bool IsKeyPressed(int key);
+ virtual bool IsLongPress();
- // Returns true if you have the volume up/down and power trio typical
- // of phones and tablets, false otherwise.
- virtual bool HasThreeButtons();
+ // Returns true if you have the volume up/down and power trio typical of phones and tablets, false
+ // otherwise.
+ virtual bool HasThreeButtons();
- // Erase any queued-up keys.
- virtual void FlushKeys();
+ // Returns true if it has a power key.
+ virtual bool HasPowerKey() const;
- // Called on each key press, even while operations are in progress.
- // Return value indicates whether an immediate operation should be
- // triggered (toggling the display, rebooting the device), or if
- // the key should be enqueued for use by the main thread.
- enum KeyAction { ENQUEUE, TOGGLE, REBOOT, IGNORE };
- virtual KeyAction CheckKey(int key, bool is_long_press);
+ // Returns true if it supports touch inputs.
+ virtual bool HasTouchScreen() const;
- // Called when a key is held down long enough to have been a
- // long-press (but before the key is released). This means that
- // if the key is eventually registered (released without any other
- // keys being pressed in the meantime), CheckKey will be called with
- // 'is_long_press' true.
- virtual void KeyLongPress(int key);
+ // Erases any queued-up keys.
+ virtual void FlushKeys();
- // Normally in recovery there's a key sequence that triggers
- // immediate reboot of the device, regardless of what recovery is
- // doing (with the default CheckKey implementation, it's pressing
- // the power button 7 times in row). Call this to enable or
- // disable that feature. It is enabled by default.
- virtual void SetEnableReboot(bool enabled);
+ // Called on each key press, even while operations are in progress. Return value indicates whether
+ // an immediate operation should be triggered (toggling the display, rebooting the device), or if
+ // the key should be enqueued for use by the main thread.
+ enum KeyAction { ENQUEUE, TOGGLE, REBOOT, IGNORE };
+ virtual KeyAction CheckKey(int key, bool is_long_press);
- // --- menu display ---
+ // Called when a key is held down long enough to have been a long-press (but before the key is
+ // released). This means that if the key is eventually registered (released without any other keys
+ // being pressed in the meantime), CheckKey will be called with 'is_long_press' true.
+ virtual void KeyLongPress(int key);
- // Display some header text followed by a menu of items, which appears
- // at the top of the screen (in place of any scrolling ui_print()
- // output, if necessary).
- virtual void StartMenu(const char* const * headers, const char* const * items,
- int initial_selection) = 0;
+ // Normally in recovery there's a key sequence that triggers immediate reboot of the device,
+ // regardless of what recovery is doing (with the default CheckKey implementation, it's pressing
+ // the power button 7 times in row). Call this to enable or disable that feature. It is enabled by
+ // default.
+ virtual void SetEnableReboot(bool enabled);
- // Set the menu highlight to the given index, wrapping if necessary.
- // Returns the actual item selected.
- virtual int SelectMenu(int sel) = 0;
+ // --- menu display ---
- // End menu mode, resetting the text overlay so that ui_print()
- // statements will be displayed.
- virtual void EndMenu() = 0;
+ // Display some header text followed by a menu of items, which appears at the top of the screen
+ // (in place of any scrolling ui_print() output, if necessary).
+ virtual void StartMenu(const char* const* headers, const char* const* items,
+ int initial_selection) = 0;
- protected:
- void EnqueueKey(int key_code);
+ // Sets the menu highlight to the given index, wrapping if necessary. Returns the actual item
+ // selected.
+ virtual int SelectMenu(int sel) = 0;
- // The locale that's used to show the rendered texts.
- std::string locale_;
- bool rtl_locale_;
+ // Ends menu mode, resetting the text overlay so that ui_print() statements will be displayed.
+ virtual void EndMenu() = 0;
- // The normal and dimmed brightness percentages (default: 50 and 25, which means 50% and 25%
- // of the max_brightness). Because the absolute values may vary across devices. These two
- // values can be configured via subclassing. Setting brightness_normal_ to 0 to disable
- // screensaver.
- unsigned int brightness_normal_;
- unsigned int brightness_dimmed_;
+ protected:
+ void EnqueueKey(int key_code);
- private:
- // Key event input queue
- pthread_mutex_t key_queue_mutex;
- pthread_cond_t key_queue_cond;
- int key_queue[256], key_queue_len;
- char key_pressed[KEY_MAX + 1]; // under key_queue_mutex
- int key_last_down; // under key_queue_mutex
- bool key_long_press; // under key_queue_mutex
- int key_down_count; // under key_queue_mutex
- bool enable_reboot; // under key_queue_mutex
- int rel_sum;
+ // The locale that's used to show the rendered texts.
+ std::string locale_;
+ bool rtl_locale_;
- int consecutive_power_keys;
- int last_key;
+ // The normal and dimmed brightness percentages (default: 50 and 25, which means 50% and 25% of
+ // the max_brightness). Because the absolute values may vary across devices. These two values can
+ // be configured via subclassing. Setting brightness_normal_ to 0 to disable screensaver.
+ unsigned int brightness_normal_;
+ unsigned int brightness_dimmed_;
- bool has_power_key;
- bool has_up_key;
- bool has_down_key;
+ // Whether we should listen for touch inputs (default: false).
+ bool touch_screen_allowed_;
- struct key_timer_t {
- RecoveryUI* ui;
- int key_code;
- int count;
- };
+ private:
+ // The sensitivity when detecting a swipe.
+ const int kTouchLowThreshold;
+ const int kTouchHighThreshold;
- pthread_t input_thread_;
+ // Key event input queue
+ pthread_mutex_t key_queue_mutex;
+ pthread_cond_t key_queue_cond;
+ int key_queue[256], key_queue_len;
+ char key_pressed[KEY_MAX + 1]; // under key_queue_mutex
+ int key_last_down; // under key_queue_mutex
+ bool key_long_press; // under key_queue_mutex
+ int key_down_count; // under key_queue_mutex
+ bool enable_reboot; // under key_queue_mutex
+ int rel_sum;
- void OnKeyDetected(int key_code);
- int OnInputEvent(int fd, uint32_t epevents);
- void ProcessKey(int key_code, int updown);
+ int consecutive_power_keys;
+ int last_key;
- bool IsUsbConnected();
+ bool has_power_key;
+ bool has_up_key;
+ bool has_down_key;
+ bool has_touch_screen;
- static void* time_key_helper(void* cookie);
- void time_key(int key_code, int count);
+ // Touch event related variables. See the comments in RecoveryUI::OnInputEvent().
+ int touch_slot_;
+ int touch_X_;
+ int touch_Y_;
+ int touch_start_X_;
+ int touch_start_Y_;
+ bool touch_finger_down_;
+ bool touch_swiping_;
+ bool is_bootreason_recovery_ui_;
- void SetLocale(const std::string&);
+ struct key_timer_t {
+ RecoveryUI* ui;
+ int key_code;
+ int count;
+ };
- enum class ScreensaverState { DISABLED, NORMAL, DIMMED, OFF };
- ScreensaverState screensaver_state_;
- // The following two contain the absolute values computed from brightness_normal_ and
- // brightness_dimmed_ respectively.
- unsigned int brightness_normal_value_;
- unsigned int brightness_dimmed_value_;
- bool InitScreensaver();
+ pthread_t input_thread_;
+
+ void OnKeyDetected(int key_code);
+ void OnTouchDetected(int dx, int dy);
+ int OnInputEvent(int fd, uint32_t epevents);
+ void ProcessKey(int key_code, int updown);
+
+ bool IsUsbConnected();
+
+ static void* time_key_helper(void* cookie);
+ void time_key(int key_code, int count);
+
+ void SetLocale(const std::string&);
+
+ enum class ScreensaverState { DISABLED, NORMAL, DIMMED, OFF };
+ ScreensaverState screensaver_state_;
+ // The following two contain the absolute values computed from brightness_normal_ and
+ // brightness_dimmed_ respectively.
+ unsigned int brightness_normal_value_;
+ unsigned int brightness_dimmed_value_;
+ bool InitScreensaver();
};
#endif // RECOVERY_UI_H
diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp
index 48242a5..ba7b7ae 100644
--- a/update_verifier/update_verifier.cpp
+++ b/update_verifier/update_verifier.cpp
@@ -45,6 +45,7 @@
#include <unistd.h>
#include <algorithm>
+#include <future>
#include <string>
#include <vector>
@@ -123,11 +124,6 @@
LOG(ERROR) << "Failed to find dm block device for " << partition;
return false;
}
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY)));
- if (fd.get() == -1) {
- PLOG(ERROR) << "Error reading " << dm_block_device << " for partition " << partition;
- return false;
- }
// For block range string, first integer 'count' equals 2 * total number of valid ranges,
// followed by 'count' number comma separated integers. Every two integers reprensent a
@@ -141,74 +137,113 @@
LOG(ERROR) << "Error in parsing range string.";
return false;
}
+ range_count /= 2;
- size_t blk_count = 0;
- for (size_t i = 1; i < ranges.size(); i += 2) {
- unsigned int range_start, range_end;
- bool parse_status = android::base::ParseUint(ranges[i], &range_start);
- parse_status = parse_status && android::base::ParseUint(ranges[i + 1], &range_end);
- if (!parse_status || range_start >= range_end) {
- LOG(ERROR) << "Invalid range pair " << ranges[i] << ", " << ranges[i + 1];
- return false;
- }
+ std::vector<std::future<bool>> threads;
+ size_t thread_num = std::thread::hardware_concurrency() ?: 4;
+ thread_num = std::min(thread_num, range_count);
+ size_t group_range_count = (range_count + thread_num - 1) / thread_num;
- static constexpr size_t BLOCKSIZE = 4096;
- if (lseek64(fd.get(), static_cast<off64_t>(range_start) * BLOCKSIZE, SEEK_SET) == -1) {
- PLOG(ERROR) << "lseek to " << range_start << " failed";
- return false;
- }
-
- size_t remain = (range_end - range_start) * BLOCKSIZE;
- while (remain > 0) {
- size_t to_read = std::min(remain, 1024 * BLOCKSIZE);
- std::vector<uint8_t> buf(to_read);
- if (!android::base::ReadFully(fd.get(), buf.data(), to_read)) {
- PLOG(ERROR) << "Failed to read blocks " << range_start << " to " << range_end;
+ for (size_t t = 0; t < thread_num; t++) {
+ auto thread_func = [t, group_range_count, &dm_block_device, &ranges, &partition]() {
+ size_t blk_count = 0;
+ static constexpr size_t kBlockSize = 4096;
+ std::vector<uint8_t> buf(1024 * kBlockSize);
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY)));
+ if (fd.get() == -1) {
+ PLOG(ERROR) << "Error reading " << dm_block_device << " for partition " << partition;
return false;
}
- remain -= to_read;
- }
- blk_count += (range_end - range_start);
+
+ for (size_t i = group_range_count * 2 * t + 1;
+ i < std::min(group_range_count * 2 * (t + 1) + 1, ranges.size()); i += 2) {
+ unsigned int range_start, range_end;
+ bool parse_status = android::base::ParseUint(ranges[i], &range_start);
+ parse_status = parse_status && android::base::ParseUint(ranges[i + 1], &range_end);
+ if (!parse_status || range_start >= range_end) {
+ LOG(ERROR) << "Invalid range pair " << ranges[i] << ", " << ranges[i + 1];
+ return false;
+ }
+
+ if (lseek64(fd.get(), static_cast<off64_t>(range_start) * kBlockSize, SEEK_SET) == -1) {
+ PLOG(ERROR) << "lseek to " << range_start << " failed";
+ return false;
+ }
+
+ size_t remain = (range_end - range_start) * kBlockSize;
+ while (remain > 0) {
+ size_t to_read = std::min(remain, 1024 * kBlockSize);
+ if (!android::base::ReadFully(fd.get(), buf.data(), to_read)) {
+ PLOG(ERROR) << "Failed to read blocks " << range_start << " to " << range_end;
+ return false;
+ }
+ remain -= to_read;
+ }
+ blk_count += (range_end - range_start);
+ }
+ LOG(INFO) << "Finished reading " << blk_count << " blocks on " << dm_block_device;
+ return true;
+ };
+
+ threads.emplace_back(std::async(std::launch::async, thread_func));
}
- LOG(INFO) << "Finished reading " << blk_count << " blocks on " << dm_block_device;
- return true;
+ bool ret = true;
+ for (auto& t : threads) {
+ ret = t.get() && ret;
+ }
+ LOG(INFO) << "Finished reading blocks on " << dm_block_device << " with " << thread_num
+ << " threads.";
+ return ret;
}
+// Returns true to indicate a passing verification (or the error should be ignored); Otherwise
+// returns false on fatal errors, where we should reject the current boot and trigger a fallback.
+// Note that update_verifier should be backward compatible to not reject care_map.txt from old
+// releases, which could otherwise fail to boot into the new release. For example, we've changed
+// the care_map format between N and O. An O update_verifier would fail to work with N
+// care_map.txt. This could be a result of sideloading an O OTA while the device having a pending N
+// update.
bool verify_image(const std::string& care_map_name) {
- android::base::unique_fd care_map_fd(TEMP_FAILURE_RETRY(open(care_map_name.c_str(), O_RDONLY)));
- // If the device is flashed before the current boot, it may not have care_map.txt
- // in /data/ota_package. To allow the device to continue booting in this situation,
- // we should print a warning and skip the block verification.
- if (care_map_fd.get() == -1) {
- PLOG(WARNING) << "Failed to open " << care_map_name;
- return true;
- }
- // Care map file has four lines (two lines if vendor partition is not present):
- // First line has the block partition name (system/vendor).
- // Second line holds all ranges of blocks to verify.
- // The next two lines have the same format but for vendor partition.
- std::string file_content;
- if (!android::base::ReadFdToString(care_map_fd.get(), &file_content)) {
- LOG(ERROR) << "Error reading care map contents to string.";
- return false;
- }
-
- std::vector<std::string> lines;
- lines = android::base::Split(android::base::Trim(file_content), "\n");
- if (lines.size() != 2 && lines.size() != 4) {
- LOG(ERROR) << "Invalid lines in care_map: found " << lines.size()
- << " lines, expecting 2 or 4 lines.";
- return false;
- }
-
- for (size_t i = 0; i < lines.size(); i += 2) {
- if (!read_blocks(lines[i], lines[i+1])) {
- return false;
- }
- }
-
+ android::base::unique_fd care_map_fd(TEMP_FAILURE_RETRY(open(care_map_name.c_str(), O_RDONLY)));
+ // If the device is flashed before the current boot, it may not have care_map.txt
+ // in /data/ota_package. To allow the device to continue booting in this situation,
+ // we should print a warning and skip the block verification.
+ if (care_map_fd.get() == -1) {
+ PLOG(WARNING) << "Failed to open " << care_map_name;
return true;
+ }
+ // Care map file has four lines (two lines if vendor partition is not present):
+ // First line has the block partition name (system/vendor).
+ // Second line holds all ranges of blocks to verify.
+ // The next two lines have the same format but for vendor partition.
+ std::string file_content;
+ if (!android::base::ReadFdToString(care_map_fd.get(), &file_content)) {
+ LOG(ERROR) << "Error reading care map contents to string.";
+ return false;
+ }
+
+ std::vector<std::string> lines;
+ lines = android::base::Split(android::base::Trim(file_content), "\n");
+ if (lines.size() != 2 && lines.size() != 4) {
+ LOG(ERROR) << "Invalid lines in care_map: found " << lines.size()
+ << " lines, expecting 2 or 4 lines.";
+ return false;
+ }
+
+ for (size_t i = 0; i < lines.size(); i += 2) {
+ // We're seeing an N care_map.txt. Skip the verification since it's not compatible with O
+ // update_verifier (the last few metadata blocks can't be read via device mapper).
+ if (android::base::StartsWith(lines[i], "/dev/block/")) {
+ LOG(WARNING) << "Found legacy care_map.txt; skipped.";
+ return true;
+ }
+ if (!read_blocks(lines[i], lines[i+1])) {
+ return false;
+ }
+ }
+
+ return true;
}
static int reboot_device() {
diff --git a/updater/Android.mk b/updater/Android.mk
index a113fe8..86dc48e 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -47,6 +47,7 @@
libcrypto_utils \
libcutils \
libtune2fs \
+ libbrotli \
$(tune2fs_static_libraries)
# libupdater (static library)
diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp
index df366b0..a0b9ad2 100644
--- a/updater/blockimg.cpp
+++ b/updater/blockimg.cpp
@@ -44,6 +44,7 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <applypatch/applypatch.h>
+#include <brotli/decode.h>
#include <openssl/sha.h>
#include <private/android_filesystem_config.h>
#include <ziparchive/zip_archive.h>
@@ -149,7 +150,11 @@
class RangeSinkWriter {
public:
RangeSinkWriter(int fd, const RangeSet& tgt)
- : fd_(fd), tgt_(tgt), next_range_(0), current_range_left_(0), bytes_written_(0) {
+ : fd_(fd),
+ tgt_(tgt),
+ next_range_(0),
+ current_range_left_(0),
+ bytes_written_(0) {
CHECK_NE(tgt.size(), static_cast<size_t>(0));
};
@@ -157,6 +162,11 @@
return next_range_ == tgt_.size() && current_range_left_ == 0;
}
+ size_t AvailableSpace() const {
+ return tgt_.blocks() * BLOCKSIZE - bytes_written_;
+ }
+
+ // Return number of bytes written; and 0 indicates a writing failure.
size_t Write(const uint8_t* data, size_t size) {
if (Finished()) {
LOG(ERROR) << "range sink write overrun; can't write " << size << " bytes";
@@ -166,23 +176,8 @@
size_t written = 0;
while (size > 0) {
// Move to the next range as needed.
- if (current_range_left_ == 0) {
- if (next_range_ < tgt_.size()) {
- const Range& range = tgt_[next_range_];
- off64_t offset = static_cast<off64_t>(range.first) * BLOCKSIZE;
- current_range_left_ = (range.second - range.first) * BLOCKSIZE;
- next_range_++;
- if (!discard_blocks(fd_, offset, current_range_left_)) {
- break;
- }
-
- if (!check_lseek(fd_, offset, SEEK_SET)) {
- break;
- }
- } else {
- // We can't write any more; return how many bytes have been written so far.
- break;
- }
+ if (!SeekToOutputRange()) {
+ break;
}
size_t write_now = size;
@@ -210,9 +205,35 @@
}
private:
- // The input data.
+ // Set up the output cursor, move to next range if needed.
+ bool SeekToOutputRange() {
+ // We haven't finished the current range yet.
+ if (current_range_left_ != 0) {
+ return true;
+ }
+ // We can't write any more; let the write function return how many bytes have been written
+ // so far.
+ if (next_range_ >= tgt_.size()) {
+ return false;
+ }
+
+ const Range& range = tgt_[next_range_];
+ off64_t offset = static_cast<off64_t>(range.first) * BLOCKSIZE;
+ current_range_left_ = (range.second - range.first) * BLOCKSIZE;
+ next_range_++;
+
+ if (!discard_blocks(fd_, offset, current_range_left_)) {
+ return false;
+ }
+ if (!check_lseek(fd_, offset, SEEK_SET)) {
+ return false;
+ }
+ return true;
+ }
+
+ // The output file descriptor.
int fd_;
- // The destination for the data.
+ // The destination ranges for the data.
const RangeSet& tgt_;
// The next range that we should write to.
size_t next_range_;
@@ -243,8 +264,10 @@
struct NewThreadInfo {
ZipArchiveHandle za;
ZipEntry entry;
+ bool brotli_compressed;
- RangeSinkWriter* writer;
+ std::unique_ptr<RangeSinkWriter> writer;
+ BrotliDecoderState* brotli_decoder_state;
bool receiver_available;
pthread_mutex_t mu;
@@ -264,9 +287,73 @@
// At this point nti->writer is set, and we own it. The main thread is waiting for it to
// disappear from nti.
- size_t written = nti->writer->Write(data, size);
- data += written;
- size -= written;
+ size_t write_now = std::min(size, nti->writer->AvailableSpace());
+ if (nti->writer->Write(data, write_now) != write_now) {
+ LOG(ERROR) << "Failed to write " << write_now << " bytes.";
+ return false;
+ }
+
+ data += write_now;
+ size -= write_now;
+
+ if (nti->writer->Finished()) {
+ // We have written all the bytes desired by this writer.
+
+ pthread_mutex_lock(&nti->mu);
+ nti->writer = nullptr;
+ pthread_cond_broadcast(&nti->cv);
+ pthread_mutex_unlock(&nti->mu);
+ }
+ }
+
+ return true;
+}
+
+static bool receive_brotli_new_data(const uint8_t* data, size_t size, void* cookie) {
+ NewThreadInfo* nti = static_cast<NewThreadInfo*>(cookie);
+
+ while (size > 0 || BrotliDecoderHasMoreOutput(nti->brotli_decoder_state)) {
+ // Wait for nti->writer to be non-null, indicating some of this data is wanted.
+ pthread_mutex_lock(&nti->mu);
+ while (nti->writer == nullptr) {
+ pthread_cond_wait(&nti->cv, &nti->mu);
+ }
+ pthread_mutex_unlock(&nti->mu);
+
+ // At this point nti->writer is set, and we own it. The main thread is waiting for it to
+ // disappear from nti.
+
+ size_t buffer_size = std::min<size_t>(32768, nti->writer->AvailableSpace());
+ if (buffer_size == 0) {
+ LOG(ERROR) << "No space left in output range";
+ return false;
+ }
+ uint8_t buffer[buffer_size];
+ size_t available_in = size;
+ size_t available_out = buffer_size;
+ uint8_t* next_out = buffer;
+
+ // The brotli decoder will update |data|, |available_in|, |next_out| and |available_out|.
+ BrotliDecoderResult result = BrotliDecoderDecompressStream(
+ nti->brotli_decoder_state, &available_in, &data, &available_out, &next_out, nullptr);
+
+ if (result == BROTLI_DECODER_RESULT_ERROR) {
+ LOG(ERROR) << "Decompression failed with "
+ << BrotliDecoderErrorString(BrotliDecoderGetErrorCode(nti->brotli_decoder_state));
+ return false;
+ }
+
+ LOG(DEBUG) << "bytes to write: " << buffer_size - available_out << ", bytes consumed "
+ << size - available_in << ", decoder status " << result;
+
+ size_t write_now = buffer_size - available_out;
+ if (nti->writer->Write(buffer, write_now) != write_now) {
+ LOG(ERROR) << "Failed to write " << write_now << " bytes.";
+ return false;
+ }
+
+ // Update the remaining size. The input data ptr is already updated by brotli decoder function.
+ size = available_in;
if (nti->writer->Finished()) {
// We have written all the bytes desired by this writer.
@@ -283,8 +370,11 @@
static void* unzip_new_data(void* cookie) {
NewThreadInfo* nti = static_cast<NewThreadInfo*>(cookie);
- ProcessZipEntryContents(nti->za, &nti->entry, receive_new_data, nti);
-
+ if (nti->brotli_compressed) {
+ ProcessZipEntryContents(nti->za, &nti->entry, receive_brotli_new_data, nti);
+ } else {
+ ProcessZipEntryContents(nti->za, &nti->entry, receive_new_data, nti);
+ }
pthread_mutex_lock(&nti->mu);
nti->receiver_available = false;
if (nti->writer != nullptr) {
@@ -1142,9 +1232,8 @@
if (params.canwrite) {
LOG(INFO) << " writing " << tgt.blocks() << " blocks of new data";
- RangeSinkWriter writer(params.fd, tgt);
pthread_mutex_lock(¶ms.nti.mu);
- params.nti.writer = &writer;
+ params.nti.writer = std::make_unique<RangeSinkWriter>(params.fd, tgt);
pthread_cond_broadcast(¶ms.nti.cv);
while (params.nti.writer != nullptr) {
@@ -1384,6 +1473,11 @@
if (params.canwrite) {
params.nti.za = za;
params.nti.entry = new_entry;
+ params.nti.brotli_compressed = android::base::EndsWith(new_data_fn->data, ".br");
+ if (params.nti.brotli_compressed) {
+ // Initialize brotli decoder state.
+ params.nti.brotli_decoder_state = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
+ }
params.nti.receiver_available = true;
pthread_mutex_init(¶ms.nti.mu, nullptr);
@@ -1526,6 +1620,10 @@
}
// params.fd will be automatically closed because it's a unique_fd.
+ if (params.nti.brotli_decoder_state != nullptr) {
+ BrotliDecoderDestroyInstance(params.nti.brotli_decoder_state);
+ }
+
// Only delete the stash if the update cannot be resumed, or it's a verification run and we
// created the stash.
if (params.isunresumable || (!params.canwrite && params.createdstash)) {
diff --git a/updater/install.cpp b/updater/install.cpp
index c9a3a07..bfe91e7 100644
--- a/updater/install.cpp
+++ b/updater/install.cpp
@@ -322,8 +322,7 @@
return StringValue(location);
}
- const char* e2fsdroid_argv[] = { "/sbin/e2fsdroid_static", "-e", "-S",
- "/file_contexts", "-a", mount_point.c_str(),
+ const char* e2fsdroid_argv[] = { "/sbin/e2fsdroid_static", "-e", "-a", mount_point.c_str(),
location.c_str(), nullptr };
status = exec_cmd(e2fsdroid_argv[0], const_cast<char**>(e2fsdroid_argv));
if (status != 0) {
diff --git a/verifier.cpp b/verifier.cpp
index 2ef9c4c..18437fb 100644
--- a/verifier.cpp
+++ b/verifier.cpp
@@ -474,81 +474,80 @@
// Otherwise returns false if the file failed to parse, or if it contains zero
// keys. The contents in certs would be unspecified on failure.
bool load_keys(const char* filename, std::vector<Certificate>& certs) {
- std::unique_ptr<FILE, decltype(&fclose)> f(fopen(filename, "r"), fclose);
- if (!f) {
- PLOG(ERROR) << "error opening " << filename;
+ std::unique_ptr<FILE, decltype(&fclose)> f(fopen(filename, "re"), fclose);
+ if (!f) {
+ PLOG(ERROR) << "error opening " << filename;
+ return false;
+ }
+
+ while (true) {
+ certs.emplace_back(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr);
+ Certificate& cert = certs.back();
+ uint32_t exponent = 0;
+
+ char start_char;
+ if (fscanf(f.get(), " %c", &start_char) != 1) return false;
+ if (start_char == '{') {
+ // a version 1 key has no version specifier.
+ cert.key_type = Certificate::KEY_TYPE_RSA;
+ exponent = 3;
+ cert.hash_len = SHA_DIGEST_LENGTH;
+ } else if (start_char == 'v') {
+ int version;
+ if (fscanf(f.get(), "%d {", &version) != 1) return false;
+ switch (version) {
+ case 2:
+ cert.key_type = Certificate::KEY_TYPE_RSA;
+ exponent = 65537;
+ cert.hash_len = SHA_DIGEST_LENGTH;
+ break;
+ case 3:
+ cert.key_type = Certificate::KEY_TYPE_RSA;
+ exponent = 3;
+ cert.hash_len = SHA256_DIGEST_LENGTH;
+ break;
+ case 4:
+ cert.key_type = Certificate::KEY_TYPE_RSA;
+ exponent = 65537;
+ cert.hash_len = SHA256_DIGEST_LENGTH;
+ break;
+ case 5:
+ cert.key_type = Certificate::KEY_TYPE_EC;
+ cert.hash_len = SHA256_DIGEST_LENGTH;
+ break;
+ default:
+ return false;
+ }
+ }
+
+ if (cert.key_type == Certificate::KEY_TYPE_RSA) {
+ cert.rsa = parse_rsa_key(f.get(), exponent);
+ if (!cert.rsa) {
return false;
+ }
+
+ LOG(INFO) << "read key e=" << exponent << " hash=" << cert.hash_len;
+ } else if (cert.key_type == Certificate::KEY_TYPE_EC) {
+ cert.ec = parse_ec_key(f.get());
+ if (!cert.ec) {
+ return false;
+ }
+ } else {
+ LOG(ERROR) << "Unknown key type " << cert.key_type;
+ return false;
}
- while (true) {
- certs.emplace_back(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr);
- Certificate& cert = certs.back();
- uint32_t exponent = 0;
-
- char start_char;
- if (fscanf(f.get(), " %c", &start_char) != 1) return false;
- if (start_char == '{') {
- // a version 1 key has no version specifier.
- cert.key_type = Certificate::KEY_TYPE_RSA;
- exponent = 3;
- cert.hash_len = SHA_DIGEST_LENGTH;
- } else if (start_char == 'v') {
- int version;
- if (fscanf(f.get(), "%d {", &version) != 1) return false;
- switch (version) {
- case 2:
- cert.key_type = Certificate::KEY_TYPE_RSA;
- exponent = 65537;
- cert.hash_len = SHA_DIGEST_LENGTH;
- break;
- case 3:
- cert.key_type = Certificate::KEY_TYPE_RSA;
- exponent = 3;
- cert.hash_len = SHA256_DIGEST_LENGTH;
- break;
- case 4:
- cert.key_type = Certificate::KEY_TYPE_RSA;
- exponent = 65537;
- cert.hash_len = SHA256_DIGEST_LENGTH;
- break;
- case 5:
- cert.key_type = Certificate::KEY_TYPE_EC;
- cert.hash_len = SHA256_DIGEST_LENGTH;
- break;
- default:
- return false;
- }
- }
-
- if (cert.key_type == Certificate::KEY_TYPE_RSA) {
- cert.rsa = parse_rsa_key(f.get(), exponent);
- if (!cert.rsa) {
- return false;
- }
-
- LOG(INFO) << "read key e=" << exponent << " hash=" << cert.hash_len;
- } else if (cert.key_type == Certificate::KEY_TYPE_EC) {
- cert.ec = parse_ec_key(f.get());
- if (!cert.ec) {
- return false;
- }
- } else {
- LOG(ERROR) << "Unknown key type " << cert.key_type;
- return false;
- }
-
- // if the line ends in a comma, this file has more keys.
- int ch = fgetc(f.get());
- if (ch == ',') {
- // more keys to come.
- continue;
- } else if (ch == EOF) {
- break;
- } else {
- LOG(ERROR) << "unexpected character between keys";
- return false;
- }
+ // if the line ends in a comma, this file has more keys.
+ int ch = fgetc(f.get());
+ if (ch == ',') {
+ // more keys to come.
+ continue;
+ } else if (ch == EOF) {
+ break;
+ } else {
+ LOG(ERROR) << "unexpected character between keys";
+ return false;
}
-
- return true;
+ }
+ return true;
}
diff --git a/vr_ui.cpp b/vr_ui.cpp
index 8b8261e..1251672 100644
--- a/vr_ui.cpp
+++ b/vr_ui.cpp
@@ -27,9 +27,9 @@
return true;
}
-void VrRecoveryUI::DrawTextLine(int x, int* y, const char* line, bool bold) const {
+int VrRecoveryUI::DrawTextLine(int x, int y, const char* line, bool bold) const {
int mid_divide = gr_fb_width() / 2;
- gr_text(gr_sys_font(), x + kStereoOffset, *y, line, bold);
- gr_text(gr_sys_font(), x - kStereoOffset + mid_divide, *y, line, bold);
- *y += char_height_ + 4;
+ gr_text(gr_sys_font(), x + kStereoOffset, y, line, bold);
+ gr_text(gr_sys_font(), x - kStereoOffset + mid_divide, y, line, bold);
+ return char_height_ + 4;
}
diff --git a/vr_ui.h b/vr_ui.h
index 31ca4a6..d996c14 100644
--- a/vr_ui.h
+++ b/vr_ui.h
@@ -20,17 +20,17 @@
#include "screen_ui.h"
class VrRecoveryUI : public ScreenRecoveryUI {
- public:
- VrRecoveryUI();
+ public:
+ VrRecoveryUI();
- protected:
- // Pixel offsets to move drawing functions to visible range.
- // Can vary per device depending on screen size and lens distortion.
- const int kStereoOffset;
+ protected:
+ // Pixel offsets to move drawing functions to visible range.
+ // Can vary per device depending on screen size and lens distortion.
+ const int kStereoOffset;
- bool InitTextParams() override;
+ bool InitTextParams() override;
- void DrawTextLine(int x, int* y, const char* line, bool bold) const override;
+ int DrawTextLine(int x, int y, const char* line, bool bold) const override;
};
#endif // RECOVERY_VR_UI_H
diff --git a/wear_device.cpp b/wear_device.cpp
new file mode 100644
index 0000000..3268130
--- /dev/null
+++ b/wear_device.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2017 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 "device.h"
+#include "wear_ui.h"
+
+Device* make_device() {
+ return new Device(new WearRecoveryUI);
+}
+
diff --git a/wear_touch.cpp b/wear_touch.cpp
deleted file mode 100644
index e2ab44d..0000000
--- a/wear_touch.cpp
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * 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 <dirent.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-
-#include <android-base/logging.h>
-#include <linux/input.h>
-
-#include "wear_touch.h"
-
-#define DEVICE_PATH "/dev/input"
-
-WearSwipeDetector::WearSwipeDetector(int low, int high, OnSwipeCallback callback, void* cookie):
- mLowThreshold(low),
- mHighThreshold(high),
- mCallback(callback),
- mCookie(cookie),
- mCurrentSlot(-1) {
- pthread_create(&mThread, NULL, touch_thread, this);
-}
-
-WearSwipeDetector::~WearSwipeDetector() {
-}
-
-void WearSwipeDetector::detect(int dx, int dy) {
- enum SwipeDirection direction;
-
- if (abs(dy) < mLowThreshold && abs(dx) > mHighThreshold) {
- direction = dx < 0 ? LEFT : RIGHT;
- } else if (abs(dx) < mLowThreshold && abs(dy) > mHighThreshold) {
- direction = dy < 0 ? UP : DOWN;
- } else {
- LOG(DEBUG) << "Ignore " << dx << " " << dy;
- return;
- }
-
- LOG(DEBUG) << "Swipe direction=" << direction;
- mCallback(mCookie, direction);
-}
-
-void WearSwipeDetector::process(struct input_event *event) {
- if (mCurrentSlot < 0) {
- mCallback(mCookie, UP);
- mCurrentSlot = 0;
- }
-
- if (event->type == EV_ABS) {
- if (event->code == ABS_MT_SLOT)
- mCurrentSlot = event->value;
-
- // Ignore other fingers
- if (mCurrentSlot > 0) {
- return;
- }
-
- switch (event->code) {
- case ABS_MT_POSITION_X:
- mX = event->value;
- mFingerDown = true;
- break;
-
- case ABS_MT_POSITION_Y:
- mY = event->value;
- mFingerDown = true;
- break;
-
- case ABS_MT_TRACKING_ID:
- if (event->value < 0)
- mFingerDown = false;
- break;
- }
- } else if (event->type == EV_SYN) {
- if (event->code == SYN_REPORT) {
- if (mFingerDown && !mSwiping) {
- mStartX = mX;
- mStartY = mY;
- mSwiping = true;
- } else if (!mFingerDown && mSwiping) {
- mSwiping = false;
- detect(mX - mStartX, mY - mStartY);
- }
- }
- }
-}
-
-void WearSwipeDetector::run() {
- int fd = findDevice(DEVICE_PATH);
- if (fd < 0) {
- LOG(ERROR) << "no input devices found";
- return;
- }
-
- struct input_event event;
- while (read(fd, &event, sizeof(event)) == sizeof(event)) {
- process(&event);
- }
-
- close(fd);
-}
-
-void* WearSwipeDetector::touch_thread(void* cookie) {
- (static_cast<WearSwipeDetector*>(cookie))->run();
- return NULL;
-}
-
-#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)%8)))
-
-int WearSwipeDetector::openDevice(const char *device) {
- int fd = open(device, O_RDONLY);
- if (fd < 0) {
- PLOG(ERROR) << "could not open " << device;
- return false;
- }
-
- char name[80];
- name[sizeof(name) - 1] = '\0';
- if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
- PLOG(ERROR) << "could not get device name for " << device;
- name[0] = '\0';
- }
-
- uint8_t bits[512];
- memset(bits, 0, sizeof(bits));
- int ret = ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(bits)), bits);
- if (ret > 0) {
- if (test_bit(ABS_MT_POSITION_X, bits) && test_bit(ABS_MT_POSITION_Y, bits)) {
- LOG(DEBUG) << "Found " << device << " " << name;
- return fd;
- }
- }
-
- close(fd);
- return -1;
-}
-
-int WearSwipeDetector::findDevice(const char* path) {
- DIR* dir = opendir(path);
- if (dir == NULL) {
- PLOG(ERROR) << "Could not open directory " << path;
- return false;
- }
-
- struct dirent* entry;
- int ret = -1;
- while (ret < 0 && (entry = readdir(dir)) != NULL) {
- if (entry->d_name[0] == '.') continue;
-
- char device[PATH_MAX];
- device[PATH_MAX-1] = '\0';
- snprintf(device, PATH_MAX-1, "%s/%s", path, entry->d_name);
-
- ret = openDevice(device);
- }
-
- closedir(dir);
- return ret;
-}
-
diff --git a/wear_touch.h b/wear_touch.h
deleted file mode 100644
index 9a1d315..0000000
--- a/wear_touch.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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 __WEAR_TOUCH_H
-#define __WEAR_TOUCH_H
-
-#include <pthread.h>
-
-class WearSwipeDetector {
-
-public:
- enum SwipeDirection { UP, DOWN, RIGHT, LEFT };
- typedef void (*OnSwipeCallback)(void* cookie, enum SwipeDirection direction);
-
- WearSwipeDetector(int low, int high, OnSwipeCallback cb, void* cookie);
- ~WearSwipeDetector();
-
-private:
- void run();
- void process(struct input_event *event);
- void detect(int dx, int dy);
-
- pthread_t mThread;
- static void* touch_thread(void* cookie);
-
- int findDevice(const char* path);
- int openDevice(const char* device);
-
- int mLowThreshold;
- int mHighThreshold;
-
- OnSwipeCallback mCallback;
- void *mCookie;
-
- int mX;
- int mY;
- int mStartX;
- int mStartY;
-
- int mCurrentSlot;
- bool mFingerDown;
- bool mSwiping;
-};
-
-#endif // __WEAR_TOUCH_H
diff --git a/wear_ui.cpp b/wear_ui.cpp
index 6c02865..624116c 100644
--- a/wear_ui.cpp
+++ b/wear_ui.cpp
@@ -45,167 +45,164 @@
// Return the current time as a double (including fractions of a second).
static double now() {
- struct timeval tv;
- gettimeofday(&tv, NULL);
- return tv.tv_sec + tv.tv_usec / 1000000.0;
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec + tv.tv_usec / 1000000.0;
}
-WearRecoveryUI::WearRecoveryUI() :
- progress_bar_y(259),
- outer_height(0),
- outer_width(0),
- menu_unusable_rows(0) {
- intro_frames = 22;
- loop_frames = 60;
- animation_fps = 30;
+WearRecoveryUI::WearRecoveryUI()
+ : kProgressBarBaseline(RECOVERY_UI_PROGRESS_BAR_BASELINE),
+ kMenuUnusableRows(RECOVERY_UI_MENU_UNUSABLE_ROWS) {
+ // TODO: kMenuUnusableRows should be computed based on the lines in draw_screen_locked().
- for (size_t i = 0; i < 5; i++)
- backgroundIcon[i] = NULL;
+ // TODO: The following three variables are likely not needed. The first two are detected
+ // automatically in ScreenRecoveryUI::LoadAnimation(), based on the actual files seen on device.
+ intro_frames = 22;
+ loop_frames = 60;
- self = this;
+ touch_screen_allowed_ = true;
+
+ for (size_t i = 0; i < 5; i++) backgroundIcon[i] = NULL;
+
+ self = this;
}
-int WearRecoveryUI::GetProgressBaseline() {
- return progress_bar_y;
+int WearRecoveryUI::GetProgressBaseline() const {
+ return kProgressBarBaseline;
}
// Draw background frame on the screen. Does not flip pages.
// Should only be called with updateMutex locked.
// 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());
+void WearRecoveryUI::draw_background_locked() {
+ pagesIdentical = false;
+ gr_color(0, 0, 0, 255);
+ gr_fill(0, 0, gr_fb_width(), gr_fb_height());
- if (currentIcon != NONE) {
- GRSurface* surface;
- if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) {
- if (!intro_done) {
- surface = introFrames[current_frame];
- } else {
- surface = loopFrames[current_frame];
- }
- }
- else {
- surface = backgroundIcon[currentIcon];
- }
-
- int width = gr_get_width(surface);
- int height = gr_get_height(surface);
-
- int x = (gr_fb_width() - width) / 2;
- int y = (gr_fb_height() - height) / 2;
-
- gr_blit(surface, 0, 0, width, height, x, y);
+ if (currentIcon != NONE) {
+ GRSurface* surface;
+ if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) {
+ if (!intro_done) {
+ surface = introFrames[current_frame];
+ } else {
+ surface = loopFrames[current_frame];
+ }
+ } else {
+ surface = backgroundIcon[currentIcon];
}
+
+ int width = gr_get_width(surface);
+ int height = gr_get_height(surface);
+
+ int x = (gr_fb_width() - width) / 2;
+ int y = (gr_fb_height() - height) / 2;
+
+ gr_blit(surface, 0, 0, width, height, x, y);
+ }
}
-static const char* HEADERS[] = {
- "Swipe up/down to move.",
- "Swipe left/right to select.",
- "",
- NULL
+static const char* SWIPE_HELP[] = {
+ "Swipe up/down to move.",
+ "Swipe left/right to select.",
+ "",
+ NULL
};
// TODO merge drawing routines with screen_ui
-void WearRecoveryUI::draw_screen_locked()
-{
- char cur_selection_str[50];
+void WearRecoveryUI::draw_screen_locked() {
+ char cur_selection_str[50];
- draw_background_locked();
- if (!show_text) {
- draw_foreground_locked();
- } else {
- SetColor(TEXT_FILL);
- gr_fill(0, 0, gr_fb_width(), gr_fb_height());
+ draw_background_locked();
+ if (!show_text) {
+ draw_foreground_locked();
+ } else {
+ SetColor(TEXT_FILL);
+ gr_fill(0, 0, gr_fb_width(), gr_fb_height());
- int y = outer_height;
- int x = outer_width;
- if (show_menu) {
- std::string recovery_fingerprint =
- android::base::GetProperty("ro.bootimage.build.fingerprint", "");
- SetColor(HEADER);
- DrawTextLine(x + 4, &y, "Android Recovery", true);
- for (auto& chunk: android::base::Split(recovery_fingerprint, ":")) {
- DrawTextLine(x +4, &y, chunk.c_str(), false);
- }
+ int y = kMarginHeight;
+ int x = kMarginWidth;
+ if (show_menu) {
+ std::string recovery_fingerprint =
+ android::base::GetProperty("ro.bootimage.build.fingerprint", "");
+ SetColor(HEADER);
+ y += DrawTextLine(x + 4, y, "Android Recovery", true);
+ for (auto& chunk : android::base::Split(recovery_fingerprint, ":")) {
+ y += DrawTextLine(x + 4, y, chunk.c_str(), false);
+ }
- // This is actually the help strings.
- DrawTextLines(x + 4, &y, HEADERS);
- SetColor(HEADER);
- DrawTextLines(x + 4, &y, menu_headers_);
+ // This is actually the help strings.
+ y += DrawTextLines(x + 4, y, SWIPE_HELP);
+ SetColor(HEADER);
+ y += DrawTextLines(x + 4, y, menu_headers_);
- // Show the current menu item number in relation to total number if
- // items don't fit on the screen.
- if (menu_items > menu_end - menu_start) {
- sprintf(cur_selection_str, "Current item: %d/%d", menu_sel + 1, menu_items);
- gr_text(gr_sys_font(), x+4, y, cur_selection_str, 1);
- y += char_height_+4;
- }
+ // Show the current menu item number in relation to total number if
+ // items don't fit on the screen.
+ if (menu_items > menu_end - menu_start) {
+ sprintf(cur_selection_str, "Current item: %d/%d", menu_sel + 1, menu_items);
+ gr_text(gr_sys_font(), x + 4, y, cur_selection_str, 1);
+ y += char_height_ + 4;
+ }
- // Menu begins here
- SetColor(MENU);
+ // Menu begins here
+ SetColor(MENU);
- for (int i = menu_start; i < menu_end; ++i) {
-
- 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);
- // white text of selected item
- SetColor(MENU_SEL_FG);
- 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);
- }
- y += char_height_+4;
- }
- SetColor(MENU);
- y += 4;
- gr_fill(0, y, gr_fb_width(), y+2);
- y += 4;
+ for (int i = menu_start; i < menu_end; ++i) {
+ 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);
+ // white text of selected item
+ SetColor(MENU_SEL_FG);
+ if (menu_[i][0]) {
+ gr_text(gr_sys_font(), x + 4, y, menu_[i].c_str(), 1);
+ }
+ SetColor(MENU);
+ } else if (menu_[i][0]) {
+ gr_text(gr_sys_font(), x + 4, y, menu_[i].c_str(), 0);
}
-
- SetColor(LOG);
-
- // display from the bottom up, until we hit the top of the
- // screen, the bottom of the menu, or we've displayed the
- // entire text buffer.
- int ty;
- int row = (text_top_ + text_rows_ - 1) % text_rows_;
- size_t count = 0;
- for (int ty = gr_fb_height() - char_height_ - outer_height;
- ty > y + 2 && count < text_rows_;
- ty -= char_height_, ++count) {
- gr_text(gr_sys_font(), x+4, ty, text_[row], 0);
- --row;
- if (row < 0) row = text_rows_ - 1;
- }
+ y += char_height_ + 4;
+ }
+ SetColor(MENU);
+ y += 4;
+ gr_fill(0, y, gr_fb_width(), y + 2);
+ y += 4;
}
+
+ SetColor(LOG);
+
+ // display from the bottom up, until we hit the top of the
+ // screen, the bottom of the menu, or we've displayed the
+ // entire text buffer.
+ int ty;
+ int row = (text_top_ + text_rows_ - 1) % text_rows_;
+ size_t count = 0;
+ for (int ty = gr_fb_height() - char_height_ - kMarginHeight; ty > y + 2 && count < text_rows_;
+ ty -= char_height_, ++count) {
+ gr_text(gr_sys_font(), x + 4, ty, text_[row], 0);
+ --row;
+ if (row < 0) row = text_rows_ - 1;
+ }
+ }
}
// TODO merge drawing routines with screen_ui
void WearRecoveryUI::update_progress_locked() {
- draw_screen_locked();
- gr_flip();
+ draw_screen_locked();
+ gr_flip();
}
bool WearRecoveryUI::InitTextParams() {
- if (!ScreenRecoveryUI::InitTextParams()) {
- return false;
- }
+ if (!ScreenRecoveryUI::InitTextParams()) {
+ return false;
+ }
- text_cols_ = (gr_fb_width() - (outer_width * 2)) / char_width_;
+ text_cols_ = (gr_fb_width() - (kMarginWidth * 2)) / char_width_;
- if (text_rows_ > kMaxRows) text_rows_ = kMaxRows;
- if (text_cols_ > kMaxCols) text_cols_ = kMaxCols;
+ if (text_rows_ > kMaxRows) text_rows_ = kMaxRows;
+ if (text_cols_ > kMaxCols) text_cols_ = kMaxCols;
- visible_text_rows = (gr_fb_height() - (outer_height * 2)) / char_height_;
- return true;
+ visible_text_rows = (gr_fb_height() - (kMarginHeight * 2)) / char_height_;
+ return true;
}
bool WearRecoveryUI::Init(const std::string& locale) {
@@ -222,7 +219,8 @@
return true;
}
-void WearRecoveryUI::SetStage(int current, int max) {}
+void WearRecoveryUI::SetStage(int current, int max) {
+}
void WearRecoveryUI::Print(const char* fmt, ...) {
char buf[256];
@@ -252,165 +250,152 @@
pthread_mutex_unlock(&updateMutex);
}
-void WearRecoveryUI::StartMenu(const char* const * headers, const char* const * items,
+void WearRecoveryUI::StartMenu(const char* const* headers, const char* const* items,
int initial_selection) {
- pthread_mutex_lock(&updateMutex);
- if (text_rows_ > 0 && text_cols_ > 0) {
- menu_headers_ = headers;
- size_t i = 0;
- // "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.
- // Bug: 23752519
- for (; items[i] != nullptr; i++) {
- strncpy(menu_[i], items[i], text_cols_ - 1);
- menu_[i][text_cols_ - 1] = '\0';
- }
- menu_items = i;
- show_menu = true;
- menu_sel = initial_selection;
- menu_start = 0;
- menu_end = visible_text_rows - 1 - menu_unusable_rows;
- if (menu_items <= menu_end)
- menu_end = menu_items;
- update_screen_locked();
+ pthread_mutex_lock(&updateMutex);
+ if (text_rows_ > 0 && text_cols_ > 0) {
+ menu_headers_ = headers;
+ menu_.clear();
+ // "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.
+ // Bug: 23752519
+ for (size_t i = 0; items[i] != nullptr; i++) {
+ menu_.emplace_back(std::string(items[i], strnlen(items[i], text_cols_ - 1)));
}
- pthread_mutex_unlock(&updateMutex);
+ menu_items = static_cast<int>(menu_.size());
+ show_menu = true;
+ menu_sel = initial_selection;
+ menu_start = 0;
+ menu_end = visible_text_rows - 1 - kMenuUnusableRows;
+ if (menu_items <= menu_end) menu_end = menu_items;
+ update_screen_locked();
+ }
+ pthread_mutex_unlock(&updateMutex);
}
int WearRecoveryUI::SelectMenu(int sel) {
- int old_sel;
- pthread_mutex_lock(&updateMutex);
- if (show_menu) {
- old_sel = menu_sel;
- menu_sel = sel;
- if (menu_sel < 0) menu_sel = 0;
- if (menu_sel >= menu_items) menu_sel = menu_items-1;
- if (menu_sel < menu_start) {
- menu_start--;
- menu_end--;
- } else if (menu_sel >= menu_end && menu_sel < menu_items) {
- menu_end++;
- menu_start++;
- }
- sel = menu_sel;
- if (menu_sel != old_sel) update_screen_locked();
+ int old_sel;
+ pthread_mutex_lock(&updateMutex);
+ if (show_menu) {
+ old_sel = menu_sel;
+ menu_sel = sel;
+ if (menu_sel < 0) menu_sel = 0;
+ if (menu_sel >= menu_items) menu_sel = menu_items - 1;
+ if (menu_sel < menu_start) {
+ menu_start--;
+ menu_end--;
+ } else if (menu_sel >= menu_end && menu_sel < menu_items) {
+ menu_end++;
+ menu_start++;
}
- pthread_mutex_unlock(&updateMutex);
- return sel;
+ sel = menu_sel;
+ if (menu_sel != old_sel) update_screen_locked();
+ }
+ pthread_mutex_unlock(&updateMutex);
+ return sel;
}
void WearRecoveryUI::ShowFile(FILE* fp) {
- std::vector<off_t> offsets;
- offsets.push_back(ftello(fp));
- ClearText();
+ std::vector<off_t> offsets;
+ offsets.push_back(ftello(fp));
+ ClearText();
- struct stat sb;
- fstat(fileno(fp), &sb);
+ struct stat sb;
+ fstat(fileno(fp), &sb);
- bool show_prompt = false;
- while (true) {
- if (show_prompt) {
- Print("--(%d%% of %d bytes)--",
- static_cast<int>(100 * (double(ftello(fp)) / double(sb.st_size))),
- static_cast<int>(sb.st_size));
- Redraw();
- while (show_prompt) {
- show_prompt = false;
- int key = WaitKey();
- if (key == KEY_POWER || key == KEY_ENTER) {
- return;
- } else if (key == KEY_UP || key == KEY_VOLUMEUP) {
- if (offsets.size() <= 1) {
- show_prompt = true;
- } else {
- offsets.pop_back();
- fseek(fp, offsets.back(), SEEK_SET);
- }
- } else {
- if (feof(fp)) {
- return;
- }
- offsets.push_back(ftello(fp));
- }
- }
- ClearText();
- }
-
- int ch = getc(fp);
- if (ch == EOF) {
- text_row_ = text_top_ = text_rows_ - 2;
+ bool show_prompt = false;
+ while (true) {
+ if (show_prompt) {
+ Print("--(%d%% of %d bytes)--",
+ static_cast<int>(100 * (double(ftello(fp)) / double(sb.st_size))),
+ static_cast<int>(sb.st_size));
+ Redraw();
+ while (show_prompt) {
+ show_prompt = false;
+ int key = WaitKey();
+ if (key == KEY_POWER || key == KEY_ENTER) {
+ return;
+ } else if (key == KEY_UP || key == KEY_VOLUMEUP) {
+ if (offsets.size() <= 1) {
show_prompt = true;
+ } else {
+ offsets.pop_back();
+ fseek(fp, offsets.back(), SEEK_SET);
+ }
} else {
- PutChar(ch);
- if (text_col_ == 0 && text_row_ >= text_rows_ - 2) {
- text_top_ = text_row_;
- show_prompt = true;
- }
+ if (feof(fp)) {
+ return;
+ }
+ offsets.push_back(ftello(fp));
}
+ }
+ ClearText();
}
+
+ int ch = getc(fp);
+ if (ch == EOF) {
+ 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_;
+ show_prompt = true;
+ }
+ }
+ }
}
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_;
- }
- pthread_mutex_unlock(&updateMutex);
+ 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_;
+ }
+ pthread_mutex_unlock(&updateMutex);
}
void WearRecoveryUI::ShowFile(const char* filename) {
- FILE* fp = fopen_path(filename, "re");
- if (fp == nullptr) {
- Print(" Unable to open %s: %s\n", filename, strerror(errno));
- return;
- }
- ShowFile(fp);
- fclose(fp);
-}
-
-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);
- }
- pthread_mutex_unlock(&updateMutex);
+ FILE* fp = fopen_path(filename, "re");
+ if (fp == nullptr) {
+ Print(" Unable to open %s: %s\n", filename, strerror(errno));
+ return;
+ }
+ ShowFile(fp);
+ fclose(fp);
}
void WearRecoveryUI::PrintOnScreenOnly(const char *fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- PrintV(fmt, false, ap);
- va_end(ap);
+ va_list ap;
+ va_start(ap, fmt);
+ PrintV(fmt, false, ap);
+ va_end(ap);
}
void WearRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) {
- std::string str;
- android::base::StringAppendV(&str, fmt, ap);
+ std::string str;
+ android::base::StringAppendV(&str, fmt, ap);
- if (copy_to_stdout) {
- fputs(str.c_str(), stdout);
- }
+ if (copy_to_stdout) {
+ fputs(str.c_str(), stdout);
+ }
- pthread_mutex_lock(&updateMutex);
- 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_[text_row_][text_col_++] = *ptr;
- }
+ pthread_mutex_lock(&updateMutex);
+ 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';
- update_screen_locked();
+ 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);
}
diff --git a/wear_ui.h b/wear_ui.h
index 4cd852f..3bd90b6 100644
--- a/wear_ui.h
+++ b/wear_ui.h
@@ -22,64 +22,58 @@
#include <string>
class WearRecoveryUI : public ScreenRecoveryUI {
- public:
- WearRecoveryUI();
+ public:
+ WearRecoveryUI();
- bool Init(const std::string& locale) override;
+ bool Init(const std::string& locale) override;
- void SetStage(int current, int max) override;
+ void SetStage(int current, int max) override;
- // printing messages
- 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;
+ // printing messages
+ 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) override;
- int SelectMenu(int sel) override;
+ // menu display
+ void StartMenu(const char* const* headers, const char* const* items,
+ int initial_selection) override;
+ int SelectMenu(int sel) override;
- protected:
- // progress bar vertical position, it's centered horizontally
- int progress_bar_y;
+ protected:
+ // progress bar vertical position, it's centered horizontally
+ const int kProgressBarBaseline;
- // outer of window
- int outer_height, outer_width;
+ // Unusable rows when displaying the recovery menu, including the lines for headers (Android
+ // Recovery, build id and etc) and the bottom lines that may otherwise go out of the screen.
+ const int kMenuUnusableRows;
- // Unusable rows when displaying the recovery menu, including the lines
- // for headers (Android Recovery, build id and etc) and the bottom lines
- // that may otherwise go out of the screen.
- int menu_unusable_rows;
+ int GetProgressBaseline() const override;
- int GetProgressBaseline() override;
+ bool InitTextParams() override;
- bool InitTextParams() override;
+ void update_progress_locked() override;
- void update_progress_locked() override;
+ void PrintV(const char*, bool, va_list) override;
- void PrintV(const char*, bool, va_list) override;
+ private:
+ GRSurface* backgroundIcon[5];
- private:
- GRSurface* backgroundIcon[5];
+ static const int kMaxCols = 96;
+ static const int kMaxRows = 96;
- static const int kMaxCols = 96;
- static const int kMaxRows = 96;
+ // Number of text rows seen on screen
+ int visible_text_rows;
- // Number of text rows seen on screen
- int visible_text_rows;
+ const char* const* menu_headers_;
+ int menu_start, menu_end;
- const char* const* menu_headers_;
- int menu_start, menu_end;
+ pthread_t progress_t;
- pthread_t progress_t;
+ void draw_background_locked() override;
+ void draw_screen_locked() override;
- void draw_background_locked() override;
- void draw_screen_locked() override;
- void draw_progress_locked();
-
- void PutChar(char);
- void ClearText();
+ void PutChar(char);
};
#endif // RECOVERY_WEAR_UI_H