• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 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 <fuzzer/FuzzedDataProvider.h>
18 
19 namespace android {
20 
21 static constexpr int32_t MAX_RANDOM_POINTERS = 4;
22 static constexpr int32_t MAX_RANDOM_DEVICES = 4;
23 
24 // The maximum value that we use for the action button field of NotifyMotionArgs. (We allow multiple
25 // bits to be set for this since we're just trying to generate a fuzzed event stream that doesn't
26 // cause crashes when enum values are converted to Rust — we don't necessarily want it to be valid.)
27 //
28 // AMOTION_EVENT_BUTTON_STYLUS_SECONDARY should be replaced with whatever AMOTION_EVENT_BUTTON_
29 // value is highest if the enum is edited.
30 static constexpr int8_t MAX_ACTION_BUTTON_VALUE = (AMOTION_EVENT_BUTTON_STYLUS_SECONDARY << 1) - 1;
31 
getFuzzedMotionAction(FuzzedDataProvider & fdp)32 int getFuzzedMotionAction(FuzzedDataProvider& fdp) {
33     int actionMasked = fdp.PickValueInArray<int>({
34             AMOTION_EVENT_ACTION_DOWN, AMOTION_EVENT_ACTION_UP, AMOTION_EVENT_ACTION_MOVE,
35             AMOTION_EVENT_ACTION_HOVER_ENTER, AMOTION_EVENT_ACTION_HOVER_MOVE,
36             AMOTION_EVENT_ACTION_HOVER_EXIT, AMOTION_EVENT_ACTION_CANCEL,
37             // do not inject AMOTION_EVENT_ACTION_OUTSIDE,
38             AMOTION_EVENT_ACTION_SCROLL, AMOTION_EVENT_ACTION_POINTER_DOWN,
39             AMOTION_EVENT_ACTION_POINTER_UP,
40             // do not send buttons until verifier supports them
41             // AMOTION_EVENT_ACTION_BUTTON_PRESS,
42             // AMOTION_EVENT_ACTION_BUTTON_RELEASE,
43     });
44     switch (actionMasked) {
45         case AMOTION_EVENT_ACTION_POINTER_DOWN:
46         case AMOTION_EVENT_ACTION_POINTER_UP: {
47             const int32_t index = fdp.ConsumeIntegralInRange(0, MAX_RANDOM_POINTERS - 1);
48             const int32_t action =
49                     actionMasked | (index << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
50             return action;
51         }
52         default:
53             return actionMasked;
54     }
55 }
56 
57 /**
58  * For now, focus on the 3 main sources.
59  */
getFuzzedSource(FuzzedDataProvider & fdp)60 int getFuzzedSource(FuzzedDataProvider& fdp) {
61     return fdp.PickValueInArray<int>({
62             // AINPUT_SOURCE_UNKNOWN,
63             // AINPUT_SOURCE_KEYBOARD,
64             // AINPUT_SOURCE_DPAD,
65             // AINPUT_SOURCE_GAMEPAD,
66             AINPUT_SOURCE_TOUCHSCREEN, AINPUT_SOURCE_MOUSE, AINPUT_SOURCE_STYLUS,
67             // AINPUT_SOURCE_BLUETOOTH_STYLUS,
68             // AINPUT_SOURCE_TRACKBALL,
69             // AINPUT_SOURCE_MOUSE_RELATIVE,
70             // AINPUT_SOURCE_TOUCHPAD,
71             // AINPUT_SOURCE_TOUCH_NAVIGATION,
72             // AINPUT_SOURCE_JOYSTICK,
73             // AINPUT_SOURCE_HDMI,
74             // AINPUT_SOURCE_SENSOR,
75             // AINPUT_SOURCE_ROTARY_ENCODER,
76             // AINPUT_SOURCE_ANY,
77     });
78 }
79 
getFuzzedButtonState(FuzzedDataProvider & fdp)80 int getFuzzedButtonState(FuzzedDataProvider& fdp) {
81     return fdp.PickValueInArray<int>({
82             0,
83             // AMOTION_EVENT_BUTTON_PRIMARY,
84             // AMOTION_EVENT_BUTTON_SECONDARY,
85             // AMOTION_EVENT_BUTTON_TERTIARY,
86             // AMOTION_EVENT_BUTTON_BACK,
87             // AMOTION_EVENT_BUTTON_FORWARD,
88             // AMOTION_EVENT_BUTTON_STYLUS_PRIMARY,
89             // AMOTION_EVENT_BUTTON_STYLUS_SECONDARY,
90     });
91 }
92 
getFuzzedFlags(FuzzedDataProvider & fdp,int32_t action)93 int32_t getFuzzedFlags(FuzzedDataProvider& fdp, int32_t action) {
94     constexpr std::array<int32_t, 4> FLAGS{
95             AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED,
96             AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED,
97             AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT,
98             AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE,
99     };
100 
101     int32_t flags = 0;
102     for (size_t i = 0; i < fdp.ConsumeIntegralInRange(size_t(0), FLAGS.size()); i++) {
103         flags |= fdp.PickValueInArray<int32_t>(FLAGS);
104     }
105     if (action == AMOTION_EVENT_ACTION_CANCEL) {
106         flags |= AMOTION_EVENT_FLAG_CANCELED;
107     }
108     if (MotionEvent::getActionMasked(action) == AMOTION_EVENT_ACTION_POINTER_UP) {
109         if (fdp.ConsumeBool()) {
110             flags |= AMOTION_EVENT_FLAG_CANCELED;
111         }
112     }
113     return flags;
114 }
115 
getFuzzedPointerCount(FuzzedDataProvider & fdp,int32_t action)116 int32_t getFuzzedPointerCount(FuzzedDataProvider& fdp, int32_t action) {
117     switch (MotionEvent::getActionMasked(action)) {
118         case AMOTION_EVENT_ACTION_DOWN:
119         case AMOTION_EVENT_ACTION_UP: {
120             return 1;
121         }
122         case AMOTION_EVENT_ACTION_OUTSIDE:
123         case AMOTION_EVENT_ACTION_CANCEL:
124         case AMOTION_EVENT_ACTION_MOVE:
125             return fdp.ConsumeIntegralInRange<int32_t>(1, MAX_RANDOM_POINTERS);
126         case AMOTION_EVENT_ACTION_HOVER_ENTER:
127         case AMOTION_EVENT_ACTION_HOVER_MOVE:
128         case AMOTION_EVENT_ACTION_HOVER_EXIT:
129             return 1;
130         case AMOTION_EVENT_ACTION_SCROLL:
131             return 1;
132         case AMOTION_EVENT_ACTION_POINTER_DOWN:
133         case AMOTION_EVENT_ACTION_POINTER_UP: {
134             const uint8_t actionIndex = MotionEvent::getActionIndex(action);
135             const int32_t count =
136                     std::max(actionIndex + 1,
137                              fdp.ConsumeIntegralInRange<int32_t>(1, MAX_RANDOM_POINTERS));
138             // Need to have at least 2 pointers
139             return std::max(2, count);
140         }
141         case AMOTION_EVENT_ACTION_BUTTON_PRESS:
142         case AMOTION_EVENT_ACTION_BUTTON_RELEASE: {
143             return 1;
144         }
145     }
146     return 1;
147 }
148 
getToolType(int32_t source)149 ToolType getToolType(int32_t source) {
150     switch (source) {
151         case AINPUT_SOURCE_TOUCHSCREEN:
152             return ToolType::FINGER;
153         case AINPUT_SOURCE_MOUSE:
154             return ToolType::MOUSE;
155         case AINPUT_SOURCE_STYLUS:
156             return ToolType::STYLUS;
157     }
158     return ToolType::UNKNOWN;
159 }
160 
now()161 inline nsecs_t now() {
162     return systemTime(SYSTEM_TIME_MONOTONIC);
163 }
164 
generateFuzzedMotionArgs(IdGenerator & idGenerator,FuzzedDataProvider & fdp,int32_t maxDisplays)165 NotifyMotionArgs generateFuzzedMotionArgs(IdGenerator& idGenerator, FuzzedDataProvider& fdp,
166                                           int32_t maxDisplays) {
167     // Create a basic motion event for testing
168     const int32_t source = getFuzzedSource(fdp);
169     const ToolType toolType = getToolType(source);
170     const int32_t action = getFuzzedMotionAction(fdp);
171     const int32_t pointerCount = getFuzzedPointerCount(fdp, action);
172     std::vector<PointerProperties> pointerProperties;
173     std::vector<PointerCoords> pointerCoords;
174     for (int i = 0; i < pointerCount; i++) {
175         PointerProperties properties{};
176         properties.id = i;
177         properties.toolType = toolType;
178         pointerProperties.push_back(properties);
179 
180         PointerCoords coords{};
181         coords.setAxisValue(AMOTION_EVENT_AXIS_X, fdp.ConsumeIntegralInRange<int>(-1000, 1000));
182         coords.setAxisValue(AMOTION_EVENT_AXIS_Y, fdp.ConsumeIntegralInRange<int>(-1000, 1000));
183         coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1);
184         pointerCoords.push_back(coords);
185     }
186 
187     const ui::LogicalDisplayId displayId{fdp.ConsumeIntegralInRange<int32_t>(0, maxDisplays - 1)};
188     const int32_t deviceId = fdp.ConsumeIntegralInRange<int32_t>(0, MAX_RANDOM_DEVICES - 1);
189 
190     // Current time +- 5 seconds
191     const nsecs_t currentTime = now();
192     const nsecs_t downTime =
193             fdp.ConsumeIntegralInRange<nsecs_t>(currentTime - 5E9, currentTime + 5E9);
194     const nsecs_t readTime = downTime;
195     const nsecs_t eventTime = fdp.ConsumeIntegralInRange<nsecs_t>(downTime, downTime + 1E9);
196     const int32_t actionButton = fdp.ConsumeIntegralInRange<int32_t>(0, MAX_ACTION_BUTTON_VALUE);
197 
198     const float cursorX = fdp.ConsumeIntegralInRange<int>(-10000, 10000);
199     const float cursorY = fdp.ConsumeIntegralInRange<int>(-10000, 10000);
200     return NotifyMotionArgs(idGenerator.nextId(), eventTime, readTime, deviceId, source, displayId,
201                             POLICY_FLAG_PASS_TO_USER, action, actionButton,
202                             getFuzzedFlags(fdp, action), AMETA_NONE, getFuzzedButtonState(fdp),
203                             MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount,
204                             pointerProperties.data(), pointerCoords.data(), /*xPrecision=*/0,
205                             /*yPrecision=*/0, cursorX, cursorY, downTime, /*videoFrames=*/{});
206 }
207 
208 } // namespace android
209