Home | History | Annotate | Download | only in evdev
      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 
     44 static const InputCbFunc kNoopInputCb = [](const std::shared_ptr<InputDeviceNode>&, InputEvent&, nsecs_t){};
     45 static const DeviceCbFunc kNoopDeviceCb = [](const std::shared_ptr<InputDeviceNode>&){};
     46 
     47 class TestInputCallback : public InputCallbackInterface {
     48 public:
     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 
     68 private:
     69     InputCbFunc mInputCb;
     70     DeviceCbFunc mDeviceAddedCb;
     71     DeviceCbFunc mDeviceRemovedCb;
     72 };
     73 
     74 class 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 
     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 
     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 
    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 
    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 
    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