summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bootloader_message/bootloader_message.cpp7
-rw-r--r--minui/events.cpp124
-rw-r--r--otautil/include/otautil/boot_state.h (renamed from common.h)21
-rw-r--r--recovery.cpp21
-rw-r--r--recovery_main.cpp19
-rw-r--r--recovery_ui/device.cpp13
-rw-r--r--recovery_ui/include/recovery_ui/device.h10
7 files changed, 172 insertions, 43 deletions
diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp
index e684abbad..b15a9b9fd 100644
--- a/bootloader_message/bootloader_message.cpp
+++ b/bootloader_message/bootloader_message.cpp
@@ -20,6 +20,7 @@
#include <fcntl.h>
#include <string.h>
+#include <optional>
#include <string>
#include <string_view>
#include <vector>
@@ -37,7 +38,7 @@
using android::fs_mgr::Fstab;
using android::fs_mgr::ReadDefaultFstab;
-static std::string g_misc_device_for_test;
+static std::optional<std::string> g_misc_device_for_test;
// Exposed for test purpose.
void SetMiscBlockDeviceForTest(std::string_view misc_device) {
@@ -45,8 +46,8 @@ void SetMiscBlockDeviceForTest(std::string_view misc_device) {
}
static std::string get_misc_blk_device(std::string* err) {
- if (!g_misc_device_for_test.empty()) {
- return g_misc_device_for_test;
+ if (g_misc_device_for_test.has_value() && !g_misc_device_for_test->empty()) {
+ return *g_misc_device_for_test;
}
Fstab fstab;
if (!ReadDefaultFstab(&fstab)) {
diff --git a/minui/events.cpp b/minui/events.cpp
index 7d0250e97..f331ed68a 100644
--- a/minui/events.cpp
+++ b/minui/events.cpp
@@ -22,6 +22,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
+#include <sys/inotify.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <unistd.h>
@@ -33,6 +34,8 @@
#include "minui/minui.h"
+constexpr const char* INPUT_DEV_DIR = "/dev/input";
+
constexpr size_t MAX_DEVICES = 16;
constexpr size_t MAX_MISC_FDS = 16;
@@ -46,6 +49,8 @@ struct FdInfo {
ev_callback cb;
};
+static bool g_allow_touch_inputs = true;
+static ev_callback g_saved_input_cb;
static android::base::unique_fd g_epoll_fd;
static epoll_event g_polled_events[MAX_DEVICES + MAX_MISC_FDS];
static int g_polled_events_count;
@@ -60,6 +65,78 @@ static bool test_bit(size_t bit, unsigned long* array) { // NOLINT
return (array[bit / BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))) != 0;
}
+static bool should_add_input_device(int fd, bool allow_touch_inputs) {
+ // 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) {
+ return false;
+ }
+
+ // 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)) {
+ if (!allow_touch_inputs || !test_bit(EV_ABS, ev_bits)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static int inotify_cb(int fd, __unused uint32_t epevents) {
+ if (g_saved_input_cb == nullptr) return -1;
+
+ // The inotify will put one or several complete events.
+ // Should not read part of one event.
+ size_t event_len;
+ int ret = ioctl(fd, FIONREAD, &event_len);
+ if (ret != 0) return -1;
+
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(INPUT_DEV_DIR), closedir);
+ if (!dir) {
+ return -1;
+ }
+
+ std::vector<int8_t> buf(event_len);
+
+ ssize_t r = TEMP_FAILURE_RETRY(read(fd, buf.data(), event_len));
+ if (r != event_len) {
+ return -1;
+ }
+
+ size_t offset = 0;
+ while (offset < event_len) {
+ struct inotify_event* pevent = reinterpret_cast<struct inotify_event*>(buf.data() + offset);
+ if (offset + sizeof(inotify_event) + pevent->len > event_len) {
+ // The pevent->len is too large and buffer will over flow.
+ // In general, should not happen, just make more stable.
+ return -1;
+ }
+ offset += sizeof(inotify_event) + pevent->len;
+
+ pevent->name[pevent->len] = '\0';
+ if (strncmp(pevent->name, "event", 5)) {
+ continue;
+ }
+
+ android::base::unique_fd dfd(openat(dirfd(dir.get()), pevent->name, O_RDONLY));
+ if (dfd == -1) {
+ break;
+ }
+
+ if (!should_add_input_device(dfd, g_allow_touch_inputs)) {
+ continue;
+ }
+
+ // Only add, we assume the user will not plug out and plug in USB device again and again :)
+ ev_add_fd(std::move(dfd), g_saved_input_cb);
+ }
+
+ return 0;
+}
+
int ev_init(ev_callback input_cb, bool allow_touch_inputs) {
g_epoll_fd.reset();
@@ -68,7 +145,16 @@ int ev_init(ev_callback input_cb, bool allow_touch_inputs) {
return -1;
}
- std::unique_ptr<DIR, decltype(&closedir)> dir(opendir("/dev/input"), closedir);
+ android::base::unique_fd inotify_fd(inotify_init1(IN_CLOEXEC));
+ if (inotify_fd.get() == -1) {
+ return -1;
+ }
+
+ if (inotify_add_watch(inotify_fd, INPUT_DEV_DIR, IN_CREATE) < 0) {
+ return -1;
+ }
+
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(INPUT_DEV_DIR), closedir);
if (!dir) {
return -1;
}
@@ -80,22 +166,10 @@ int ev_init(ev_callback input_cb, bool allow_touch_inputs) {
android::base::unique_fd fd(openat(dirfd(dir.get()), de->d_name, O_RDONLY | O_CLOEXEC));
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) {
+ if (!should_add_input_device(fd, allow_touch_inputs)) {
continue;
}
- // 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)) {
- if (!allow_touch_inputs || !test_bit(EV_ABS, ev_bits)) {
- continue;
- }
- }
-
epoll_event ev;
ev.events = EPOLLIN | EPOLLWAKEUP;
ev.data.ptr = &ev_fdinfo[g_ev_count];
@@ -116,6 +190,11 @@ int ev_init(ev_callback input_cb, bool allow_touch_inputs) {
}
g_epoll_fd.reset(epoll_fd.release());
+
+ g_saved_input_cb = input_cb;
+ g_allow_touch_inputs = allow_touch_inputs;
+ ev_add_fd(std::move(inotify_fd), inotify_cb);
+
return 0;
}
@@ -148,6 +227,7 @@ void ev_exit(void) {
}
g_ev_misc_count = 0;
g_ev_dev_count = 0;
+ g_saved_input_cb = nullptr;
g_epoll_fd.reset();
}
@@ -170,13 +250,17 @@ void ev_dispatch(void) {
}
int ev_get_input(int fd, uint32_t epevents, input_event* ev) {
- if (epevents & EPOLLIN) {
- ssize_t r = TEMP_FAILURE_RETRY(read(fd, ev, sizeof(*ev)));
- if (r == sizeof(*ev)) {
- return 0;
- }
+ if (epevents & EPOLLIN) {
+ ssize_t r = TEMP_FAILURE_RETRY(read(fd, ev, sizeof(*ev)));
+ if (r == sizeof(*ev)) {
+ return 0;
}
- return -1;
+ }
+ if (epevents & EPOLLHUP) {
+ // Delete this watch
+ epoll_ctl(g_epoll_fd, EPOLL_CTL_DEL, fd, nullptr);
+ }
+ return -1;
}
int ev_sync_key_state(const ev_set_key_callback& set_key_cb) {
diff --git a/common.h b/otautil/include/otautil/boot_state.h
index 128a69d9b..6c877baef 100644
--- a/common.h
+++ b/otautil/include/otautil/boot_state.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -17,9 +17,20 @@
#pragma once
#include <string>
+#include <string_view>
-// The current stage, e.g. "1/2".
-extern std::string stage;
+class BootState {
+ public:
+ BootState(std::string_view reason, std::string_view stage) : reason_(reason), stage_(stage) {}
-// The reason argument provided in "--reason=".
-extern const char* reason;
+ std::string reason() const {
+ return reason_;
+ }
+ std::string stage() const {
+ return stage_;
+ }
+
+ private:
+ std::string reason_; // The reason argument provided in "--reason=".
+ std::string stage_; // The current stage, e.g. "1/2".
+};
diff --git a/recovery.cpp b/recovery.cpp
index b989b2465..4862dfccb 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -46,7 +46,6 @@
#include <ziparchive/zip_archive.h>
#include "bootloader_message/bootloader_message.h"
-#include "common.h"
#include "fsck_unshare_blocks.h"
#include "install/adb_install.h"
#include "install/fuse_install.h"
@@ -54,6 +53,7 @@
#include "install/package.h"
#include "install/wipe_data.h"
#include "install/wipe_device.h"
+#include "otautil/boot_state.h"
#include "otautil/error_code.h"
#include "otautil/logging.h"
#include "otautil/paths.h"
@@ -70,8 +70,6 @@ static constexpr const char* LOCALE_FILE = "/cache/recovery/last_locale";
static constexpr const char* CACHE_ROOT = "/cache";
static bool save_current_log = false;
-std::string stage;
-const char* reason = nullptr;
/*
* The recovery tool communicates with the main system through /cache files.
@@ -208,7 +206,8 @@ static InstallResult prompt_and_wipe_data(Device* device) {
}
if (ask_to_wipe_data(device)) {
- bool convert_fbe = reason && strcmp(reason, "convert_fbe") == 0;
+ CHECK(device->GetReason().has_value());
+ bool convert_fbe = device->GetReason().value() == "convert_fbe";
if (WipeData(device, convert_fbe)) {
return INSTALL_SUCCESS;
} else {
@@ -635,12 +634,10 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
fsck_unshare_blocks = true;
} else if (option == "install_with_fuse") {
install_with_fuse = true;
- } else if (option == "locale" || option == "fastboot") {
+ } else if (option == "locale" || option == "fastboot" || option == "reason") {
// Handled in recovery_main.cpp
} else if (option == "prompt_and_wipe_data") {
should_prompt_and_wipe_data = true;
- } else if (option == "reason") {
- reason = optarg;
} else if (option == "rescue") {
rescue = true;
} else if (option == "retry_count") {
@@ -674,8 +671,8 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
}
optind = 1;
- printf("stage is [%s]\n", stage.c_str());
- printf("reason is [%s]\n", reason);
+ printf("stage is [%s]\n", device->GetStage().value_or("").c_str());
+ printf("reason is [%s]\n", device->GetReason().value_or("").c_str());
auto ui = device->GetUI();
@@ -684,7 +681,8 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
ui->SetSystemUpdateText(security_update);
int st_cur, st_max;
- if (!stage.empty() && sscanf(stage.c_str(), "%d/%d", &st_cur, &st_max) == 2) {
+ if (!device->GetStage().has_value() &&
+ sscanf(device->GetStage().value().c_str(), "%d/%d", &st_cur, &st_max) == 2) {
ui->SetStage(st_cur, st_max);
}
@@ -790,7 +788,8 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri
}
} else if (should_wipe_data) {
save_current_log = true;
- bool convert_fbe = reason && strcmp(reason, "convert_fbe") == 0;
+ CHECK(device->GetReason().has_value());
+ bool convert_fbe = device->GetReason().value() == "convert_fbe";
if (!WipeData(device, convert_fbe)) {
status = INSTALL_ERROR;
}
diff --git a/recovery_main.cpp b/recovery_main.cpp
index f0d75ee10..28197bf40 100644
--- a/recovery_main.cpp
+++ b/recovery_main.cpp
@@ -48,9 +48,9 @@
#include <selinux/label.h>
#include <selinux/selinux.h>
-#include "common.h"
#include "fastboot/fastboot.h"
#include "install/wipe_data.h"
+#include "otautil/boot_state.h"
#include "otautil/logging.h"
#include "otautil/paths.h"
#include "otautil/roots.h"
@@ -80,11 +80,12 @@ static void UiLogger(android::base::LogId /* id */, android::base::LogSeverity s
}
}
+// Parses the command line argument from various sources; and reads the stage field from BCB.
// command line args come from, in decreasing precedence:
// - the actual command line
// - the bootloader control block (one per line, after "recovery")
// - the contents of COMMAND_FILE (one per line)
-static std::vector<std::string> get_args(const int argc, char** const argv) {
+static std::vector<std::string> get_args(const int argc, char** const argv, std::string* stage) {
CHECK_GT(argc, 0);
bootloader_message boot = {};
@@ -94,7 +95,9 @@ static std::vector<std::string> get_args(const int argc, char** const argv) {
// If fails, leave a zeroed bootloader_message.
boot = {};
}
- stage = std::string(boot.stage);
+ if (stage) {
+ *stage = std::string(boot.stage);
+ }
std::string boot_command;
if (boot.command[0] != 0) {
@@ -331,12 +334,14 @@ int main(int argc, char** argv) {
load_volume_table();
- std::vector<std::string> args = get_args(argc, argv);
+ std::string stage;
+ std::vector<std::string> args = get_args(argc, argv, &stage);
auto args_to_parse = StringVectorToNullTerminatedArray(args);
static constexpr struct option OPTIONS[] = {
{ "fastboot", no_argument, nullptr, 0 },
{ "locale", required_argument, nullptr, 0 },
+ { "reason", required_argument, nullptr, 0 },
{ "show_text", no_argument, nullptr, 't' },
{ nullptr, 0, nullptr, 0 },
};
@@ -344,6 +349,7 @@ int main(int argc, char** argv) {
bool show_text = false;
bool fastboot = false;
std::string locale;
+ std::string reason;
int arg;
int option_index;
@@ -357,6 +363,8 @@ int main(int argc, char** argv) {
std::string option = OPTIONS[option_index].name;
if (option == "locale") {
locale = optarg;
+ } else if (option == "reason") {
+ reason = optarg;
} else if (option == "fastboot" &&
android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) {
fastboot = true;
@@ -412,6 +420,9 @@ int main(int argc, char** argv) {
device->ResetUI(new StubRecoveryUI());
}
}
+
+ BootState boot_state(reason, stage); // recovery_main owns the state of boot.
+ device->SetBootState(&boot_state);
ui = device->GetUI();
if (!HasCache()) {
diff --git a/recovery_ui/device.cpp b/recovery_ui/device.cpp
index e7ae1a3e1..d46df92d3 100644
--- a/recovery_ui/device.cpp
+++ b/recovery_ui/device.cpp
@@ -23,6 +23,7 @@
#include <android-base/logging.h>
+#include "otautil/boot_state.h"
#include "recovery_ui/ui.h"
static std::vector<std::pair<std::string, Device::BuiltinAction>> g_menu_actions{
@@ -95,3 +96,15 @@ int Device::HandleMenuKey(int key, bool visible) {
return ui_->HasThreeButtons() ? kNoAction : kHighlightDown;
}
}
+
+void Device::SetBootState(const BootState* state) {
+ boot_state_ = state;
+}
+
+std::optional<std::string> Device::GetReason() const {
+ return boot_state_ ? std::make_optional(boot_state_->reason()) : std::nullopt;
+}
+
+std::optional<std::string> Device::GetStage() const {
+ return boot_state_ ? std::make_optional(boot_state_->stage()) : std::nullopt;
+}
diff --git a/recovery_ui/include/recovery_ui/device.h b/recovery_ui/include/recovery_ui/device.h
index 9a4edf261..f4f993638 100644
--- a/recovery_ui/include/recovery_ui/device.h
+++ b/recovery_ui/include/recovery_ui/device.h
@@ -20,12 +20,15 @@
#include <stddef.h>
#include <memory>
+#include <optional>
#include <string>
#include <vector>
// Forward declaration to avoid including "ui.h".
class RecoveryUI;
+class BootState;
+
class Device {
public:
static constexpr const int kNoAction = -1;
@@ -126,9 +129,16 @@ class Device {
return true;
}
+ void SetBootState(const BootState* state);
+ // The getters for reason and stage may return std::nullopt until StartRecovery() is called. It's
+ // the caller's responsibility to perform the check and handle the exception.
+ std::optional<std::string> GetReason() const;
+ std::optional<std::string> GetStage() const;
+
private:
// The RecoveryUI object that should be used to display the user interface for this device.
std::unique_ptr<RecoveryUI> ui_;
+ const BootState* boot_state_{ nullptr };
};
// Disable name mangling, as this function will be loaded via dlsym(3).