| /* | |
| * 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 <fuzzer/FuzzedDataProvider.h> | |
| namespace android { | |
| namespace { | |
| static constexpr int32_t MAX_RANDOM_POINTERS = 4; | |
| static constexpr int32_t MAX_RANDOM_DEVICES = 4; | |
| } // namespace | |
| int getFuzzedMotionAction(FuzzedDataProvider& fdp) { | |
| int actionMasked = fdp.PickValueInArray<int>({ | |
| AMOTION_EVENT_ACTION_DOWN, AMOTION_EVENT_ACTION_UP, AMOTION_EVENT_ACTION_MOVE, | |
| AMOTION_EVENT_ACTION_HOVER_ENTER, AMOTION_EVENT_ACTION_HOVER_MOVE, | |
| AMOTION_EVENT_ACTION_HOVER_EXIT, AMOTION_EVENT_ACTION_CANCEL, | |
| // do not inject AMOTION_EVENT_ACTION_OUTSIDE, | |
| AMOTION_EVENT_ACTION_SCROLL, AMOTION_EVENT_ACTION_POINTER_DOWN, | |
| AMOTION_EVENT_ACTION_POINTER_UP, | |
| // do not send buttons until verifier supports them | |
| // AMOTION_EVENT_ACTION_BUTTON_PRESS, | |
| // AMOTION_EVENT_ACTION_BUTTON_RELEASE, | |
| }); | |
| switch (actionMasked) { | |
| case AMOTION_EVENT_ACTION_POINTER_DOWN: | |
| case AMOTION_EVENT_ACTION_POINTER_UP: { | |
| const int32_t index = fdp.ConsumeIntegralInRange(0, MAX_RANDOM_POINTERS - 1); | |
| const int32_t action = | |
| actionMasked | (index << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); | |
| return action; | |
| } | |
| default: | |
| return actionMasked; | |
| } | |
| } | |
| /** | |
| * For now, focus on the 3 main sources. | |
| */ | |
| int getFuzzedSource(FuzzedDataProvider& fdp) { | |
| return fdp.PickValueInArray<int>({ | |
| // AINPUT_SOURCE_UNKNOWN, | |
| // AINPUT_SOURCE_KEYBOARD, | |
| // AINPUT_SOURCE_DPAD, | |
| // AINPUT_SOURCE_GAMEPAD, | |
| AINPUT_SOURCE_TOUCHSCREEN, AINPUT_SOURCE_MOUSE, AINPUT_SOURCE_STYLUS, | |
| // AINPUT_SOURCE_BLUETOOTH_STYLUS, | |
| // AINPUT_SOURCE_TRACKBALL, | |
| // AINPUT_SOURCE_MOUSE_RELATIVE, | |
| // AINPUT_SOURCE_TOUCHPAD, | |
| // AINPUT_SOURCE_TOUCH_NAVIGATION, | |
| // AINPUT_SOURCE_JOYSTICK, | |
| // AINPUT_SOURCE_HDMI, | |
| // AINPUT_SOURCE_SENSOR, | |
| // AINPUT_SOURCE_ROTARY_ENCODER, | |
| // AINPUT_SOURCE_ANY, | |
| }); | |
| } | |
| int getFuzzedButtonState(FuzzedDataProvider& fdp) { | |
| return fdp.PickValueInArray<int>({ | |
| 0, | |
| // AMOTION_EVENT_BUTTON_PRIMARY, | |
| // AMOTION_EVENT_BUTTON_SECONDARY, | |
| // AMOTION_EVENT_BUTTON_TERTIARY, | |
| // AMOTION_EVENT_BUTTON_BACK, | |
| // AMOTION_EVENT_BUTTON_FORWARD, | |
| // AMOTION_EVENT_BUTTON_STYLUS_PRIMARY, | |
| // AMOTION_EVENT_BUTTON_STYLUS_SECONDARY, | |
| }); | |
| } | |
| int32_t getFuzzedFlags(FuzzedDataProvider& fdp, int32_t action) { | |
| constexpr std::array<int32_t, 4> FLAGS{ | |
| AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, | |
| AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED, | |
| AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT, | |
| AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE, | |
| }; | |
| int32_t flags = 0; | |
| for (size_t i = 0; i < fdp.ConsumeIntegralInRange(size_t(0), FLAGS.size()); i++) { | |
| flags |= fdp.PickValueInArray<int32_t>(FLAGS); | |
| } | |
| if (action == AMOTION_EVENT_ACTION_CANCEL) { | |
| flags |= AMOTION_EVENT_FLAG_CANCELED; | |
| } | |
| if (MotionEvent::getActionMasked(action) == AMOTION_EVENT_ACTION_POINTER_UP) { | |
| if (fdp.ConsumeBool()) { | |
| flags |= AMOTION_EVENT_FLAG_CANCELED; | |
| } | |
| } | |
| return flags; | |
| } | |
| int32_t getFuzzedPointerCount(FuzzedDataProvider& fdp, int32_t action) { | |
| switch (MotionEvent::getActionMasked(action)) { | |
| case AMOTION_EVENT_ACTION_DOWN: | |
| case AMOTION_EVENT_ACTION_UP: { | |
| return 1; | |
| } | |
| case AMOTION_EVENT_ACTION_OUTSIDE: | |
| case AMOTION_EVENT_ACTION_CANCEL: | |
| case AMOTION_EVENT_ACTION_MOVE: | |
| return fdp.ConsumeIntegralInRange<int32_t>(1, MAX_RANDOM_POINTERS); | |
| case AMOTION_EVENT_ACTION_HOVER_ENTER: | |
| case AMOTION_EVENT_ACTION_HOVER_MOVE: | |
| case AMOTION_EVENT_ACTION_HOVER_EXIT: | |
| return 1; | |
| case AMOTION_EVENT_ACTION_SCROLL: | |
| return 1; | |
| case AMOTION_EVENT_ACTION_POINTER_DOWN: | |
| case AMOTION_EVENT_ACTION_POINTER_UP: { | |
| const uint8_t actionIndex = MotionEvent::getActionIndex(action); | |
| const int32_t count = | |
| std::max(actionIndex + 1, | |
| fdp.ConsumeIntegralInRange<int32_t>(1, MAX_RANDOM_POINTERS)); | |
| // Need to have at least 2 pointers | |
| return std::max(2, count); | |
| } | |
| case AMOTION_EVENT_ACTION_BUTTON_PRESS: | |
| case AMOTION_EVENT_ACTION_BUTTON_RELEASE: { | |
| return 1; | |
| } | |
| } | |
| return 1; | |
| } | |
| ToolType getToolType(int32_t source) { | |
| switch (source) { | |
| case AINPUT_SOURCE_TOUCHSCREEN: | |
| return ToolType::FINGER; | |
| case AINPUT_SOURCE_MOUSE: | |
| return ToolType::MOUSE; | |
| case AINPUT_SOURCE_STYLUS: | |
| return ToolType::STYLUS; | |
| } | |
| return ToolType::UNKNOWN; | |
| } | |
| inline nsecs_t now() { | |
| return systemTime(SYSTEM_TIME_MONOTONIC); | |
| } | |
| NotifyMotionArgs generateFuzzedMotionArgs(IdGenerator& idGenerator, FuzzedDataProvider& fdp, | |
| int32_t maxDisplays) { | |
| // Create a basic motion event for testing | |
| const int32_t source = getFuzzedSource(fdp); | |
| const ToolType toolType = getToolType(source); | |
| const int32_t action = getFuzzedMotionAction(fdp); | |
| const int32_t pointerCount = getFuzzedPointerCount(fdp, action); | |
| std::vector<PointerProperties> pointerProperties; | |
| std::vector<PointerCoords> pointerCoords; | |
| for (int i = 0; i < pointerCount; i++) { | |
| PointerProperties properties{}; | |
| properties.id = i; | |
| properties.toolType = toolType; | |
| pointerProperties.push_back(properties); | |
| PointerCoords coords{}; | |
| coords.setAxisValue(AMOTION_EVENT_AXIS_X, fdp.ConsumeIntegralInRange<int>(-1000, 1000)); | |
| coords.setAxisValue(AMOTION_EVENT_AXIS_Y, fdp.ConsumeIntegralInRange<int>(-1000, 1000)); | |
| coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1); | |
| pointerCoords.push_back(coords); | |
| } | |
| const ui::LogicalDisplayId displayId{fdp.ConsumeIntegralInRange<int32_t>(0, maxDisplays - 1)}; | |
| const int32_t deviceId = fdp.ConsumeIntegralInRange<int32_t>(0, MAX_RANDOM_DEVICES - 1); | |
| // Current time +- 5 seconds | |
| const nsecs_t currentTime = now(); | |
| const nsecs_t downTime = | |
| fdp.ConsumeIntegralInRange<nsecs_t>(currentTime - 5E9, currentTime + 5E9); | |
| const nsecs_t readTime = downTime; | |
| const nsecs_t eventTime = fdp.ConsumeIntegralInRange<nsecs_t>(downTime, downTime + 1E9); | |
| const float cursorX = fdp.ConsumeIntegralInRange<int>(-10000, 10000); | |
| const float cursorY = fdp.ConsumeIntegralInRange<int>(-10000, 10000); | |
| return NotifyMotionArgs(idGenerator.nextId(), eventTime, readTime, deviceId, source, displayId, | |
| POLICY_FLAG_PASS_TO_USER, action, | |
| /*actionButton=*/fdp.ConsumeIntegral<int32_t>(), | |
| getFuzzedFlags(fdp, action), AMETA_NONE, getFuzzedButtonState(fdp), | |
| MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, | |
| pointerProperties.data(), pointerCoords.data(), | |
| /*xPrecision=*/0, | |
| /*yPrecision=*/0, cursorX, cursorY, downTime, | |
| /*videoFrames=*/{}); | |
| } | |
| } // namespace android |