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