| /* |
| * Copyright 2023 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "KeyboardInputMapper.h" |
| |
| #include <cstdint> |
| #include <list> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| |
| #include <android/input.h> |
| #include <android/keycodes.h> |
| #include <com_android_input_flags.h> |
| #include <flag_macros.h> |
| #include <ftl/flags.h> |
| #include <gtest/gtest.h> |
| #include <input/DisplayViewport.h> |
| #include <input/Input.h> |
| #include <input/InputDevice.h> |
| #include <ui/LogicalDisplayId.h> |
| #include <ui/Rotation.h> |
| #include <utils/Errors.h> |
| |
| #include "EventHub.h" |
| #include "InputMapperTest.h" |
| #include "InterfaceMocks.h" |
| #include "NotifyArgs.h" |
| #include "TestConstants.h" |
| #include "TestEventMatchers.h" |
| |
| #define TAG "KeyboardInputMapper_test" |
| |
| namespace android { |
| |
| using namespace ftl::flag_operators; |
| using testing::_; |
| using testing::AllOf; |
| using testing::AnyOf; |
| using testing::Args; |
| using testing::DoAll; |
| using testing::IsEmpty; |
| using testing::Return; |
| using testing::ReturnArg; |
| using testing::SaveArg; |
| using testing::SetArgPointee; |
| using testing::VariantWith; |
| |
| namespace { |
| |
| // Arbitrary display properties. |
| constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId::DEFAULT; |
| constexpr int32_t DISPLAY_WIDTH = 480; |
| constexpr int32_t DISPLAY_HEIGHT = 800; |
| |
| DisplayViewport createPrimaryViewport(ui::Rotation orientation) { |
| const bool isRotated = |
| orientation == ui::Rotation::Rotation90 || orientation == ui::Rotation::Rotation270; |
| DisplayViewport v; |
| v.displayId = DISPLAY_ID; |
| v.orientation = orientation; |
| v.logicalRight = isRotated ? DISPLAY_HEIGHT : DISPLAY_WIDTH; |
| v.logicalBottom = isRotated ? DISPLAY_WIDTH : DISPLAY_HEIGHT; |
| v.physicalRight = isRotated ? DISPLAY_HEIGHT : DISPLAY_WIDTH; |
| v.physicalBottom = isRotated ? DISPLAY_WIDTH : DISPLAY_HEIGHT; |
| v.deviceWidth = isRotated ? DISPLAY_HEIGHT : DISPLAY_WIDTH; |
| v.deviceHeight = isRotated ? DISPLAY_WIDTH : DISPLAY_HEIGHT; |
| v.isActive = true; |
| v.uniqueId = "local:1"; |
| return v; |
| } |
| |
| } // namespace |
| |
| /** |
| * Unit tests for KeyboardInputMapper. |
| */ |
| class KeyboardInputMapperUnitTest : public InputMapperUnitTest { |
| protected: |
| const KeyboardLayoutInfo DEVICE_KEYBOARD_LAYOUT_INFO = KeyboardLayoutInfo("en-US", "qwerty"); |
| |
| sp<FakeInputReaderPolicy> mFakePolicy; |
| const std::unordered_map<int32_t, int32_t> mKeyCodeMap{{KEY_0, AKEYCODE_0}, |
| {KEY_A, AKEYCODE_A}, |
| {KEY_LEFTCTRL, AKEYCODE_CTRL_LEFT}, |
| {KEY_LEFTALT, AKEYCODE_ALT_LEFT}, |
| {KEY_RIGHTALT, AKEYCODE_ALT_RIGHT}, |
| {KEY_LEFTSHIFT, AKEYCODE_SHIFT_LEFT}, |
| {KEY_RIGHTSHIFT, AKEYCODE_SHIFT_RIGHT}, |
| {KEY_FN, AKEYCODE_FUNCTION}, |
| {KEY_LEFTCTRL, AKEYCODE_CTRL_LEFT}, |
| {KEY_RIGHTCTRL, AKEYCODE_CTRL_RIGHT}, |
| {KEY_LEFTMETA, AKEYCODE_META_LEFT}, |
| {KEY_RIGHTMETA, AKEYCODE_META_RIGHT}, |
| {KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK}, |
| {KEY_NUMLOCK, AKEYCODE_NUM_LOCK}, |
| {KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK}}; |
| |
| void SetUp() override { |
| InputMapperUnitTest::SetUp(); |
| |
| // set key-codes expected in tests |
| for (const auto& [evdevCode, outKeycode] : mKeyCodeMap) { |
| addKeyByEvdevCode(evdevCode, outKeycode); |
| } |
| |
| mFakePolicy = sp<FakeInputReaderPolicy>::make(); |
| EXPECT_CALL(mMockInputReaderContext, getPolicy).WillRepeatedly(Return(mFakePolicy.get())); |
| |
| ON_CALL((*mDevice), getSources).WillByDefault(Return(AINPUT_SOURCE_KEYBOARD)); |
| |
| mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration, |
| AINPUT_SOURCE_KEYBOARD); |
| } |
| |
| void addKeyByEvdevCode(int32_t evdevCode, int32_t keyCode, int32_t flags = 0) { |
| EXPECT_CALL(mMockEventHub, mapKey(EVENTHUB_ID, evdevCode, _, _, _, _, _)) |
| .WillRepeatedly([=](int32_t, int32_t, int32_t, int32_t metaState, |
| int32_t* outKeycode, int32_t* outMetaState, |
| uint32_t* outFlags) { |
| if (outKeycode != nullptr) { |
| *outKeycode = keyCode; |
| } |
| if (outMetaState != nullptr) { |
| *outMetaState = metaState; |
| } |
| if (outFlags != nullptr) { |
| *outFlags = flags; |
| } |
| return NO_ERROR; |
| }); |
| } |
| |
| void addKeyByUsageCode(int32_t usageCode, int32_t keyCode, int32_t flags = 0) { |
| EXPECT_CALL(mMockEventHub, mapKey(EVENTHUB_ID, _, usageCode, _, _, _, _)) |
| .WillRepeatedly([=](int32_t, int32_t, int32_t, int32_t metaState, |
| int32_t* outKeycode, int32_t* outMetaState, |
| uint32_t* outFlags) { |
| if (outKeycode != nullptr) { |
| *outKeycode = keyCode; |
| } |
| if (outMetaState != nullptr) { |
| *outMetaState = metaState; |
| } |
| if (outFlags != nullptr) { |
| *outFlags = flags; |
| } |
| return NO_ERROR; |
| }); |
| } |
| |
| void setDisplayOrientation(ui::Rotation orientation) { |
| EXPECT_CALL((*mDevice), getAssociatedViewport) |
| .WillRepeatedly(Return(createPrimaryViewport(orientation))); |
| std::list<NotifyArgs> args = |
| mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, |
| InputReaderConfiguration::Change::DISPLAY_INFO); |
| ASSERT_EQ(0u, args.size()); |
| } |
| |
| NotifyKeyArgs expectSingleKeyArg(const std::list<NotifyArgs>& args) { |
| EXPECT_EQ(1u, args.size()); |
| return std::get<NotifyKeyArgs>(args.front()); |
| } |
| |
| void testDPadKeyRotation(int32_t originalEvdevCode, int32_t originalKeyCode, |
| int32_t rotatedKeyCode, ui::LogicalDisplayId displayId = DISPLAY_ID) { |
| std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, originalEvdevCode, 1); |
| NotifyKeyArgs args = expectSingleKeyArg(argsList); |
| ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); |
| ASSERT_EQ(originalEvdevCode, args.scanCode); |
| ASSERT_EQ(rotatedKeyCode, args.keyCode); |
| ASSERT_EQ(displayId, args.displayId); |
| |
| argsList = process(ARBITRARY_TIME, EV_KEY, originalEvdevCode, 0); |
| args = expectSingleKeyArg(argsList); |
| ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); |
| ASSERT_EQ(originalEvdevCode, args.scanCode); |
| ASSERT_EQ(rotatedKeyCode, args.keyCode); |
| ASSERT_EQ(displayId, args.displayId); |
| } |
| }; |
| |
| TEST_F(KeyboardInputMapperUnitTest, GetSources) { |
| ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, mMapper->getSources()); |
| } |
| |
| TEST_F(KeyboardInputMapperUnitTest, KeyPressTimestampRecorded) { |
| nsecs_t when = ARBITRARY_TIME; |
| std::vector<int32_t> keyCodes{KEY_0, KEY_A, KEY_LEFTCTRL, KEY_RIGHTALT, KEY_LEFTSHIFT}; |
| EXPECT_CALL(mMockInputReaderContext, setLastKeyDownTimestamp) |
| .With(Args<0>(when)) |
| .Times(keyCodes.size()); |
| for (int32_t keyCode : keyCodes) { |
| process(when, EV_KEY, keyCode, 1); |
| process(when, EV_SYN, SYN_REPORT, 0); |
| process(when, EV_KEY, keyCode, 0); |
| process(when, EV_SYN, SYN_REPORT, 0); |
| } |
| } |
| |
| TEST_F(KeyboardInputMapperUnitTest, RepeatEventsDiscarded) { |
| std::list<NotifyArgs> args; |
| args += process(ARBITRARY_TIME, EV_KEY, KEY_0, 1); |
| args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); |
| |
| args += process(ARBITRARY_TIME, EV_KEY, KEY_0, 2); |
| args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); |
| |
| args += process(ARBITRARY_TIME, EV_KEY, KEY_0, 0); |
| args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); |
| |
| EXPECT_THAT(args, |
| ElementsAre(VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), |
| WithKeyCode(AKEYCODE_0), |
| WithScanCode(KEY_0))), |
| VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), |
| WithKeyCode(AKEYCODE_0), |
| WithScanCode(KEY_0))))); |
| } |
| |
| TEST_F(KeyboardInputMapperUnitTest, Process_SimpleKeyPress) { |
| const int32_t USAGE_A = 0x070004; |
| addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME, POLICY_FLAG_WAKE); |
| addKeyByUsageCode(USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE); |
| |
| // Initial metastate is AMETA_NONE. |
| ASSERT_EQ(AMETA_NONE, mMapper->getMetaState()); |
| |
| // Key down by evdev code. |
| std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1); |
| NotifyKeyArgs args = expectSingleKeyArg(argsList); |
| ASSERT_EQ(DEVICE_ID, args.deviceId); |
| ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); |
| ASSERT_EQ(ARBITRARY_TIME, args.eventTime); |
| ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); |
| ASSERT_EQ(AKEYCODE_HOME, args.keyCode); |
| ASSERT_EQ(KEY_HOME, args.scanCode); |
| ASSERT_EQ(AMETA_NONE, args.metaState); |
| ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); |
| ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); |
| ASSERT_EQ(ARBITRARY_TIME, args.downTime); |
| |
| // Key up by evdev code. |
| argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0); |
| args = expectSingleKeyArg(argsList); |
| ASSERT_EQ(DEVICE_ID, args.deviceId); |
| ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); |
| ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime); |
| ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); |
| ASSERT_EQ(AKEYCODE_HOME, args.keyCode); |
| ASSERT_EQ(KEY_HOME, args.scanCode); |
| ASSERT_EQ(AMETA_NONE, args.metaState); |
| ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); |
| ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); |
| ASSERT_EQ(ARBITRARY_TIME, args.downTime); |
| |
| // Key down by usage code. |
| argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A); |
| argsList += process(ARBITRARY_TIME, EV_KEY, 0, 1); |
| args = expectSingleKeyArg(argsList); |
| ASSERT_EQ(DEVICE_ID, args.deviceId); |
| ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); |
| ASSERT_EQ(ARBITRARY_TIME, args.eventTime); |
| ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); |
| ASSERT_EQ(AKEYCODE_A, args.keyCode); |
| ASSERT_EQ(0, args.scanCode); |
| ASSERT_EQ(AMETA_NONE, args.metaState); |
| ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); |
| ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); |
| ASSERT_EQ(ARBITRARY_TIME, args.downTime); |
| |
| // Key up by usage code. |
| argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A); |
| argsList += process(ARBITRARY_TIME + 1, EV_KEY, 0, 0); |
| args = expectSingleKeyArg(argsList); |
| ASSERT_EQ(DEVICE_ID, args.deviceId); |
| ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); |
| ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime); |
| ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); |
| ASSERT_EQ(AKEYCODE_A, args.keyCode); |
| ASSERT_EQ(0, args.scanCode); |
| ASSERT_EQ(AMETA_NONE, args.metaState); |
| ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); |
| ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); |
| ASSERT_EQ(ARBITRARY_TIME, args.downTime); |
| } |
| |
| TEST_F(KeyboardInputMapperUnitTest, Process_UnknownKey) { |
| const int32_t USAGE_UNKNOWN = 0x07ffff; |
| EXPECT_CALL(mMockEventHub, mapKey(EVENTHUB_ID, KEY_UNKNOWN, USAGE_UNKNOWN, _, _, _, _)) |
| .WillRepeatedly(Return(NAME_NOT_FOUND)); |
| |
| // Key down with unknown scan code or usage code. |
| std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN); |
| argsList += process(ARBITRARY_TIME, EV_KEY, KEY_UNKNOWN, 1); |
| NotifyKeyArgs args = expectSingleKeyArg(argsList); |
| ASSERT_EQ(DEVICE_ID, args.deviceId); |
| ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); |
| ASSERT_EQ(ARBITRARY_TIME, args.eventTime); |
| ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); |
| ASSERT_EQ(0, args.keyCode); |
| ASSERT_EQ(KEY_UNKNOWN, args.scanCode); |
| ASSERT_EQ(AMETA_NONE, args.metaState); |
| ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); |
| ASSERT_EQ(0U, args.policyFlags); |
| ASSERT_EQ(ARBITRARY_TIME, args.downTime); |
| |
| // Key up with unknown scan code or usage code. |
| argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN); |
| argsList += process(ARBITRARY_TIME + 1, EV_KEY, KEY_UNKNOWN, 0); |
| args = expectSingleKeyArg(argsList); |
| ASSERT_EQ(DEVICE_ID, args.deviceId); |
| ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); |
| ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime); |
| ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); |
| ASSERT_EQ(0, args.keyCode); |
| ASSERT_EQ(KEY_UNKNOWN, args.scanCode); |
| ASSERT_EQ(AMETA_NONE, args.metaState); |
| ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); |
| ASSERT_EQ(0U, args.policyFlags); |
| ASSERT_EQ(ARBITRARY_TIME, args.downTime); |
| } |
| |
| /** |
| * Ensure that the readTime is set to the time when the EV_KEY is received. |
| */ |
| TEST_F(KeyboardInputMapperUnitTest, Process_SendsReadTime) { |
| addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME); |
| |
| // Key down |
| std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, /*readTime=*/12, EV_KEY, KEY_HOME, 1); |
| ASSERT_EQ(12, expectSingleKeyArg(argsList).readTime); |
| |
| // Key up |
| argsList = process(ARBITRARY_TIME, /*readTime=*/15, EV_KEY, KEY_HOME, 1); |
| ASSERT_EQ(15, expectSingleKeyArg(argsList).readTime); |
| } |
| |
| TEST_F(KeyboardInputMapperUnitTest, Process_ShouldUpdateMetaState) { |
| addKeyByEvdevCode(KEY_LEFTSHIFT, AKEYCODE_SHIFT_LEFT); |
| addKeyByEvdevCode(KEY_A, AKEYCODE_A); |
| |
| EXPECT_CALL(mMockInputReaderContext, updateGlobalMetaState()).Times(2); |
| |
| // Initial metastate is AMETA_NONE. |
| ASSERT_EQ(AMETA_NONE, mMapper->getMetaState()); |
| |
| // Metakey down. |
| std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_LEFTSHIFT, 1); |
| ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, expectSingleKeyArg(argsList).metaState); |
| ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mMapper->getMetaState()); |
| |
| // Key down. |
| argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_A, 1); |
| ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, expectSingleKeyArg(argsList).metaState); |
| ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mMapper->getMetaState()); |
| |
| // Key up. |
| argsList = process(ARBITRARY_TIME + 2, EV_KEY, KEY_A, 0); |
| ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, expectSingleKeyArg(argsList).metaState); |
| ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mMapper->getMetaState()); |
| |
| // Metakey up. |
| argsList = process(ARBITRARY_TIME + 3, EV_KEY, KEY_LEFTSHIFT, 0); |
| ASSERT_EQ(AMETA_NONE, expectSingleKeyArg(argsList).metaState); |
| ASSERT_EQ(AMETA_NONE, mMapper->getMetaState()); |
| } |
| |
| TEST_F(KeyboardInputMapperUnitTest, Process_WhenNotOrientationAware_ShouldNotRotateDPad) { |
| addKeyByEvdevCode(KEY_UP, AKEYCODE_DPAD_UP); |
| addKeyByEvdevCode(KEY_RIGHT, AKEYCODE_DPAD_RIGHT); |
| addKeyByEvdevCode(KEY_DOWN, AKEYCODE_DPAD_DOWN); |
| addKeyByEvdevCode(KEY_LEFT, AKEYCODE_DPAD_LEFT); |
| |
| setDisplayOrientation(ui::Rotation::Rotation90); |
| ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP)); |
| ASSERT_NO_FATAL_FAILURE( |
| testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT)); |
| ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN)); |
| ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT)); |
| } |
| |
| TEST_F(KeyboardInputMapperUnitTest, Process_WhenOrientationAware_ShouldRotateDPad) { |
| addKeyByEvdevCode(KEY_UP, AKEYCODE_DPAD_UP); |
| addKeyByEvdevCode(KEY_RIGHT, AKEYCODE_DPAD_RIGHT); |
| addKeyByEvdevCode(KEY_DOWN, AKEYCODE_DPAD_DOWN); |
| addKeyByEvdevCode(KEY_LEFT, AKEYCODE_DPAD_LEFT); |
| |
| mPropertyMap.addProperty("keyboard.orientationAware", "1"); |
| mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration, |
| AINPUT_SOURCE_KEYBOARD); |
| setDisplayOrientation(ui::ROTATION_0); |
| |
| ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP)); |
| ASSERT_NO_FATAL_FAILURE( |
| testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT)); |
| ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN)); |
| ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT)); |
| |
| setDisplayOrientation(ui::ROTATION_90); |
| ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT)); |
| ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP)); |
| ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT)); |
| ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN)); |
| |
| setDisplayOrientation(ui::ROTATION_180); |
| ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN)); |
| ASSERT_NO_FATAL_FAILURE( |
| testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_LEFT)); |
| ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_UP)); |
| ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT)); |
| |
| setDisplayOrientation(ui::ROTATION_270); |
| ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT)); |
| ASSERT_NO_FATAL_FAILURE( |
| testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_DOWN)); |
| ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_LEFT)); |
| ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_UP)); |
| |
| // Special case: if orientation changes while key is down, we still emit the same keycode |
| // in the key up as we did in the key down. |
| setDisplayOrientation(ui::ROTATION_270); |
| std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1); |
| NotifyKeyArgs args = expectSingleKeyArg(argsList); |
| ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); |
| ASSERT_EQ(KEY_UP, args.scanCode); |
| ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode); |
| |
| setDisplayOrientation(ui::ROTATION_180); |
| argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0); |
| args = expectSingleKeyArg(argsList); |
| ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); |
| ASSERT_EQ(KEY_UP, args.scanCode); |
| ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode); |
| } |
| |
| TEST_F(KeyboardInputMapperUnitTest, DisplayIdConfigurationChange_NotOrientationAware) { |
| // If the keyboard is not orientation aware, |
| // key events should not be associated with a specific display id |
| addKeyByEvdevCode(KEY_UP, AKEYCODE_DPAD_UP); |
| |
| // Display id should be LogicalDisplayId::INVALID without any display configuration. |
| std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1); |
| ASSERT_GT(argsList.size(), 0u); |
| argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0); |
| ASSERT_GT(argsList.size(), 0u); |
| ASSERT_EQ(ui::LogicalDisplayId::INVALID, std::get<NotifyKeyArgs>(argsList.front()).displayId); |
| } |
| |
| TEST_F(KeyboardInputMapperUnitTest, DisplayIdConfigurationChange_OrientationAware) { |
| // If the keyboard is orientation aware, |
| // key events should be associated with the internal viewport |
| addKeyByEvdevCode(KEY_UP, AKEYCODE_DPAD_UP); |
| |
| mPropertyMap.addProperty("keyboard.orientationAware", "1"); |
| mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration, |
| AINPUT_SOURCE_KEYBOARD); |
| |
| // Display id should be LogicalDisplayId::INVALID without any display configuration. |
| // ^--- already checked by the previous test |
| |
| setDisplayOrientation(ui::ROTATION_0); |
| std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1); |
| ASSERT_GT(argsList.size(), 0u); |
| argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0); |
| ASSERT_GT(argsList.size(), 0u); |
| ASSERT_EQ(DISPLAY_ID, std::get<NotifyKeyArgs>(argsList.front()).displayId); |
| |
| constexpr ui::LogicalDisplayId newDisplayId = ui::LogicalDisplayId{2}; |
| DisplayViewport newViewport = createPrimaryViewport(ui::ROTATION_0); |
| newViewport.displayId = newDisplayId; |
| EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(newViewport)); |
| argsList = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, |
| InputReaderConfiguration::Change::DISPLAY_INFO); |
| ASSERT_EQ(0u, argsList.size()); |
| argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1); |
| ASSERT_GT(argsList.size(), 0u); |
| argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0); |
| ASSERT_GT(argsList.size(), 0u); |
| ASSERT_EQ(newDisplayId, std::get<NotifyKeyArgs>(argsList.front()).displayId); |
| } |
| |
| TEST_F(KeyboardInputMapperUnitTest, GetKeyCodeState) { |
| EXPECT_CALL(mMockEventHub, getKeyCodeState(EVENTHUB_ID, AKEYCODE_A)) |
| .WillRepeatedly(Return(AKEY_STATE_DOWN)); |
| ASSERT_EQ(AKEY_STATE_DOWN, mMapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A)); |
| |
| EXPECT_CALL(mMockEventHub, getKeyCodeState(EVENTHUB_ID, AKEYCODE_A)) |
| .WillRepeatedly(Return(AKEY_STATE_UP)); |
| ASSERT_EQ(AKEY_STATE_UP, mMapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A)); |
| } |
| |
| TEST_F(KeyboardInputMapperUnitTest, GetKeyCodeForKeyLocation) { |
| EXPECT_CALL(mMockEventHub, getKeyCodeForKeyLocation(EVENTHUB_ID, _)) |
| .WillRepeatedly(ReturnArg<1>()); |
| EXPECT_CALL(mMockEventHub, getKeyCodeForKeyLocation(EVENTHUB_ID, AKEYCODE_Y)) |
| .WillRepeatedly(Return(AKEYCODE_Z)); |
| ASSERT_EQ(AKEYCODE_Z, mMapper->getKeyCodeForKeyLocation(AKEYCODE_Y)) |
| << "If a mapping is available, the result is equal to the mapping"; |
| |
| ASSERT_EQ(AKEYCODE_A, mMapper->getKeyCodeForKeyLocation(AKEYCODE_A)) |
| << "If no mapping is available, the result is the key location"; |
| } |
| |
| TEST_F(KeyboardInputMapperUnitTest, GetScanCodeState) { |
| EXPECT_CALL(mMockEventHub, getScanCodeState(EVENTHUB_ID, KEY_A)) |
| .WillRepeatedly(Return(AKEY_STATE_DOWN)); |
| ASSERT_EQ(AKEY_STATE_DOWN, mMapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A)); |
| |
| EXPECT_CALL(mMockEventHub, getScanCodeState(EVENTHUB_ID, KEY_A)) |
| .WillRepeatedly(Return(AKEY_STATE_UP)); |
| ASSERT_EQ(AKEY_STATE_UP, mMapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A)); |
| } |
| |
| TEST_F(KeyboardInputMapperUnitTest, Process_LockedKeysShouldToggleMetaStateAndLeds) { |
| EXPECT_CALL(mMockEventHub, |
| hasLed(EVENTHUB_ID, AnyOf(LED_CAPSL, LED_NUML, LED_SCROLLL /*NOTYPO*/))) |
| .WillRepeatedly(Return(true)); |
| bool capsLockLed = true; // Initially on |
| bool numLockLed = false; // Initially off |
| bool scrollLockLed = false; // Initially off |
| EXPECT_CALL(mMockEventHub, setLedState(EVENTHUB_ID, LED_CAPSL, _)) |
| .WillRepeatedly(SaveArg<2>(&capsLockLed)); |
| EXPECT_CALL(mMockEventHub, setLedState(EVENTHUB_ID, LED_NUML, _)) |
| .WillRepeatedly(SaveArg<2>(&numLockLed)); |
| EXPECT_CALL(mMockEventHub, setLedState(EVENTHUB_ID, LED_SCROLLL /*NOTYPO*/, _)) |
| .WillRepeatedly(SaveArg<2>(&scrollLockLed)); |
| addKeyByEvdevCode(KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK); |
| addKeyByEvdevCode(KEY_NUMLOCK, AKEYCODE_NUM_LOCK); |
| addKeyByEvdevCode(KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK); |
| |
| // In real operation, mappers pass new LED states to InputReader (via the context), which then |
| // calls back to the mappers to apply that state. Mimic the same thing here with mocks. |
| int32_t ledMetaState; |
| EXPECT_CALL(mMockInputReaderContext, updateLedMetaState(_)) |
| .WillRepeatedly([&](int32_t newState) { |
| ledMetaState = newState; |
| mMapper->updateLedState(false); |
| }); |
| EXPECT_CALL(mMockInputReaderContext, getLedMetaState()) |
| .WillRepeatedly(testing::ReturnPointee(&ledMetaState)); |
| |
| ASSERT_THAT(mMapper->reset(ARBITRARY_TIME), IsEmpty()); |
| |
| // Initial metastate is AMETA_NONE. |
| ASSERT_EQ(AMETA_NONE, mMapper->getMetaState()); |
| |
| // Initialization should have turned all of the lights off. |
| ASSERT_FALSE(capsLockLed); |
| ASSERT_FALSE(numLockLed); |
| ASSERT_FALSE(scrollLockLed); |
| |
| // Toggle caps lock on. |
| std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1); |
| argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0); |
| ASSERT_TRUE(capsLockLed); |
| ASSERT_FALSE(numLockLed); |
| ASSERT_FALSE(scrollLockLed); |
| ASSERT_EQ(AMETA_CAPS_LOCK_ON, mMapper->getMetaState()); |
| |
| // Toggle num lock on. |
| argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1); |
| argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0); |
| ASSERT_TRUE(capsLockLed); |
| ASSERT_TRUE(numLockLed); |
| ASSERT_FALSE(scrollLockLed); |
| ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mMapper->getMetaState()); |
| |
| // Toggle caps lock off. |
| argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1); |
| argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0); |
| ASSERT_FALSE(capsLockLed); |
| ASSERT_TRUE(numLockLed); |
| ASSERT_FALSE(scrollLockLed); |
| ASSERT_EQ(AMETA_NUM_LOCK_ON, mMapper->getMetaState()); |
| |
| // Toggle scroll lock on. |
| argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1); |
| argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0); |
| ASSERT_FALSE(capsLockLed); |
| ASSERT_TRUE(numLockLed); |
| ASSERT_TRUE(scrollLockLed); |
| ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mMapper->getMetaState()); |
| |
| // Toggle num lock off. |
| argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1); |
| argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0); |
| ASSERT_FALSE(capsLockLed); |
| ASSERT_FALSE(numLockLed); |
| ASSERT_TRUE(scrollLockLed); |
| ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mMapper->getMetaState()); |
| |
| // Toggle scroll lock off. |
| argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1); |
| argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0); |
| ASSERT_FALSE(capsLockLed); |
| ASSERT_FALSE(numLockLed); |
| ASSERT_FALSE(scrollLockLed); |
| ASSERT_EQ(AMETA_NONE, mMapper->getMetaState()); |
| } |
| |
| TEST_F(KeyboardInputMapperUnitTest, DisablingDeviceResetsPressedKeys) { |
| const int32_t USAGE_A = 0x070004; |
| addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME, POLICY_FLAG_WAKE); |
| addKeyByUsageCode(USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE); |
| |
| // Key down by scan code. |
| std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1); |
| NotifyKeyArgs args = expectSingleKeyArg(argsList); |
| ASSERT_EQ(DEVICE_ID, args.deviceId); |
| ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); |
| ASSERT_EQ(ARBITRARY_TIME, args.eventTime); |
| ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); |
| ASSERT_EQ(AKEYCODE_HOME, args.keyCode); |
| ASSERT_EQ(KEY_HOME, args.scanCode); |
| ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); |
| |
| // Disable device, it should synthesize cancellation events for down events. |
| mReaderConfiguration.disabledDevices.insert(DEVICE_ID); |
| argsList = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, |
| InputReaderConfiguration::Change::ENABLED_STATE); |
| argsList += mMapper->reset(ARBITRARY_TIME); |
| args = expectSingleKeyArg(argsList); |
| ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); |
| ASSERT_EQ(AKEYCODE_HOME, args.keyCode); |
| ASSERT_EQ(KEY_HOME, args.scanCode); |
| ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED, args.flags); |
| } |
| |
| TEST_F(KeyboardInputMapperUnitTest, Configure_AssignKeyboardLayoutInfo) { |
| std::list<NotifyArgs> unused = |
| mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, /*changes=*/{}); |
| |
| int32_t generation = mDevice->getGeneration(); |
| mReaderConfiguration.keyboardLayoutAssociations.insert( |
| {mIdentifier.location, DEVICE_KEYBOARD_LAYOUT_INFO}); |
| |
| unused += mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, |
| InputReaderConfiguration::Change::KEYBOARD_LAYOUT_ASSOCIATION); |
| |
| InputDeviceInfo deviceInfo; |
| mMapper->populateDeviceInfo(deviceInfo); |
| ASSERT_EQ(DEVICE_KEYBOARD_LAYOUT_INFO.languageTag, |
| deviceInfo.getKeyboardLayoutInfo()->languageTag); |
| ASSERT_EQ(DEVICE_KEYBOARD_LAYOUT_INFO.layoutType, |
| deviceInfo.getKeyboardLayoutInfo()->layoutType); |
| ASSERT_GT(mDevice->getGeneration(), generation); |
| |
| // Call change layout association with the same values: Generation shouldn't change |
| generation = mDevice->getGeneration(); |
| unused += mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, |
| InputReaderConfiguration::Change::KEYBOARD_LAYOUT_ASSOCIATION); |
| ASSERT_EQ(mDevice->getGeneration(), generation); |
| } |
| |
| TEST_F(KeyboardInputMapperUnitTest, LayoutInfoCorrectlyMapped) { |
| EXPECT_CALL(mMockEventHub, getRawLayoutInfo(EVENTHUB_ID)) |
| .WillRepeatedly(Return(RawLayoutInfo{.languageTag = "en", .layoutType = "extended"})); |
| |
| // Configuration |
| std::list<NotifyArgs> unused = |
| mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, /*changes=*/{}); |
| |
| InputDeviceInfo deviceInfo; |
| mMapper->populateDeviceInfo(deviceInfo); |
| ASSERT_EQ("en", deviceInfo.getKeyboardLayoutInfo()->languageTag); |
| ASSERT_EQ("extended", deviceInfo.getKeyboardLayoutInfo()->layoutType); |
| } |
| |
| TEST_F(KeyboardInputMapperUnitTest, Process_GestureEventToSetFlagKeepTouchMode) { |
| addKeyByEvdevCode(KEY_LEFT, AKEYCODE_DPAD_LEFT, POLICY_FLAG_GESTURE); |
| |
| // Key down |
| std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_LEFT, 1); |
| ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_KEEP_TOUCH_MODE, |
| expectSingleKeyArg(argsList).flags); |
| } |
| |
| TEST_F_WITH_FLAGS(KeyboardInputMapperUnitTest, WakeBehavior_AlphabeticKeyboard, |
| REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, |
| enable_alphabetic_keyboard_wake))) { |
| // For internal alphabetic devices, keys will trigger wake on key down. |
| |
| addKeyByEvdevCode(KEY_A, AKEYCODE_A); |
| addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME); |
| addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE); |
| |
| std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_A, 1); |
| ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); |
| |
| argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_A, 0); |
| ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); |
| |
| argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1); |
| ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); |
| |
| argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0); |
| ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); |
| |
| argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1); |
| ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); |
| |
| argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0); |
| ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); |
| } |
| |
| // --- KeyboardInputMapperTest --- |
| |
| // TODO(b/283812079): convert the tests for this class, which use multiple mappers each, to use |
| // InputMapperUnitTest. |
| class KeyboardInputMapperTest : public InputMapperTest { |
| protected: |
| void SetUp() override { |
| InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::KEYBOARD | |
| InputDeviceClass::ALPHAKEY); |
| } |
| const std::string UNIQUE_ID = "local:0"; |
| |
| void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalScanCode, |
| int32_t originalKeyCode, int32_t rotatedKeyCode, |
| ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID); |
| }; |
| |
| void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper& mapper, |
| int32_t originalScanCode, int32_t originalKeyCode, |
| int32_t rotatedKeyCode, |
| ui::LogicalDisplayId displayId) { |
| NotifyKeyArgs args; |
| |
| process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 1); |
| ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); |
| ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); |
| ASSERT_EQ(originalScanCode, args.scanCode); |
| ASSERT_EQ(rotatedKeyCode, args.keyCode); |
| ASSERT_EQ(displayId, args.displayId); |
| |
| process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 0); |
| ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); |
| ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); |
| ASSERT_EQ(originalScanCode, args.scanCode); |
| ASSERT_EQ(rotatedKeyCode, args.keyCode); |
| ASSERT_EQ(displayId, args.displayId); |
| } |
| |
| TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) { |
| // keyboard 1. |
| mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); |
| mFakeEventHub->addKey(EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); |
| mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); |
| mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); |
| |
| // keyboard 2. |
| const std::string USB2 = "USB2"; |
| const std::string DEVICE_NAME2 = "KEYBOARD2"; |
| constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1; |
| constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1; |
| std::shared_ptr<InputDevice> device2 = |
| newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID, |
| ftl::Flags<InputDeviceClass>(0)); |
| |
| mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); |
| mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); |
| mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); |
| mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); |
| |
| KeyboardInputMapper& mapper = |
| constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); |
| |
| device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID); |
| KeyboardInputMapper& mapper2 = |
| device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, |
| mFakePolicy |
| ->getReaderConfiguration(), |
| AINPUT_SOURCE_KEYBOARD); |
| std::list<NotifyArgs> unused = |
| device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), |
| /*changes=*/{}); |
| unused += device2->reset(ARBITRARY_TIME); |
| |
| // Prepared displays and associated info. |
| constexpr uint8_t hdmi1 = 0; |
| constexpr uint8_t hdmi2 = 1; |
| const std::string SECONDARY_UNIQUE_ID = "local:1"; |
| |
| mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1); |
| mFakePolicy->addInputPortAssociation(USB2, hdmi2); |
| |
| // No associated display viewport found, should disable the device. |
| unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), |
| InputReaderConfiguration::Change::DISPLAY_INFO); |
| ASSERT_FALSE(device2->isEnabled()); |
| |
| // Prepare second display. |
| constexpr ui::LogicalDisplayId newDisplayId = ui::LogicalDisplayId{2}; |
| setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, |
| UNIQUE_ID, hdmi1, ViewportType::INTERNAL); |
| setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, |
| SECONDARY_UNIQUE_ID, hdmi2, ViewportType::EXTERNAL); |
| // Default device will reconfigure above, need additional reconfiguration for another device. |
| unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), |
| InputReaderConfiguration::Change::DISPLAY_INFO); |
| |
| // Device should be enabled after the associated display is found. |
| ASSERT_TRUE(mDevice->isEnabled()); |
| ASSERT_TRUE(device2->isEnabled()); |
| |
| // Test pad key events |
| ASSERT_NO_FATAL_FAILURE( |
| testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID)); |
| ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, |
| AKEYCODE_DPAD_RIGHT, DISPLAY_ID)); |
| ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN, |
| AKEYCODE_DPAD_DOWN, DISPLAY_ID)); |
| ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT, |
| AKEYCODE_DPAD_LEFT, DISPLAY_ID)); |
| |
| ASSERT_NO_FATAL_FAILURE( |
| testDPadKeyRotation(mapper2, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, newDisplayId)); |
| ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, |
| AKEYCODE_DPAD_RIGHT, newDisplayId)); |
| ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_DOWN, AKEYCODE_DPAD_DOWN, |
| AKEYCODE_DPAD_DOWN, newDisplayId)); |
| ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_LEFT, AKEYCODE_DPAD_LEFT, |
| AKEYCODE_DPAD_LEFT, newDisplayId)); |
| } |
| |
| TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleAfterReattach) { |
| mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/); |
| mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/); |
| mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/); |
| mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0); |
| mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); |
| mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); |
| |
| KeyboardInputMapper& mapper = |
| constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); |
| // Initial metastate is AMETA_NONE. |
| ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); |
| |
| // Initialization should have turned all of the lights off. |
| ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); |
| ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); |
| ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); |
| |
| // Toggle caps lock on. |
| process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1); |
| process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0); |
| ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); |
| ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState()); |
| |
| // Toggle num lock on. |
| process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1); |
| process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0); |
| ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); |
| ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState()); |
| |
| // Toggle scroll lock on. |
| process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1); |
| process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0); |
| ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); |
| ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState()); |
| |
| mFakeEventHub->removeDevice(EVENTHUB_ID); |
| mReader->loopOnce(); |
| |
| // keyboard 2 should default toggle keys. |
| const std::string USB2 = "USB2"; |
| const std::string DEVICE_NAME2 = "KEYBOARD2"; |
| constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1; |
| constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1; |
| std::shared_ptr<InputDevice> device2 = |
| newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID, |
| ftl::Flags<InputDeviceClass>(0)); |
| mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_CAPSL, true /*initially on*/); |
| mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_NUML, false /*initially off*/); |
| mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_SCROLLL, false /*initially off*/); |
| mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0); |
| mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); |
| mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); |
| |
| device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID); |
| KeyboardInputMapper& mapper2 = |
| device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, |
| mFakePolicy |
| ->getReaderConfiguration(), |
| AINPUT_SOURCE_KEYBOARD); |
| std::list<NotifyArgs> unused = |
| device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), |
| /*changes=*/{}); |
| unused += device2->reset(ARBITRARY_TIME); |
| |
| ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_CAPSL)); |
| ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_NUML)); |
| ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_SCROLLL)); |
| ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, |
| mapper2.getMetaState()); |
| } |
| |
| TEST_F(KeyboardInputMapperTest, Process_toggleCapsLockState) { |
| mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0); |
| mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); |
| mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); |
| |
| // Suppose we have two mappers. (DPAD + KEYBOARD) |
| constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD); |
| KeyboardInputMapper& mapper = |
| constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); |
| // Initial metastate is AMETA_NONE. |
| ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); |
| |
| mReader->toggleCapsLockState(DEVICE_ID); |
| ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState()); |
| } |
| |
| TEST_F(KeyboardInputMapperTest, Process_ResetLockedModifierState) { |
| mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0); |
| mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); |
| mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); |
| |
| KeyboardInputMapper& mapper = |
| constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); |
| // Initial metastate is AMETA_NONE. |
| ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); |
| |
| // Toggle caps lock on. |
| process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1); |
| process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0); |
| |
| // Toggle num lock on. |
| process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1); |
| process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0); |
| |
| // Toggle scroll lock on. |
| process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1); |
| process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0); |
| ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState()); |
| |
| mReader->resetLockedModifierState(); |
| ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); |
| } |
| |
| TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleInMultiDevices) { |
| // keyboard 1. |
| mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/); |
| mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/); |
| mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/); |
| mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0); |
| mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); |
| mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); |
| |
| KeyboardInputMapper& mapper1 = |
| constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); |
| |
| // keyboard 2. |
| const std::string USB2 = "USB2"; |
| const std::string DEVICE_NAME2 = "KEYBOARD2"; |
| constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1; |
| constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1; |
| std::shared_ptr<InputDevice> device2 = |
| newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID, |
| ftl::Flags<InputDeviceClass>(0)); |
| mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_CAPSL, true /*initially on*/); |
| mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_NUML, false /*initially off*/); |
| mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_SCROLLL, false /*initially off*/); |
| mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0); |
| mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); |
| mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); |
| |
| device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID); |
| KeyboardInputMapper& mapper2 = |
| device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, |
| mFakePolicy |
| ->getReaderConfiguration(), |
| AINPUT_SOURCE_KEYBOARD); |
| std::list<NotifyArgs> unused = |
| device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), |
| /*changes=*/{}); |
| unused += device2->reset(ARBITRARY_TIME); |
| |
| // Initial metastate is AMETA_NONE. |
| ASSERT_EQ(AMETA_NONE, mapper1.getMetaState()); |
| ASSERT_EQ(AMETA_NONE, mapper2.getMetaState()); |
| |
| // Toggle num lock on and off. |
| process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1); |
| process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0); |
| ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); |
| ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper1.getMetaState()); |
| ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper2.getMetaState()); |
| |
| process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1); |
| process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0); |
| ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); |
| ASSERT_EQ(AMETA_NONE, mapper1.getMetaState()); |
| ASSERT_EQ(AMETA_NONE, mapper2.getMetaState()); |
| |
| // Toggle caps lock on and off. |
| process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1); |
| process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0); |
| ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); |
| ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper1.getMetaState()); |
| ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper2.getMetaState()); |
| |
| process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1); |
| process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0); |
| ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); |
| ASSERT_EQ(AMETA_NONE, mapper1.getMetaState()); |
| ASSERT_EQ(AMETA_NONE, mapper2.getMetaState()); |
| |
| // Toggle scroll lock on and off. |
| process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1); |
| process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0); |
| ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); |
| ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper1.getMetaState()); |
| ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper2.getMetaState()); |
| |
| process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1); |
| process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0); |
| ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); |
| ASSERT_EQ(AMETA_NONE, mapper1.getMetaState()); |
| ASSERT_EQ(AMETA_NONE, mapper2.getMetaState()); |
| } |
| |
| /** |
| * When there is more than one KeyboardInputMapper for an InputDevice, each mapper should produce |
| * events that use the shared keyboard source across all mappers. This is to ensure that each |
| * input device generates key events in a consistent manner, regardless of which mapper produces |
| * the event. |
| */ |
| TEST_F(KeyboardInputMapperTest, UsesSharedKeyboardSource) { |
| mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); |
| |
| // Add a mapper with SOURCE_KEYBOARD |
| KeyboardInputMapper& keyboardMapper = |
| constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); |
| |
| process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1); |
| ASSERT_NO_FATAL_FAILURE( |
| mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD))); |
| process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0); |
| ASSERT_NO_FATAL_FAILURE( |
| mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD))); |
| |
| // Add a mapper with SOURCE_DPAD |
| KeyboardInputMapper& dpadMapper = |
| constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD); |
| for (auto* mapper : {&keyboardMapper, &dpadMapper}) { |
| process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1); |
| ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled( |
| WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD))); |
| process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0); |
| ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled( |
| WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD))); |
| } |
| |
| // Add a mapper with SOURCE_GAMEPAD |
| KeyboardInputMapper& gamepadMapper = |
| constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_GAMEPAD); |
| for (auto* mapper : {&keyboardMapper, &dpadMapper, &gamepadMapper}) { |
| process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1); |
| ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled( |
| WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD))); |
| process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0); |
| ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled( |
| WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD))); |
| } |
| } |
| |
| // --- KeyboardInputMapperTest_ExternalAlphabeticDevice --- |
| |
| class KeyboardInputMapperTest_ExternalAlphabeticDevice : public KeyboardInputMapperUnitTest { |
| protected: |
| void SetUp() override { |
| InputMapperUnitTest::SetUp(); |
| ON_CALL((*mDevice), getSources).WillByDefault(Return(AINPUT_SOURCE_KEYBOARD)); |
| ON_CALL((*mDevice), getKeyboardType).WillByDefault(Return(KeyboardType::ALPHABETIC)); |
| ON_CALL((*mDevice), isExternal).WillByDefault(Return(true)); |
| EXPECT_CALL(mMockEventHub, getDeviceClasses(EVENTHUB_ID)) |
| .WillRepeatedly(Return(InputDeviceClass::KEYBOARD | InputDeviceClass::ALPHAKEY | |
| InputDeviceClass::EXTERNAL)); |
| mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration, |
| AINPUT_SOURCE_KEYBOARD); |
| } |
| }; |
| |
| // --- KeyboardInputMapperTest_ExternalNonAlphabeticDevice --- |
| |
| class KeyboardInputMapperTest_ExternalNonAlphabeticDevice : public KeyboardInputMapperUnitTest { |
| protected: |
| void SetUp() override { |
| InputMapperUnitTest::SetUp(); |
| ON_CALL((*mDevice), getSources).WillByDefault(Return(AINPUT_SOURCE_KEYBOARD)); |
| ON_CALL((*mDevice), getKeyboardType).WillByDefault(Return(KeyboardType::NON_ALPHABETIC)); |
| ON_CALL((*mDevice), isExternal).WillByDefault(Return(true)); |
| EXPECT_CALL(mMockEventHub, getDeviceClasses(EVENTHUB_ID)) |
| .WillRepeatedly(Return(InputDeviceClass::KEYBOARD | InputDeviceClass::EXTERNAL)); |
| mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration, |
| AINPUT_SOURCE_KEYBOARD); |
| } |
| }; |
| |
| TEST_F(KeyboardInputMapperTest_ExternalAlphabeticDevice, WakeBehavior_AlphabeticKeyboard) { |
| // For external devices, keys will trigger wake on key down. Media keys should also trigger |
| // wake if triggered from external devices. |
| |
| addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME); |
| addKeyByEvdevCode(KEY_PLAY, AKEYCODE_MEDIA_PLAY); |
| addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE); |
| |
| std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1); |
| ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); |
| |
| argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0); |
| ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); |
| |
| argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1); |
| ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); |
| |
| argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0); |
| ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); |
| |
| argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1); |
| ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); |
| |
| argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0); |
| ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); |
| } |
| |
| TEST_F(KeyboardInputMapperTest_ExternalNonAlphabeticDevice, WakeBehavior_NonAlphabeticKeyboard) { |
| // For external devices, keys will trigger wake on key down. Media keys should not trigger |
| // wake if triggered from external non-alphaebtic keyboard (e.g. headsets). |
| |
| addKeyByEvdevCode(KEY_PLAY, AKEYCODE_MEDIA_PLAY); |
| addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE); |
| |
| std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1); |
| ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); |
| |
| argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0); |
| ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); |
| |
| argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1); |
| ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); |
| |
| argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0); |
| ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); |
| } |
| |
| TEST_F(KeyboardInputMapperTest_ExternalAlphabeticDevice, DoNotWakeByDefaultBehavior) { |
| // Tv Remote key's wake behavior is prescribed by the keylayout file. |
| |
| addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME, POLICY_FLAG_WAKE); |
| addKeyByEvdevCode(KEY_DOWN, AKEYCODE_DPAD_DOWN); |
| addKeyByEvdevCode(KEY_PLAY, AKEYCODE_MEDIA_PLAY, POLICY_FLAG_WAKE); |
| |
| mPropertyMap.addProperty("keyboard.doNotWakeByDefault", "1"); |
| mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration, |
| AINPUT_SOURCE_KEYBOARD); |
| |
| std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1); |
| ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); |
| |
| argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0); |
| ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); |
| |
| argsList = process(ARBITRARY_TIME, EV_KEY, KEY_DOWN, 1); |
| ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); |
| |
| argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_DOWN, 0); |
| ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); |
| |
| argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1); |
| ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); |
| |
| argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0); |
| ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); |
| } |
| |
| } // namespace android |