Merge changes from topic "b/64114943"
am: 98caf20a65
Change-Id: I7629aa02a6e2562b6ec1af6b1f76e6ef0118029a
diff --git a/init/init.cpp b/init/init.cpp
index 0d5690b..54bbb4b 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -31,6 +31,10 @@
#include <sys/types.h>
#include <unistd.h>
+#include <map>
+#include <memory>
+#include <optional>
+
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -43,9 +47,6 @@
#include <private/android_filesystem_config.h>
#include <selinux/android.h>
-#include <memory>
-#include <optional>
-
#include "action_parser.h"
#include "import_parser.h"
#include "init_first_stage.h"
@@ -130,12 +131,31 @@
}
}
-void register_epoll_handler(int fd, void (*fn)()) {
+static std::map<int, std::function<void()>> epoll_handlers;
+
+void register_epoll_handler(int fd, std::function<void()> handler) {
+ auto[it, inserted] = epoll_handlers.emplace(fd, std::move(handler));
+ if (!inserted) {
+ LOG(ERROR) << "Cannot specify two epoll handlers for a given FD";
+ return;
+ }
epoll_event ev;
ev.events = EPOLLIN;
- ev.data.ptr = reinterpret_cast<void*>(fn);
+ // std::map's iterators do not get invalidated until erased, so we use the pointer to the
+ // std::function in the map directly for epoll_ctl.
+ ev.data.ptr = reinterpret_cast<void*>(&it->second);
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
- PLOG(ERROR) << "epoll_ctl failed";
+ PLOG(ERROR) << "epoll_ctl failed to add fd";
+ epoll_handlers.erase(fd);
+ }
+}
+
+void unregister_epoll_handler(int fd) {
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, nullptr) == -1) {
+ PLOG(ERROR) << "epoll_ctl failed to remove fd";
+ }
+ if (epoll_handlers.erase(fd) != 1) {
+ LOG(ERROR) << "Attempting to remove epoll handler for FD without an existing handler";
}
}
@@ -334,8 +354,8 @@
return Success();
}
-static Result<Success> keychord_init_action(const BuiltinArguments& args) {
- keychord_init();
+static Result<Success> KeychordInitAction(const BuiltinArguments& args) {
+ KeychordInit();
return Success();
}
@@ -752,7 +772,7 @@
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
- am.QueueBuiltinAction(keychord_init_action, "keychord_init");
+ am.QueueBuiltinAction(KeychordInitAction, "KeychordInit");
am.QueueBuiltinAction(console_init_action, "console_init");
// Trigger all the boot actions to get us started.
@@ -809,7 +829,7 @@
if (nr == -1) {
PLOG(ERROR) << "epoll_wait failed";
} else if (nr == 1) {
- ((void (*)()) ev.data.ptr)();
+ std::invoke(*reinterpret_cast<std::function<void()>*>(ev.data.ptr));
}
}
diff --git a/init/init.h b/init/init.h
index d4a0e96..e7c4d8d 100644
--- a/init/init.h
+++ b/init/init.h
@@ -19,6 +19,7 @@
#include <sys/types.h>
+#include <functional>
#include <string>
#include <vector>
@@ -42,7 +43,8 @@
void property_changed(const std::string& name, const std::string& value);
-void register_epoll_handler(int fd, void (*fn)());
+void register_epoll_handler(int fd, std::function<void()> handler);
+void unregister_epoll_handler(int fd);
bool start_waiting_for_property(const char *name, const char *value);
diff --git a/init/keychords.cpp b/init/keychords.cpp
index e686ce1..f55d2c4 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -16,13 +16,20 @@
#include "keychords.h"
+#include <dirent.h>
#include <fcntl.h>
-#include <stdlib.h>
-#include <sys/stat.h>
+#include <linux/input.h>
+#include <sys/cdefs.h>
+#include <sys/ioctl.h>
#include <sys/types.h>
-#include <linux/keychord.h>
#include <unistd.h>
+#include <algorithm>
+#include <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
#include <android-base/logging.h>
#include <android-base/properties.h>
@@ -31,51 +38,87 @@
namespace android {
namespace init {
-static struct input_keychord *keychords = 0;
-static int keychords_count = 0;
-static int keychords_length = 0;
-static int keychord_fd = -1;
+namespace {
-void add_service_keycodes(Service* svc)
-{
- struct input_keychord *keychord;
- size_t i, size;
+int keychords_count;
- if (!svc->keycodes().empty()) {
- /* add a new keychord to the list */
- size = sizeof(*keychord) + svc->keycodes().size() * sizeof(keychord->keycodes[0]);
- keychords = (input_keychord*) realloc(keychords, keychords_length + size);
- if (!keychords) {
- PLOG(ERROR) << "could not allocate keychords";
- keychords_length = 0;
- keychords_count = 0;
- return;
+struct KeychordEntry {
+ const std::vector<int> keycodes;
+ bool notified;
+ int id;
+
+ KeychordEntry(const std::vector<int>& keycodes, int id)
+ : keycodes(keycodes), notified(false), id(id) {}
+};
+
+std::vector<KeychordEntry> keychord_entries;
+
+// Bit management
+class KeychordMask {
+ private:
+ typedef unsigned int mask_t;
+ std::vector<mask_t> bits;
+ static constexpr size_t bits_per_byte = 8;
+
+ public:
+ explicit KeychordMask(size_t bit = 0) : bits((bit + sizeof(mask_t) - 1) / sizeof(mask_t), 0) {}
+
+ void SetBit(size_t bit, bool value = true) {
+ auto idx = bit / (bits_per_byte * sizeof(mask_t));
+ if (idx >= bits.size()) return;
+ if (value) {
+ bits[idx] |= mask_t(1) << (bit % (bits_per_byte * sizeof(mask_t)));
+ } else {
+ bits[idx] &= ~(mask_t(1) << (bit % (bits_per_byte * sizeof(mask_t))));
}
-
- keychord = (struct input_keychord *)((char *)keychords + keychords_length);
- keychord->version = KEYCHORD_VERSION;
- keychord->id = keychords_count + 1;
- keychord->count = svc->keycodes().size();
- svc->set_keychord_id(keychord->id);
-
- for (i = 0; i < svc->keycodes().size(); i++) {
- keychord->keycodes[i] = svc->keycodes()[i];
- }
- keychords_count++;
- keychords_length += size;
- }
-}
-
-static void handle_keychord() {
- int ret;
- __u16 id;
-
- ret = read(keychord_fd, &id, sizeof(id));
- if (ret != sizeof(id)) {
- PLOG(ERROR) << "could not read keychord id";
- return;
}
+ bool GetBit(size_t bit) const {
+ auto idx = bit / (bits_per_byte * sizeof(mask_t));
+ return bits[idx] & (mask_t(1) << (bit % (bits_per_byte * sizeof(mask_t))));
+ }
+
+ size_t bytesize() const { return bits.size() * sizeof(mask_t); }
+ void* data() { return bits.data(); }
+ size_t size() const { return bits.size() * sizeof(mask_t) * bits_per_byte; }
+ void resize(size_t bit) {
+ auto idx = bit / (bits_per_byte * sizeof(mask_t));
+ if (idx >= bits.size()) {
+ bits.resize(idx + 1, 0);
+ }
+ }
+
+ operator bool() const {
+ for (size_t i = 0; i < bits.size(); ++i) {
+ if (bits[i]) return true;
+ }
+ return false;
+ }
+
+ KeychordMask operator&(const KeychordMask& rval) const {
+ auto len = std::min(bits.size(), rval.bits.size());
+ KeychordMask ret;
+ ret.bits.resize(len);
+ for (size_t i = 0; i < len; ++i) {
+ ret.bits[i] = bits[i] & rval.bits[i];
+ }
+ return ret;
+ }
+
+ void operator|=(const KeychordMask& rval) {
+ size_t len = rval.bits.size();
+ bits.resize(len);
+ for (size_t i = 0; i < len; ++i) {
+ bits[i] |= rval.bits[i];
+ }
+ }
+};
+
+KeychordMask keychord_current;
+
+constexpr char kDevicePath[] = "/dev/input";
+
+void HandleKeychord(int id) {
// Only handle keychords if adb is enabled.
std::string adb_enabled = android::base::GetProperty("init.svc.adbd", "");
if (adb_enabled == "running") {
@@ -94,32 +137,125 @@
}
}
-void keychord_init() {
+void KeychordLambdaCheck() {
+ for (auto& e : keychord_entries) {
+ bool found = true;
+ for (auto& code : e.keycodes) {
+ if (!keychord_current.GetBit(code)) {
+ e.notified = false;
+ found = false;
+ break;
+ }
+ }
+ if (!found) continue;
+ if (e.notified) continue;
+ e.notified = true;
+ HandleKeychord(e.id);
+ }
+}
+
+void KeychordLambdaHandler(int fd) {
+ input_event event;
+ auto res = TEMP_FAILURE_RETRY(::read(fd, &event, sizeof(event)));
+ if ((res != sizeof(event)) || (event.type != EV_KEY)) return;
+ keychord_current.SetBit(event.code, event.value);
+ KeychordLambdaCheck();
+}
+
+bool KeychordGeteventEnable(int fd) {
+ static bool EviocsmaskSupported = true;
+
+ // Make sure it is an event channel, should pass this ioctl call
+ int version;
+ if (::ioctl(fd, EVIOCGVERSION, &version)) return false;
+
+ if (EviocsmaskSupported) {
+ KeychordMask mask(EV_KEY);
+ mask.SetBit(EV_KEY);
+ input_mask msg = {};
+ msg.type = EV_SYN;
+ msg.codes_size = mask.bytesize();
+ msg.codes_ptr = reinterpret_cast<uintptr_t>(mask.data());
+ if (::ioctl(fd, EVIOCSMASK, &msg) == -1) {
+ PLOG(WARNING) << "EVIOCSMASK not supported";
+ EviocsmaskSupported = false;
+ }
+ }
+
+ KeychordMask mask;
+ for (auto& e : keychord_entries) {
+ for (auto& code : e.keycodes) {
+ mask.resize(code);
+ mask.SetBit(code);
+ }
+ }
+
+ keychord_current.resize(mask.size());
+ KeychordMask available(mask.size());
+ auto res = ::ioctl(fd, EVIOCGBIT(EV_KEY, available.bytesize()), available.data());
+ if (res == -1) return false;
+ if (!(available & mask)) return false;
+
+ if (EviocsmaskSupported) {
+ input_mask msg = {};
+ msg.type = EV_KEY;
+ msg.codes_size = mask.bytesize();
+ msg.codes_ptr = reinterpret_cast<uintptr_t>(mask.data());
+ ::ioctl(fd, EVIOCSMASK, &msg);
+ }
+
+ KeychordMask set(mask.size());
+ res = ::ioctl(fd, EVIOCGKEY(res), set.data());
+ if (res > 0) {
+ keychord_current |= mask & available & set;
+ KeychordLambdaCheck();
+ }
+ register_epoll_handler(fd, [fd]() { KeychordLambdaHandler(fd); });
+ return true;
+}
+
+void GeteventOpenDevice(const std::string& device) {
+ auto fd = TEMP_FAILURE_RETRY(::open(device.c_str(), O_RDWR | O_CLOEXEC));
+ if (fd == -1) {
+ PLOG(ERROR) << "Can not open " << device;
+ return;
+ }
+ if (!KeychordGeteventEnable(fd)) {
+ ::close(fd);
+ }
+}
+
+void GeteventOpenDevice() {
+ std::unique_ptr<DIR, decltype(&closedir)> device(opendir(kDevicePath), closedir);
+ if (!device) return;
+
+ dirent* entry;
+ while ((entry = readdir(device.get()))) {
+ if (entry->d_name[0] == '.') continue;
+ std::string devname(kDevicePath);
+ devname += '/';
+ devname += entry->d_name;
+ GeteventOpenDevice(devname);
+ }
+}
+
+void AddServiceKeycodes(Service* svc) {
+ if (svc->keycodes().empty()) return;
+ for (auto& code : svc->keycodes()) {
+ if ((code < 0) || (code >= KEY_MAX)) return;
+ }
+ ++keychords_count;
+ keychord_entries.emplace_back(KeychordEntry(svc->keycodes(), keychords_count));
+ svc->set_keychord_id(keychords_count);
+}
+
+} // namespace
+
+void KeychordInit() {
for (const auto& service : ServiceList::GetInstance()) {
- add_service_keycodes(service.get());
+ AddServiceKeycodes(service.get());
}
-
- // Nothing to do if no services require keychords.
- if (!keychords) {
- return;
- }
-
- keychord_fd = TEMP_FAILURE_RETRY(open("/dev/keychord", O_RDWR | O_CLOEXEC));
- if (keychord_fd == -1) {
- PLOG(ERROR) << "could not open /dev/keychord";
- return;
- }
-
- int ret = write(keychord_fd, keychords, keychords_length);
- if (ret != keychords_length) {
- PLOG(ERROR) << "could not configure /dev/keychord " << ret;
- close(keychord_fd);
- }
-
- free(keychords);
- keychords = nullptr;
-
- register_epoll_handler(keychord_fd, handle_keychord);
+ if (keychords_count) GeteventOpenDevice();
}
} // namespace init
diff --git a/init/keychords.h b/init/keychords.h
index 1c34098..689a3b5 100644
--- a/init/keychords.h
+++ b/init/keychords.h
@@ -22,8 +22,7 @@
namespace android {
namespace init {
-void add_service_keycodes(Service* svc);
-void keychord_init();
+void KeychordInit();
} // namespace init
} // namespace android