| Prabir Pradhan | e0105c9 | 2019-12-26 12:32:13 -0800 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (C) 2020 The Android Open Source Project | 
|  | 3 | * | 
|  | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | 5 | * you may not use this file except in compliance with the License. | 
|  | 6 | * You may obtain a copy of the License at | 
|  | 7 | * | 
|  | 8 | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | 9 | * | 
|  | 10 | * Unless required by applicable law or agreed to in writing, software | 
|  | 11 | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | 13 | * See the License for the specific language governing permissions and | 
|  | 14 | * limitations under the License. | 
|  | 15 | */ | 
|  | 16 |  | 
|  | 17 | #include "UinputDevice.h" | 
|  | 18 |  | 
|  | 19 | #include <android-base/stringprintf.h> | 
|  | 20 |  | 
|  | 21 | namespace android { | 
|  | 22 |  | 
|  | 23 | // --- UinputDevice --- | 
|  | 24 |  | 
|  | 25 | UinputDevice::UinputDevice(const char* name) : mName(name) {} | 
|  | 26 |  | 
|  | 27 | UinputDevice::~UinputDevice() { | 
|  | 28 | if (ioctl(mDeviceFd, UI_DEV_DESTROY)) { | 
|  | 29 | ALOGE("Error while destroying uinput device: %s", strerror(errno)); | 
|  | 30 | } | 
|  | 31 | mDeviceFd.reset(); | 
|  | 32 | } | 
|  | 33 |  | 
|  | 34 | void UinputDevice::init() { | 
|  | 35 | mDeviceFd = android::base::unique_fd(open("/dev/uinput", O_WRONLY | O_NONBLOCK)); | 
|  | 36 | if (mDeviceFd < 0) { | 
|  | 37 | FAIL() << "Can't open /dev/uinput :" << strerror(errno); | 
|  | 38 | } | 
|  | 39 |  | 
|  | 40 | struct uinput_user_dev device = {}; | 
|  | 41 | strlcpy(device.name, mName, UINPUT_MAX_NAME_SIZE); | 
|  | 42 | device.id.bustype = BUS_USB; | 
|  | 43 | device.id.vendor = 0x01; | 
|  | 44 | device.id.product = 0x01; | 
|  | 45 | device.id.version = 1; | 
|  | 46 |  | 
| Siarhei Vishniakou | e5b5e45 | 2020-04-02 17:59:16 -0700 | [diff] [blame] | 47 | ASSERT_NO_FATAL_FAILURE(configureDevice(mDeviceFd, &device)); | 
| Prabir Pradhan | e0105c9 | 2019-12-26 12:32:13 -0800 | [diff] [blame] | 48 |  | 
|  | 49 | if (write(mDeviceFd, &device, sizeof(device)) < 0) { | 
|  | 50 | FAIL() << "Could not write uinput_user_dev struct into uinput file descriptor: " | 
|  | 51 | << strerror(errno); | 
|  | 52 | } | 
|  | 53 |  | 
|  | 54 | if (ioctl(mDeviceFd, UI_DEV_CREATE)) { | 
|  | 55 | FAIL() << "Error in ioctl : UI_DEV_CREATE: " << strerror(errno); | 
|  | 56 | } | 
|  | 57 | } | 
|  | 58 |  | 
|  | 59 | void UinputDevice::injectEvent(uint16_t type, uint16_t code, int32_t value) { | 
|  | 60 | struct input_event event = {}; | 
|  | 61 | event.type = type; | 
|  | 62 | event.code = code; | 
|  | 63 | event.value = value; | 
|  | 64 | event.time = {}; // uinput ignores the timestamp | 
|  | 65 |  | 
|  | 66 | if (write(mDeviceFd, &event, sizeof(input_event)) < 0) { | 
|  | 67 | std::string msg = base::StringPrintf("Could not write event %" PRIu16 " %" PRIu16 | 
|  | 68 | " with value %" PRId32 " : %s", | 
|  | 69 | type, code, value, strerror(errno)); | 
|  | 70 | ALOGE("%s", msg.c_str()); | 
| Siarhei Vishniakou | e5b5e45 | 2020-04-02 17:59:16 -0700 | [diff] [blame] | 71 | FAIL() << msg.c_str(); | 
| Prabir Pradhan | e0105c9 | 2019-12-26 12:32:13 -0800 | [diff] [blame] | 72 | } | 
|  | 73 | } | 
|  | 74 |  | 
|  | 75 | // --- UinputKeyboard --- | 
|  | 76 |  | 
|  | 77 | UinputKeyboard::UinputKeyboard(std::initializer_list<int> keys) | 
|  | 78 | : UinputDevice(UinputKeyboard::KEYBOARD_NAME), mKeys(keys.begin(), keys.end()) {} | 
|  | 79 |  | 
|  | 80 | void UinputKeyboard::configureDevice(int fd, uinput_user_dev* device) { | 
|  | 81 | // enable key press/release event | 
|  | 82 | if (ioctl(fd, UI_SET_EVBIT, EV_KEY)) { | 
| Siarhei Vishniakou | e5b5e45 | 2020-04-02 17:59:16 -0700 | [diff] [blame] | 83 | FAIL() << "Error in ioctl : UI_SET_EVBIT : EV_KEY: " << strerror(errno); | 
| Prabir Pradhan | e0105c9 | 2019-12-26 12:32:13 -0800 | [diff] [blame] | 84 | } | 
|  | 85 |  | 
|  | 86 | // enable set of KEY events | 
|  | 87 | std::for_each(mKeys.begin(), mKeys.end(), [fd](int key) { | 
|  | 88 | if (ioctl(fd, UI_SET_KEYBIT, key)) { | 
| Siarhei Vishniakou | e5b5e45 | 2020-04-02 17:59:16 -0700 | [diff] [blame] | 89 | FAIL() << "Error in ioctl : UI_SET_KEYBIT : " << key << " : " << strerror(errno); | 
| Prabir Pradhan | e0105c9 | 2019-12-26 12:32:13 -0800 | [diff] [blame] | 90 | } | 
|  | 91 | }); | 
|  | 92 |  | 
|  | 93 | // enable synchronization event | 
|  | 94 | if (ioctl(fd, UI_SET_EVBIT, EV_SYN)) { | 
| Siarhei Vishniakou | e5b5e45 | 2020-04-02 17:59:16 -0700 | [diff] [blame] | 95 | FAIL() << "Error in ioctl : UI_SET_EVBIT : EV_SYN: " << strerror(errno); | 
| Prabir Pradhan | e0105c9 | 2019-12-26 12:32:13 -0800 | [diff] [blame] | 96 | } | 
|  | 97 | } | 
|  | 98 |  | 
|  | 99 | void UinputKeyboard::pressKey(int key) { | 
|  | 100 | if (mKeys.find(key) == mKeys.end()) { | 
| Siarhei Vishniakou | e5b5e45 | 2020-04-02 17:59:16 -0700 | [diff] [blame] | 101 | FAIL() << mName << ": Cannot inject key press: Key not found: " << key; | 
| Prabir Pradhan | e0105c9 | 2019-12-26 12:32:13 -0800 | [diff] [blame] | 102 | } | 
| Siarhei Vishniakou | e5b5e45 | 2020-04-02 17:59:16 -0700 | [diff] [blame] | 103 | injectEvent(EV_KEY, key, 1); | 
|  | 104 | injectEvent(EV_SYN, SYN_REPORT, 0); | 
| Prabir Pradhan | e0105c9 | 2019-12-26 12:32:13 -0800 | [diff] [blame] | 105 | } | 
|  | 106 |  | 
|  | 107 | void UinputKeyboard::releaseKey(int key) { | 
|  | 108 | if (mKeys.find(key) == mKeys.end()) { | 
| Siarhei Vishniakou | e5b5e45 | 2020-04-02 17:59:16 -0700 | [diff] [blame] | 109 | FAIL() << mName << ": Cannot inject key release: Key not found: " << key; | 
| Prabir Pradhan | e0105c9 | 2019-12-26 12:32:13 -0800 | [diff] [blame] | 110 | } | 
| Siarhei Vishniakou | e5b5e45 | 2020-04-02 17:59:16 -0700 | [diff] [blame] | 111 | injectEvent(EV_KEY, key, 0); | 
|  | 112 | injectEvent(EV_SYN, SYN_REPORT, 0); | 
| Prabir Pradhan | e0105c9 | 2019-12-26 12:32:13 -0800 | [diff] [blame] | 113 | } | 
|  | 114 |  | 
|  | 115 | void UinputKeyboard::pressAndReleaseKey(int key) { | 
| Siarhei Vishniakou | e5b5e45 | 2020-04-02 17:59:16 -0700 | [diff] [blame] | 116 | pressKey(key); | 
|  | 117 | releaseKey(key); | 
| Prabir Pradhan | e0105c9 | 2019-12-26 12:32:13 -0800 | [diff] [blame] | 118 | } | 
|  | 119 |  | 
| Arthur Hung | aab2562 | 2020-01-16 11:22:11 +0800 | [diff] [blame] | 120 | // --- UinputHomeKey --- | 
| Prabir Pradhan | e0105c9 | 2019-12-26 12:32:13 -0800 | [diff] [blame] | 121 |  | 
|  | 122 | UinputHomeKey::UinputHomeKey() : UinputKeyboard({KEY_HOME}) {} | 
|  | 123 |  | 
|  | 124 | void UinputHomeKey::pressAndReleaseHomeKey() { | 
| Siarhei Vishniakou | e5b5e45 | 2020-04-02 17:59:16 -0700 | [diff] [blame] | 125 | pressAndReleaseKey(KEY_HOME); | 
| Prabir Pradhan | e0105c9 | 2019-12-26 12:32:13 -0800 | [diff] [blame] | 126 | } | 
|  | 127 |  | 
| Siarhei Vishniakou | a0d2b80 | 2020-05-13 14:00:31 -0700 | [diff] [blame] | 128 | // --- UinputSteamController | 
|  | 129 | UinputSteamController::UinputSteamController() : UinputKeyboard({BTN_GEAR_DOWN, BTN_GEAR_UP}) {} | 
|  | 130 |  | 
| Arthur Hung | aab2562 | 2020-01-16 11:22:11 +0800 | [diff] [blame] | 131 | // --- UinputTouchScreen --- | 
|  | 132 | UinputTouchScreen::UinputTouchScreen(const Rect* size) | 
|  | 133 | : UinputDevice(UinputTouchScreen::DEVICE_NAME), mSize(*size) {} | 
|  | 134 |  | 
|  | 135 | void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) { | 
|  | 136 | // Setup the touch screen device | 
|  | 137 | ioctl(fd, UI_SET_EVBIT, EV_KEY); | 
|  | 138 | ioctl(fd, UI_SET_EVBIT, EV_REL); | 
|  | 139 | ioctl(fd, UI_SET_EVBIT, EV_ABS); | 
|  | 140 | ioctl(fd, UI_SET_ABSBIT, ABS_MT_SLOT); | 
|  | 141 | ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR); | 
|  | 142 | ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_X); | 
|  | 143 | ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y); | 
|  | 144 | ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID); | 
|  | 145 | ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE); | 
|  | 146 | ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT); | 
|  | 147 | ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH); | 
|  | 148 |  | 
|  | 149 | device->absmin[ABS_MT_SLOT] = RAW_SLOT_MIN; | 
|  | 150 | device->absmax[ABS_MT_SLOT] = RAW_SLOT_MAX; | 
|  | 151 | device->absmin[ABS_MT_TOUCH_MAJOR] = RAW_TOUCH_MIN; | 
|  | 152 | device->absmax[ABS_MT_TOUCH_MAJOR] = RAW_TOUCH_MAX; | 
|  | 153 | device->absmin[ABS_MT_POSITION_X] = mSize.left; | 
|  | 154 | device->absmax[ABS_MT_POSITION_X] = mSize.right - 1; | 
|  | 155 | device->absmin[ABS_MT_POSITION_Y] = mSize.top; | 
|  | 156 | device->absmax[ABS_MT_POSITION_Y] = mSize.bottom - 1; | 
|  | 157 | device->absmin[ABS_MT_TRACKING_ID] = RAW_ID_MIN; | 
|  | 158 | device->absmax[ABS_MT_TRACKING_ID] = RAW_ID_MAX; | 
|  | 159 | } | 
|  | 160 |  | 
|  | 161 | void UinputTouchScreen::sendSlot(int32_t slot) { | 
| Siarhei Vishniakou | e5b5e45 | 2020-04-02 17:59:16 -0700 | [diff] [blame] | 162 | injectEvent(EV_ABS, ABS_MT_SLOT, slot); | 
| Arthur Hung | aab2562 | 2020-01-16 11:22:11 +0800 | [diff] [blame] | 163 | } | 
|  | 164 |  | 
|  | 165 | void UinputTouchScreen::sendTrackingId(int32_t trackingId) { | 
| Siarhei Vishniakou | e5b5e45 | 2020-04-02 17:59:16 -0700 | [diff] [blame] | 166 | injectEvent(EV_ABS, ABS_MT_TRACKING_ID, trackingId); | 
| Arthur Hung | aab2562 | 2020-01-16 11:22:11 +0800 | [diff] [blame] | 167 | } | 
|  | 168 |  | 
|  | 169 | void UinputTouchScreen::sendDown(const Point& point) { | 
| Siarhei Vishniakou | e5b5e45 | 2020-04-02 17:59:16 -0700 | [diff] [blame] | 170 | injectEvent(EV_KEY, BTN_TOUCH, 1); | 
|  | 171 | injectEvent(EV_ABS, ABS_MT_POSITION_X, point.x); | 
|  | 172 | injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y); | 
|  | 173 | injectEvent(EV_SYN, SYN_REPORT, 0); | 
| Arthur Hung | aab2562 | 2020-01-16 11:22:11 +0800 | [diff] [blame] | 174 | } | 
|  | 175 |  | 
|  | 176 | void UinputTouchScreen::sendMove(const Point& point) { | 
| Siarhei Vishniakou | e5b5e45 | 2020-04-02 17:59:16 -0700 | [diff] [blame] | 177 | injectEvent(EV_ABS, ABS_MT_POSITION_X, point.x); | 
|  | 178 | injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y); | 
|  | 179 | injectEvent(EV_SYN, SYN_REPORT, 0); | 
| Arthur Hung | aab2562 | 2020-01-16 11:22:11 +0800 | [diff] [blame] | 180 | } | 
|  | 181 |  | 
|  | 182 | void UinputTouchScreen::sendUp() { | 
|  | 183 | sendTrackingId(0xffffffff); | 
| Siarhei Vishniakou | e5b5e45 | 2020-04-02 17:59:16 -0700 | [diff] [blame] | 184 | injectEvent(EV_KEY, BTN_TOUCH, 0); | 
|  | 185 | injectEvent(EV_SYN, SYN_REPORT, 0); | 
| Arthur Hung | aab2562 | 2020-01-16 11:22:11 +0800 | [diff] [blame] | 186 | } | 
|  | 187 |  | 
|  | 188 | void UinputTouchScreen::sendToolType(int32_t toolType) { | 
| Siarhei Vishniakou | e5b5e45 | 2020-04-02 17:59:16 -0700 | [diff] [blame] | 189 | injectEvent(EV_ABS, ABS_MT_TOOL_TYPE, toolType); | 
|  | 190 | injectEvent(EV_SYN, SYN_REPORT, 0); | 
| Arthur Hung | aab2562 | 2020-01-16 11:22:11 +0800 | [diff] [blame] | 191 | } | 
|  | 192 |  | 
|  | 193 | // Get the center x, y base on the range definition. | 
|  | 194 | const Point UinputTouchScreen::getCenterPoint() { | 
|  | 195 | return Point(mSize.left + mSize.width() / 2, mSize.top + mSize.height() / 2); | 
|  | 196 | } | 
|  | 197 |  | 
| Prabir Pradhan | e0105c9 | 2019-12-26 12:32:13 -0800 | [diff] [blame] | 198 | } // namespace android |