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
36 namespace android {
37 namespace tests {
38
39 using namespace std::literals::chrono_literals;
40
41 using InputCbFunc = std::function<void(const std::shared_ptr<InputDeviceNode>&, InputEvent&, nsecs_t)>;
42 using DeviceCbFunc = std::function<void(const std::shared_ptr<InputDeviceNode>&)>;
43
__anon5f3141d90102(const std::shared_ptr<InputDeviceNode>&, InputEvent&, nsecs_t)44 static const InputCbFunc kNoopInputCb = [](const std::shared_ptr<InputDeviceNode>&, InputEvent&, nsecs_t){};
__anon5f3141d90202(const std::shared_ptr<InputDeviceNode>&)45 static const DeviceCbFunc kNoopDeviceCb = [](const std::shared_ptr<InputDeviceNode>&){};
46
47 class TestInputCallback : public InputCallbackInterface {
48 public:
TestInputCallback()49 TestInputCallback() :
50 mInputCb(kNoopInputCb), mDeviceAddedCb(kNoopDeviceCb), mDeviceRemovedCb(kNoopDeviceCb) {}
51 virtual ~TestInputCallback() = default;
52
setInputCallback(const InputCbFunc & cb)53 void setInputCallback(const InputCbFunc& cb) { mInputCb = cb; }
setDeviceAddedCallback(const DeviceCbFunc & cb)54 void setDeviceAddedCallback(const DeviceCbFunc& cb) { mDeviceAddedCb = cb; }
setDeviceRemovedCallback(const DeviceCbFunc & cb)55 void setDeviceRemovedCallback(const DeviceCbFunc& cb) { mDeviceRemovedCb = cb; }
56
onInputEvent(const std::shared_ptr<InputDeviceNode> & node,InputEvent & event,nsecs_t event_time)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 }
onDeviceAdded(const std::shared_ptr<InputDeviceNode> & node)61 virtual void onDeviceAdded(const std::shared_ptr<InputDeviceNode>& node) override {
62 mDeviceAddedCb(node);
63 }
onDeviceRemoved(const std::shared_ptr<InputDeviceNode> & node)64 virtual void onDeviceRemoved(const std::shared_ptr<InputDeviceNode>& node) override {
65 mDeviceRemovedCb(node);
66 }
67
68 private:
69 InputCbFunc mInputCb;
70 DeviceCbFunc mDeviceAddedCb;
71 DeviceCbFunc mDeviceRemovedCb;
72 };
73
74 class InputHubTest : public ::testing::Test {
75 protected:
SetUp()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
TEST_F(InputHubTest,testWake)85 TEST_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
TEST_F(InputHubTest,DISABLED_testDeviceAdded)96 TEST_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
TEST_F(InputHubTest,DISABLED_testDeviceRemoved)126 TEST_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
TEST_F(InputHubTest,DISABLED_testInputEvent)157 TEST_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
TEST_F(InputHubTest,DISABLED_testCallbackOrder)200 TEST_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