1 /*
2  * Copyright (C) 2019 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 "../InputClassifier.h"
18 #include <gtest/gtest.h>
19 
20 #include "TestInputListener.h"
21 
22 #include <android/hardware/input/classifier/1.0/IInputClassifier.h>
23 
24 using namespace android::hardware::input;
25 using android::hardware::Return;
26 using android::hardware::Void;
27 using android::hardware::input::common::V1_0::Classification;
28 
29 namespace android {
30 
31 // --- InputClassifierTest ---
32 
generateBasicMotionArgs()33 static NotifyMotionArgs generateBasicMotionArgs() {
34     // Create a basic motion event for testing
35     PointerProperties properties;
36     properties.id = 0;
37     properties.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
38 
39     PointerCoords coords;
40     coords.clear();
41     coords.setAxisValue(AMOTION_EVENT_AXIS_X, 1);
42     coords.setAxisValue(AMOTION_EVENT_AXIS_Y, 1);
43     static constexpr nsecs_t downTime = 2;
44     NotifyMotionArgs motionArgs(1 /*sequenceNum*/, downTime /*eventTime*/, 3 /*deviceId*/,
45                                 AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4 /*policyFlags*/,
46                                 AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, 0 /*flags*/,
47                                 AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE,
48                                 AMOTION_EVENT_EDGE_FLAG_NONE, 1 /*pointerCount*/, &properties,
49                                 &coords, 0 /*xPrecision*/, 0 /*yPrecision*/,
50                                 AMOTION_EVENT_INVALID_CURSOR_POSITION,
51                                 AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime,
52                                 {} /*videoFrames*/);
53     return motionArgs;
54 }
55 
56 class InputClassifierTest : public testing::Test {
57 protected:
58     sp<InputClassifierInterface> mClassifier;
59     sp<TestInputListener> mTestListener;
60 
SetUp()61     virtual void SetUp() override {
62         mTestListener = new TestInputListener();
63         mClassifier = new InputClassifier(mTestListener);
64     }
65 
TearDown()66     virtual void TearDown() override {
67         mClassifier.clear();
68         mTestListener.clear();
69     }
70 };
71 
72 /**
73  * Create a basic configuration change and send it to input classifier.
74  * Expect that the event is received by the next input stage, unmodified.
75  */
TEST_F(InputClassifierTest,SendToNextStage_NotifyConfigurationChangedArgs)76 TEST_F(InputClassifierTest, SendToNextStage_NotifyConfigurationChangedArgs) {
77     // Create a basic configuration change and send to classifier
78     NotifyConfigurationChangedArgs args(1/*sequenceNum*/, 2/*eventTime*/);
79 
80     mClassifier->notifyConfigurationChanged(&args);
81     NotifyConfigurationChangedArgs outArgs;
82     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled(&outArgs));
83     ASSERT_EQ(args, outArgs);
84 }
85 
TEST_F(InputClassifierTest,SendToNextStage_NotifyKeyArgs)86 TEST_F(InputClassifierTest, SendToNextStage_NotifyKeyArgs) {
87     // Create a basic key event and send to classifier
88     NotifyKeyArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*deviceId*/, AINPUT_SOURCE_KEYBOARD,
89             ADISPLAY_ID_DEFAULT, 0/*policyFlags*/, AKEY_EVENT_ACTION_DOWN, 4/*flags*/,
90             AKEYCODE_HOME, 5/*scanCode*/, AMETA_NONE, 6/*downTime*/);
91 
92     mClassifier->notifyKey(&args);
93     NotifyKeyArgs outArgs;
94     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&outArgs));
95     ASSERT_EQ(args, outArgs);
96 }
97 
98 
99 /**
100  * Create a basic motion event and send it to input classifier.
101  * Expect that the event is received by the next input stage, unmodified.
102  */
TEST_F(InputClassifierTest,SendToNextStage_NotifyMotionArgs)103 TEST_F(InputClassifierTest, SendToNextStage_NotifyMotionArgs) {
104     NotifyMotionArgs motionArgs = generateBasicMotionArgs();
105     mClassifier->notifyMotion(&motionArgs);
106     NotifyMotionArgs args;
107     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
108     ASSERT_EQ(motionArgs, args);
109 }
110 
111 /**
112  * Create a basic switch event and send it to input classifier.
113  * Expect that the event is received by the next input stage, unmodified.
114  */
TEST_F(InputClassifierTest,SendToNextStage_NotifySwitchArgs)115 TEST_F(InputClassifierTest, SendToNextStage_NotifySwitchArgs) {
116     NotifySwitchArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*policyFlags*/, 4/*switchValues*/,
117             5/*switchMask*/);
118 
119     mClassifier->notifySwitch(&args);
120     NotifySwitchArgs outArgs;
121     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifySwitchWasCalled(&outArgs));
122     ASSERT_EQ(args, outArgs);
123 }
124 
125 /**
126  * Create a basic device reset event and send it to input classifier.
127  * Expect that the event is received by the next input stage, unmodified.
128  */
TEST_F(InputClassifierTest,SendToNextStage_NotifyDeviceResetArgs)129 TEST_F(InputClassifierTest, SendToNextStage_NotifyDeviceResetArgs) {
130     NotifyDeviceResetArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*deviceId*/);
131 
132     mClassifier->notifyDeviceReset(&args);
133     NotifyDeviceResetArgs outArgs;
134     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyDeviceResetWasCalled(&outArgs));
135     ASSERT_EQ(args, outArgs);
136 }
137 
TEST_F(InputClassifierTest,SetMotionClassifier_Enabled)138 TEST_F(InputClassifierTest, SetMotionClassifier_Enabled) {
139     mClassifier->setMotionClassifierEnabled(true);
140 }
141 
TEST_F(InputClassifierTest,SetMotionClassifier_Disabled)142 TEST_F(InputClassifierTest, SetMotionClassifier_Disabled) {
143     mClassifier->setMotionClassifierEnabled(false);
144 }
145 
146 /**
147  * Try to break it by calling setMotionClassifierEnabled multiple times.
148  */
TEST_F(InputClassifierTest,SetMotionClassifier_Multiple)149 TEST_F(InputClassifierTest, SetMotionClassifier_Multiple) {
150     mClassifier->setMotionClassifierEnabled(true);
151     mClassifier->setMotionClassifierEnabled(true);
152     mClassifier->setMotionClassifierEnabled(true);
153     mClassifier->setMotionClassifierEnabled(false);
154     mClassifier->setMotionClassifierEnabled(false);
155     mClassifier->setMotionClassifierEnabled(true);
156     mClassifier->setMotionClassifierEnabled(true);
157     mClassifier->setMotionClassifierEnabled(true);
158 }
159 
160 /**
161  * A minimal implementation of IInputClassifier.
162  */
163 struct TestHal : public android::hardware::input::classifier::V1_0::IInputClassifier {
classifyandroid::TestHal164     Return<Classification> classify(
165             const android::hardware::input::common::V1_0::MotionEvent& event) override {
166         return Classification::NONE;
167     };
resetandroid::TestHal168     Return<void> reset() override { return Void(); };
resetDeviceandroid::TestHal169     Return<void> resetDevice(int32_t deviceId) override { return Void(); };
170 };
171 
172 /**
173  * An entity that will be subscribed to the HAL death.
174  */
175 class TestDeathRecipient : public android::hardware::hidl_death_recipient {
176 public:
serviceDied(uint64_t cookie,const wp<android::hidl::base::V1_0::IBase> & who)177     virtual void serviceDied(uint64_t cookie,
178                              const wp<android::hidl::base::V1_0::IBase>& who) override{};
179 };
180 
181 // --- MotionClassifierTest ---
182 
183 class MotionClassifierTest : public testing::Test {
184 protected:
185     std::unique_ptr<MotionClassifierInterface> mMotionClassifier;
186 
SetUp()187     virtual void SetUp() override {
188         mMotionClassifier = MotionClassifier::create(new TestDeathRecipient());
189         if (mMotionClassifier == nullptr) {
190             // If the device running this test does not have IInputClassifier service,
191             // use the test HAL instead.
192             // Using 'new' to access non-public constructor
193             mMotionClassifier =
194                     std::unique_ptr<MotionClassifier>(new MotionClassifier(new TestHal()));
195         }
196     }
197 };
198 
199 /**
200  * Since MotionClassifier creates a new thread to communicate with HAL,
201  * it's not really expected to ever exit. However, for testing purposes,
202  * we need to ensure that it is able to exit cleanly.
203  * If the thread is not properly cleaned up, it will generate SIGABRT.
204  * The logic for exiting the thread and cleaning up the resources is inside
205  * the destructor. Here, we just make sure the destructor does not crash.
206  */
TEST_F(MotionClassifierTest,Destructor_DoesNotCrash)207 TEST_F(MotionClassifierTest, Destructor_DoesNotCrash) {
208     mMotionClassifier = nullptr;
209 }
210 
211 /**
212  * Make sure MotionClassifier can handle events that don't have any
213  * video frames.
214  */
TEST_F(MotionClassifierTest,Classify_NoVideoFrames)215 TEST_F(MotionClassifierTest, Classify_NoVideoFrames) {
216     NotifyMotionArgs motionArgs = generateBasicMotionArgs();
217 
218     // We are not checking the return value, because we can't be making assumptions
219     // about the HAL operation, since it will be highly hardware-dependent
220     ASSERT_NO_FATAL_FAILURE(mMotionClassifier->classify(motionArgs));
221 }
222 
223 /**
224  * Make sure nothing crashes when a videoFrame is sent.
225  */
TEST_F(MotionClassifierTest,Classify_OneVideoFrame)226 TEST_F(MotionClassifierTest, Classify_OneVideoFrame) {
227     NotifyMotionArgs motionArgs = generateBasicMotionArgs();
228 
229     std::vector<int16_t> videoData = {1, 2, 3, 4};
230     timeval timestamp = { 1, 1};
231     TouchVideoFrame frame(2, 2, std::move(videoData), timestamp);
232     motionArgs.videoFrames = {frame};
233 
234     // We are not checking the return value, because we can't be making assumptions
235     // about the HAL operation, since it will be highly hardware-dependent
236     ASSERT_NO_FATAL_FAILURE(mMotionClassifier->classify(motionArgs));
237 }
238 
239 /**
240  * Make sure nothing crashes when 2 videoFrames are sent.
241  */
TEST_F(MotionClassifierTest,Classify_TwoVideoFrames)242 TEST_F(MotionClassifierTest, Classify_TwoVideoFrames) {
243     NotifyMotionArgs motionArgs = generateBasicMotionArgs();
244 
245     std::vector<int16_t> videoData1 = {1, 2, 3, 4};
246     timeval timestamp1 = { 1, 1};
247     TouchVideoFrame frame1(2, 2, std::move(videoData1), timestamp1);
248 
249     std::vector<int16_t> videoData2 = {6, 6, 6, 6};
250     timeval timestamp2 = { 1, 2};
251     TouchVideoFrame frame2(2, 2, std::move(videoData2), timestamp2);
252 
253     motionArgs.videoFrames = {frame1, frame2};
254 
255     // We are not checking the return value, because we can't be making assumptions
256     // about the HAL operation, since it will be highly hardware-dependent
257     ASSERT_NO_FATAL_FAILURE(mMotionClassifier->classify(motionArgs));
258 }
259 
260 /**
261  * Make sure MotionClassifier does not crash when it is reset.
262  */
TEST_F(MotionClassifierTest,Reset_DoesNotCrash)263 TEST_F(MotionClassifierTest, Reset_DoesNotCrash) {
264     ASSERT_NO_FATAL_FAILURE(mMotionClassifier->reset());
265 }
266 
267 /**
268  * Make sure MotionClassifier does not crash when a device is reset.
269  */
TEST_F(MotionClassifierTest,DeviceReset_DoesNotCrash)270 TEST_F(MotionClassifierTest, DeviceReset_DoesNotCrash) {
271     NotifyDeviceResetArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*deviceId*/);
272     ASSERT_NO_FATAL_FAILURE(mMotionClassifier->reset(args));
273 }
274 
275 } // namespace android
276