diff --git a/init/init.cpp b/init/init.cpp index 42032caeb..54bbb4b66 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -354,8 +354,8 @@ static Result wait_for_coldboot_done_action(const BuiltinArguments& arg return Success(); } -static Result keychord_init_action(const BuiltinArguments& args) { - keychord_init(); +static Result KeychordInitAction(const BuiltinArguments& args) { + KeychordInit(); return Success(); } @@ -772,7 +772,7 @@ int main(int argc, char** argv) { 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. diff --git a/init/keychords.cpp b/init/keychords.cpp index e686ce1e4..f55d2c43c 100644 --- a/init/keychords.cpp +++ b/init/keychords.cpp @@ -16,13 +16,20 @@ #include "keychords.h" +#include #include -#include -#include +#include +#include +#include #include -#include #include +#include +#include +#include +#include +#include + #include #include @@ -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 keycodes; + bool notified; + int id; + + KeychordEntry(const std::vector& keycodes, int id) + : keycodes(keycodes), notified(false), id(id) {} +}; + +std::vector keychord_entries; + +// Bit management +class KeychordMask { + private: + typedef unsigned int mask_t; + std::vector 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 @@ static void handle_keychord() { } } -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(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(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 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 1c3409865..689a3b578 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