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