1/* 2 * Copyright (C) 2015 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 "InputHub.h" 18 19#include <chrono> 20#include <memory> 21#include <mutex> 22 23#include <linux/input.h> 24 25#include <gtest/gtest.h> 26 27#include <utils/StopWatch.h> 28#include <utils/Timers.h> 29 30#include "TestHelpers.h" 31 32// # of milliseconds to fudge stopwatch measurements 33#define TIMING_TOLERANCE_MS 25 34#define NO_TIMEOUT (-1) 35 36namespace android { 37namespace tests { 38 39using namespace std::literals::chrono_literals; 40 41using InputCbFunc = std::function<void(const std::shared_ptr<InputDeviceNode>&, InputEvent&, nsecs_t)>; 42using DeviceCbFunc = std::function<void(const std::shared_ptr<InputDeviceNode>&)>; 43 44static const InputCbFunc kNoopInputCb = [](const std::shared_ptr<InputDeviceNode>&, InputEvent&, nsecs_t){}; 45static const DeviceCbFunc kNoopDeviceCb = [](const std::shared_ptr<InputDeviceNode>&){}; 46 47class TestInputCallback : public InputCallbackInterface { 48public: 49 TestInputCallback() : 50 mInputCb(kNoopInputCb), mDeviceAddedCb(kNoopDeviceCb), mDeviceRemovedCb(kNoopDeviceCb) {} 51 virtual ~TestInputCallback() = default; 52 53 void setInputCallback(InputCbFunc cb) { mInputCb = cb; } 54 void setDeviceAddedCallback(DeviceCbFunc cb) { mDeviceAddedCb = cb; } 55 void setDeviceRemovedCallback(DeviceCbFunc cb) { mDeviceRemovedCb = cb; } 56 57 virtual void onInputEvent(const std::shared_ptr<InputDeviceNode>& node, InputEvent& event, 58 nsecs_t event_time) override { 59 mInputCb(node, event, event_time); 60 } 61 virtual void onDeviceAdded(const std::shared_ptr<InputDeviceNode>& node) override { 62 mDeviceAddedCb(node); 63 } 64 virtual void onDeviceRemoved(const std::shared_ptr<InputDeviceNode>& node) override { 65 mDeviceRemovedCb(node); 66 } 67 68private: 69 InputCbFunc mInputCb; 70 DeviceCbFunc mDeviceAddedCb; 71 DeviceCbFunc mDeviceRemovedCb; 72}; 73 74class InputHubTest : public ::testing::Test { 75 protected: 76 virtual void SetUp() { 77 mCallback = std::make_shared<TestInputCallback>(); 78 mInputHub = std::make_shared<InputHub>(mCallback); 79 } 80 81 std::shared_ptr<TestInputCallback> mCallback; 82 std::shared_ptr<InputHub> mInputHub; 83}; 84 85TEST_F(InputHubTest, testWake) { 86 // Call wake() after 100ms. 87 auto f = delay_async(100ms, [&]() { EXPECT_EQ(OK, mInputHub->wake()); }); 88 89 StopWatch stopWatch("poll"); 90 EXPECT_EQ(OK, mInputHub->poll()); 91 int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); 92 93 EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS); 94} 95 96TEST_F(InputHubTest, DISABLED_testDeviceAdded) { 97 auto tempDir = std::make_shared<TempDir>(); 98 std::string pathname; 99 // Expect that this callback will run and set handle and pathname. 100 mCallback->setDeviceAddedCallback( 101 [&](const std::shared_ptr<InputDeviceNode>& node) { 102 pathname = node->getPath(); 103 }); 104 105 ASSERT_EQ(OK, mInputHub->registerDevicePath(tempDir->getName())); 106 107 // Create a new file in tempDir after 100ms. 108 std::unique_ptr<TempFile> tempFile; 109 std::mutex tempFileMutex; 110 auto f = delay_async(100ms, 111 [&]() { 112 std::lock_guard<std::mutex> lock(tempFileMutex); 113 tempFile.reset(tempDir->newTempFile()); 114 }); 115 116 StopWatch stopWatch("poll"); 117 EXPECT_EQ(OK, mInputHub->poll()); 118 int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); 119 120 121 EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS); 122 std::lock_guard<std::mutex> lock(tempFileMutex); 123 EXPECT_EQ(tempFile->getName(), pathname); 124} 125 126TEST_F(InputHubTest, DISABLED_testDeviceRemoved) { 127 // Create a temp dir and file. Save its name and handle (to be filled in 128 // once InputHub scans the dir). 129 auto tempDir = std::make_unique<TempDir>(); 130 auto deviceFile = std::unique_ptr<TempFile>(tempDir->newTempFile()); 131 std::string tempFileName(deviceFile->getName()); 132 133 std::shared_ptr<InputDeviceNode> tempNode; 134 // Expect that these callbacks will run for the above device file. 135 mCallback->setDeviceAddedCallback( 136 [&](const std::shared_ptr<InputDeviceNode>& node) { 137 tempNode = node; 138 }); 139 mCallback->setDeviceRemovedCallback( 140 [&](const std::shared_ptr<InputDeviceNode>& node) { 141 EXPECT_EQ(tempNode, node); 142 }); 143 144 ASSERT_EQ(OK, mInputHub->registerDevicePath(tempDir->getName())); 145 // Ensure that tempDir was scanned to find the device. 146 ASSERT_TRUE(tempNode != nullptr); 147 148 auto f = delay_async(100ms, [&]() { deviceFile.reset(); }); 149 150 StopWatch stopWatch("poll"); 151 EXPECT_EQ(OK, mInputHub->poll()); 152 int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); 153 154 EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS); 155} 156 157TEST_F(InputHubTest, DISABLED_testInputEvent) { 158 // Create a temp dir and file. Save its name and handle (to be filled in 159 // once InputHub scans the dir.) 160 auto tempDir = std::make_unique<TempDir>(); 161 auto deviceFile = std::unique_ptr<TempFile>(tempDir->newTempFile()); 162 std::string tempFileName(deviceFile->getName()); 163 164 // Send a key event corresponding to HOME. 165 struct input_event iev; 166 iev.time = { 1, 0 }; 167 iev.type = EV_KEY; 168 iev.code = KEY_HOME; 169 iev.value = 0x01; 170 171 auto inputDelayMs = 100ms; 172 auto f = delay_async(inputDelayMs, [&] { 173 ssize_t nWrite = TEMP_FAILURE_RETRY(write(deviceFile->getFd(), &iev, sizeof(iev))); 174 175 ASSERT_EQ(static_cast<ssize_t>(sizeof(iev)), nWrite) << "could not write to " 176 << deviceFile->getFd() << ". errno: " << errno; 177 }); 178 179 // Expect this callback to run when the input event is read. 180 nsecs_t expectedWhen = systemTime(CLOCK_MONOTONIC) + ms2ns(inputDelayMs.count()); 181 mCallback->setInputCallback( 182 [&](const std::shared_ptr<InputDeviceNode>& node, InputEvent& event, 183 nsecs_t event_time) { 184 EXPECT_NEAR(expectedWhen, event_time, ms2ns(TIMING_TOLERANCE_MS)); 185 EXPECT_EQ(s2ns(1), event.when); 186 EXPECT_EQ(tempFileName, node->getPath()); 187 EXPECT_EQ(EV_KEY, event.type); 188 EXPECT_EQ(KEY_HOME, event.code); 189 EXPECT_EQ(0x01, event.value); 190 }); 191 ASSERT_EQ(OK, mInputHub->registerDevicePath(tempDir->getName())); 192 193 StopWatch stopWatch("poll"); 194 EXPECT_EQ(OK, mInputHub->poll()); 195 int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); 196 197 EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS); 198} 199 200TEST_F(InputHubTest, DISABLED_testCallbackOrder) { 201 // Create two "devices": one to receive input and the other to go away. 202 auto tempDir = std::make_unique<TempDir>(); 203 auto deviceFile1 = std::unique_ptr<TempFile>(tempDir->newTempFile()); 204 auto deviceFile2 = std::unique_ptr<TempFile>(tempDir->newTempFile()); 205 std::string tempFileName(deviceFile2->getName()); 206 207 bool inputCallbackFinished = false, deviceCallbackFinished = false; 208 209 // Setup the callback for input events. Should run before the device 210 // callback. 211 mCallback->setInputCallback( 212 [&](const std::shared_ptr<InputDeviceNode>&, InputEvent&, nsecs_t) { 213 ASSERT_FALSE(deviceCallbackFinished); 214 inputCallbackFinished = true; 215 }); 216 217 // Setup the callback for device removal. Should run after the input 218 // callback. 219 mCallback->setDeviceRemovedCallback( 220 [&](const std::shared_ptr<InputDeviceNode>& node) { 221 ASSERT_TRUE(inputCallbackFinished) 222 << "input callback did not run before device changed callback"; 223 // Make sure the correct device was removed. 224 EXPECT_EQ(tempFileName, node->getPath()); 225 deviceCallbackFinished = true; 226 }); 227 ASSERT_EQ(OK, mInputHub->registerDevicePath(tempDir->getName())); 228 229 auto f = delay_async(100ms, 230 [&]() { 231 // Delete the second device file first. 232 deviceFile2.reset(); 233 234 // Then inject an input event into the first device. 235 struct input_event iev; 236 iev.time = { 1, 0 }; 237 iev.type = EV_KEY; 238 iev.code = KEY_HOME; 239 iev.value = 0x01; 240 241 ssize_t nWrite = TEMP_FAILURE_RETRY(write(deviceFile1->getFd(), &iev, sizeof(iev))); 242 243 ASSERT_EQ(static_cast<ssize_t>(sizeof(iev)), nWrite) << "could not write to " 244 << deviceFile1->getFd() << ". errno: " << errno; 245 }); 246 247 StopWatch stopWatch("poll"); 248 EXPECT_EQ(OK, mInputHub->poll()); 249 int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); 250 251 EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS); 252 EXPECT_TRUE(inputCallbackFinished); 253 EXPECT_TRUE(deviceCallbackFinished); 254} 255 256} // namespace tests 257} // namespace android 258