From f6ad2a201e0cc0910879ecae957b30fbc7d7b657 Mon Sep 17 00:00:00 2001 From: kotori0 Date: Tue, 26 Jan 2021 17:43:34 +0800 Subject: [PATCH] add key-selector --- edxp-core/build.gradle | 2 + key-selector/.gitignore | 1 + key-selector/build.gradle | 76 ++++++ key-selector/src/main/AndroidManifest.xml | 5 + key-selector/src/main/cpp/CMakeLists.txt | 5 + key-selector/src/main/cpp/key_selector.cpp | 298 +++++++++++++++++++++ key-selector/src/main/cpp/key_selector.h | 32 +++ settings.gradle | 2 +- 8 files changed, 420 insertions(+), 1 deletion(-) create mode 100644 key-selector/.gitignore create mode 100644 key-selector/build.gradle create mode 100644 key-selector/src/main/AndroidManifest.xml create mode 100644 key-selector/src/main/cpp/CMakeLists.txt create mode 100644 key-selector/src/main/cpp/key_selector.cpp create mode 100644 key-selector/src/main/cpp/key_selector.h diff --git a/edxp-core/build.gradle b/edxp-core/build.gradle index 712fe618..c13d76a6 100644 --- a/edxp-core/build.gradle +++ b/edxp-core/build.gradle @@ -38,6 +38,7 @@ dependencies { implementation 'rikka.ndk:riru:10' implementation project(path: ':sandhook-hooklib') compileOnly project(':hiddenapi-stubs') + compileOnly project(':key-selector') compileOnly 'androidx.annotation:annotation:1.1.0' } @@ -152,6 +153,7 @@ afterEvaluate { def prepareMagiskFilesTask = task("prepareMagiskFiles${variantCapped}", type: Delete) { dependsOn "assemble${variantCapped}" dependsOn tasks.getByPath(":sandhook-hooklib:copySandHook${variantCapped}LibraryToMagiskTemplate") + dependsOn tasks.getByPath(":key-selector:copyKeySelector${variantCapped}LibraryToMagiskTemplate") doFirst { copy { from "${projectDir}/tpl/module.prop.tpl" diff --git a/key-selector/.gitignore b/key-selector/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/key-selector/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/key-selector/build.gradle b/key-selector/build.gradle new file mode 100644 index 00000000..b3b8d2d7 --- /dev/null +++ b/key-selector/build.gradle @@ -0,0 +1,76 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion androidCompileSdkVersion.toInteger() + + defaultConfig { + minSdkVersion androidMinSdkVersion.toInteger() + targetSdkVersion androidTargetSdkVersion.toInteger() + + externalNativeBuild { + cmake { + abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64' + cppFlags "-std=c++17" + cFlags "-std=gnu99" + } + } + } + + buildTypes { + debug { + externalNativeBuild { + cmake { + cppFlags "-O0" + cFlags "-O0" + } + } + } + release { + externalNativeBuild { + cmake { + cppFlags "-fvisibility=hidden -fvisibility-inlines-hidden -O2 -s -Wno-unused-value" + cFlags "-fvisibility=hidden -fvisibility-inlines-hidden -O2 -s -Wno-unused-value" + } + } + } + } + externalNativeBuild { + cmake { + path "src/main/cpp/CMakeLists.txt" + } + } +} + +afterEvaluate { + android.libraryVariants.all { variant -> + def variantNameCapped = variant.name.capitalize() + def variantNameLowered = variant.name.toLowerCase() + + task("copyKeySelector${variantNameCapped}LibraryToMagiskTemplate") { + def libPathRelease = "${buildDir}/intermediates/cmake/${variantNameLowered}/obj" + doLast { + copy { + include "key_selector" + from "${libPathRelease}/armeabi-v7a" + into "${zipPathMagiskReleasePath}/system/bin" + } + copy { + include "key_selector" + from "${libPathRelease}/arm64-v8a" + into "${zipPathMagiskReleasePath}/system/bin64" + } + copy { + include "key_selector" + from "${libPathRelease}/x86" + into "${zipPathMagiskReleasePath}/system_x86/bin" + } + copy { + include "key_selector" + from "${libPathRelease}/x86_64" + into "${zipPathMagiskReleasePath}/system_x86/bin64" + } + } + } + + } +} \ No newline at end of file diff --git a/key-selector/src/main/AndroidManifest.xml b/key-selector/src/main/AndroidManifest.xml new file mode 100644 index 00000000..cbe21451 --- /dev/null +++ b/key-selector/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/key-selector/src/main/cpp/CMakeLists.txt b/key-selector/src/main/cpp/CMakeLists.txt new file mode 100644 index 00000000..10e183ff --- /dev/null +++ b/key-selector/src/main/cpp/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.4.1) + +project(key_selector) + +add_executable(key_selector key_selector.cpp) diff --git a/key-selector/src/main/cpp/key_selector.cpp b/key-selector/src/main/cpp/key_selector.cpp new file mode 100644 index 00000000..7e31fe1c --- /dev/null +++ b/key-selector/src/main/cpp/key_selector.cpp @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2015-2016 The CyanogenMod Project + * Copyright (C) 2021 LSPosed + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "key_selector.h" + +// Global variables +static struct pollfd *ufds; +static char **device_names; +static int nfds; + +static int open_device(const char *device) +{ + int version; + int fd; + int clkid = CLOCK_MONOTONIC; + struct pollfd *new_ufds; + char **new_device_names; + char name[80]; + char location[80]; + char idstr[80]; + struct input_id id; + + fd = open(device, O_RDWR); + if (fd < 0) { + return -1; + } + + if (ioctl(fd, EVIOCGVERSION, &version)) { + return -1; + } + + if (ioctl(fd, EVIOCGID, &id)) { + return -1; + } + + name[sizeof(name) - 1] = '\0'; + location[sizeof(location) - 1] = '\0'; + idstr[sizeof(idstr) - 1] = '\0'; + + if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) { + name[0] = '\0'; + } + + if (ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) { + location[0] = '\0'; + } + + if (ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) { + idstr[0] = '\0'; + } + + if (ioctl(fd, EVIOCSCLOCKID, &clkid) != 0) { + // a non-fatal error + } + + new_ufds = static_cast(realloc(ufds, sizeof(ufds[0]) * (nfds + 1))); + if (new_ufds == nullptr) { + return -1; + } + + ufds = new_ufds; + new_device_names = static_cast(realloc(device_names, + sizeof(device_names[0]) * (nfds + 1))); + if (new_device_names == nullptr) { + return -1; + } + + device_names = new_device_names; + + ufds[nfds].fd = fd; + ufds[nfds].events = POLLIN; + device_names[nfds] = strdup(device); + nfds++; + + return 0; +} + +int close_device(const char *device) +{ + int i; + for (i = 1; i < nfds; i++) { + if (strcmp(device_names[i], device) == 0) { + int count = nfds - i - 1; + free(device_names[i]); + memmove(device_names + i, device_names + i + 1, + sizeof(device_names[0]) * count); + memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count); + nfds--; + return 0; + } + } + return -1; +} + +static int read_notify(const char *dirname, int nfd) +{ + int res; + char devname[PATH_MAX]; + char *filename; + char event_buf[512]; + int event_size; + int event_pos = 0; + struct inotify_event *event; + + res = read(nfd, event_buf, sizeof(event_buf)); + if (res < (int)sizeof(*event)) { + if (errno == EINTR) { + return 0; + } + return 1; + } + + strcpy(devname, dirname); + filename = devname + strlen(devname); + *filename++ = '/'; + + while (res >= (int)sizeof(*event)) { + event = (struct inotify_event *)(event_buf + event_pos); + if (event->len) { + strcpy(filename, event->name); + if (event->mask & IN_CREATE) { + open_device(devname); + } else { + close_device(devname); + } + } + event_size = sizeof(*event) + event->len; + res -= event_size; + event_pos += event_size; + } + return 0; +} + +static int scan_dir(const char *dirname) +{ + char devname[PATH_MAX]; + char *filename; + DIR *dir; + struct dirent *de; + + dir = opendir(dirname); + if (dir == NULL) { + return -1; + } + + strcpy(devname, dirname); + filename = devname + strlen(devname); + *filename++ = '/'; + + while ((de = readdir(dir))) { + if (de->d_name[0] == '.' && (de->d_name[1] == '\0' || + (de->d_name[1] == '.' && de->d_name[2] == '\0'))) { + continue; + } + + strcpy(filename, de->d_name); + open_device(devname); + } + closedir(dir); + return 0; +} + + +uint32_t get_event() { + int i; + int res; + struct input_event event; + const char *device_path = "/dev/input"; + unsigned char keys; + + keys = KEYCHECK_CHECK_VOLUMEDOWN | KEYCHECK_CHECK_VOLUMEUP; + nfds = 1; + ufds = static_cast(calloc(1, sizeof(ufds[0]))); + ufds[0].fd = inotify_init(); + ufds[0].events = POLLIN; + + res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE); + if (res < 0) { + throw std::logic_error("inotify_add_watch failed"); + } + + res = scan_dir(device_path); + if (res < 0) { + throw std::logic_error("scan dev failed"); + } + + while (true) { + poll(ufds, nfds, -1); + if (ufds[0].revents & POLLIN) { + read_notify(device_path, ufds[0].fd); + } + + for (i = 1; i < nfds; i++) { + if ((ufds[i].revents) && (ufds[i].revents & POLLIN)) { + res = read(ufds[i].fd, &event, sizeof(event)); + if (res < (int)sizeof(event)) { + return 1; + } + + // keypress only + if (event.value == 1) { + if (event.code == KEY_VOLUMEDOWN && + (keys & KEYCHECK_CHECK_VOLUMEDOWN) != 0) { + return KEYCHECK_PRESSED_VOLUMEDOWN; + } + else if (event.code == KEY_VOLUMEUP && + (keys & KEYCHECK_CHECK_VOLUMEUP) != 0) { + return KEYCHECK_PRESSED_VOLUMEUP; + } + } + } + } + } +} + +int main() { + if (getuid() != 0) { + std::cout << "Root required" << std::endl; + exit(1); + } + + // for phone which has no button + const uint16_t timeout = 20; + alarm(timeout); + auto sig_handler = [](int){ + std::cout << "No operation after " << timeout << " seconds" << std::endl; + exit(YAHFA); + }; + signal(SIGALRM, sig_handler); + + int cursor = YAHFA; + const int cursor_max = SandHook; + + auto print_status = [&cursor](){ + std::cout << "\33[2K\r"; // clear this line + std::cout << "["; + std::cout << (cursor == YAHFA ? "x" : " "); + std::cout << "] YAHFA ["; + std::cout << (cursor == SandHook ? "x" : " "); + std::cout << "] SandHook" << std::flush; + }; + + std::cout << "Select variant. Use Volume Down to move and Volume Up to confirm." << std::endl; + std::cout << "The program will select YAHFA for you in " << timeout << " seconds if you don't have a physical volume button. " << std::endl; + print_status(); + while (int event = get_event()) { + bool leave = false; + //std::cout << event << " " << cursor << std::endl; + switch (event) { + case KEYCHECK_PRESSED_VOLUMEUP: + leave = true; + break; + case KEYCHECK_PRESSED_VOLUMEDOWN: + cursor++; + if (cursor > cursor_max) { + cursor = YAHFA; + } + break; + default: + std::cout << "ERROR\n"; + } + print_status(); + if (leave) { + break; + } + } + + // std::cout << std::endl << cursor << std::endl; + return cursor; +} \ No newline at end of file diff --git a/key-selector/src/main/cpp/key_selector.h b/key-selector/src/main/cpp/key_selector.h new file mode 100644 index 00000000..730c4f0f --- /dev/null +++ b/key-selector/src/main/cpp/key_selector.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2015-2016 The CyanogenMod Project + * Copyright (C) 2021 LSPosed + * + * 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 __KEYCHECK_H__ +#define __KEYCHECK_H__ + +// Constants: pressed keys +#define KEYCHECK_CHECK_VOLUMEDOWN 0x01u +#define KEYCHECK_CHECK_VOLUMEUP 0x02u +#define KEYCHECK_PRESSED_VOLUMEDOWN 41u +#define KEYCHECK_PRESSED_VOLUMEUP 42u + +enum Variant { + YAHFA = 0x11, + SandHook = 0x12, +}; + +#endif // __KEYCHECK_H__ \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 82bdba0d..e8f128de 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':edxp-core', ':hiddenapi-stubs', ':sandhook-hooklib', ':sandhook-annotation', ':app' +include ':edxp-core', ':hiddenapi-stubs', ':sandhook-hooklib', ':sandhook-annotation', ':app', ':key-selector'