blob: 885820fafb48311c2f188fbc9abfb0cdb12ec916 [file] [log] [blame]
/*
* 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 int32_t 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