blob: cdbc1869c3bce46458be0ccc44ae864f8d59da12 [file] [log] [blame] [edit]
/**
* Copyright 2024 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.
*/
#define LOG_TAG "InputConsumerNoResampling"
#define ATRACE_TAG ATRACE_TAG_INPUT
#include <chrono>
#include <inttypes.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <cutils/properties.h>
#include <ftl/enum.h>
#include <utils/Trace.h>
#include <com_android_input_flags.h>
#include <input/InputConsumerNoResampling.h>
#include <input/PrintTools.h>
#include <input/TraceTools.h>
namespace android {
namespace {
using std::chrono::nanoseconds;
/**
* Log debug messages relating to the consumer end of the transport channel.
* Enable this via "adb shell setprop log.tag.InputTransportConsumer DEBUG" (requires restart)
*/
const bool DEBUG_TRANSPORT_CONSUMER =
__android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Consumer", ANDROID_LOG_INFO);
std::unique_ptr<KeyEvent> createKeyEvent(const InputMessage& msg) {
std::unique_ptr<KeyEvent> event = std::make_unique<KeyEvent>();
event->initialize(msg.body.key.eventId, msg.body.key.deviceId, msg.body.key.source,
ui::LogicalDisplayId{msg.body.key.displayId}, msg.body.key.hmac,
msg.body.key.action, msg.body.key.flags, msg.body.key.keyCode,
msg.body.key.scanCode, msg.body.key.metaState, msg.body.key.repeatCount,
msg.body.key.downTime, msg.body.key.eventTime);
return event;
}
std::unique_ptr<FocusEvent> createFocusEvent(const InputMessage& msg) {
std::unique_ptr<FocusEvent> event = std::make_unique<FocusEvent>();
event->initialize(msg.body.focus.eventId, msg.body.focus.hasFocus);
return event;
}
std::unique_ptr<CaptureEvent> createCaptureEvent(const InputMessage& msg) {
std::unique_ptr<CaptureEvent> event = std::make_unique<CaptureEvent>();
event->initialize(msg.body.capture.eventId, msg.body.capture.pointerCaptureEnabled);
return event;
}
std::unique_ptr<DragEvent> createDragEvent(const InputMessage& msg) {
std::unique_ptr<DragEvent> event = std::make_unique<DragEvent>();
event->initialize(msg.body.drag.eventId, msg.body.drag.x, msg.body.drag.y,
msg.body.drag.isExiting);
return event;
}
std::unique_ptr<MotionEvent> createMotionEvent(const InputMessage& msg) {
std::unique_ptr<MotionEvent> event = std::make_unique<MotionEvent>();
const uint32_t pointerCount = msg.body.motion.pointerCount;
std::vector<PointerProperties> pointerProperties;
pointerProperties.reserve(pointerCount);
std::vector<PointerCoords> pointerCoords;
pointerCoords.reserve(pointerCount);
for (uint32_t i = 0; i < pointerCount; i++) {
pointerProperties.push_back(msg.body.motion.pointers[i].properties);
pointerCoords.push_back(msg.body.motion.pointers[i].coords);
}
ui::Transform transform;
transform.set({msg.body.motion.dsdx, msg.body.motion.dtdx, msg.body.motion.tx,
msg.body.motion.dtdy, msg.body.motion.dsdy, msg.body.motion.ty, 0, 0, 1});
ui::Transform displayTransform;
displayTransform.set({msg.body.motion.dsdxRaw, msg.body.motion.dtdxRaw, msg.body.motion.txRaw,
msg.body.motion.dtdyRaw, msg.body.motion.dsdyRaw, msg.body.motion.tyRaw,
0, 0, 1});
event->initialize(msg.body.motion.eventId, msg.body.motion.deviceId, msg.body.motion.source,
ui::LogicalDisplayId{msg.body.motion.displayId}, msg.body.motion.hmac,
msg.body.motion.action, msg.body.motion.actionButton, msg.body.motion.flags,
msg.body.motion.edgeFlags, msg.body.motion.metaState,
msg.body.motion.buttonState, msg.body.motion.classification, transform,
msg.body.motion.xPrecision, msg.body.motion.yPrecision,
msg.body.motion.xCursorPosition, msg.body.motion.yCursorPosition,
displayTransform, msg.body.motion.downTime, msg.body.motion.eventTime,
pointerCount, pointerProperties.data(), pointerCoords.data());
return event;
}
void addSample(MotionEvent& event, const InputMessage& msg) {
uint32_t pointerCount = msg.body.motion.pointerCount;
std::vector<PointerCoords> pointerCoords;
pointerCoords.reserve(pointerCount);
for (uint32_t i = 0; i < pointerCount; i++) {
pointerCoords.push_back(msg.body.motion.pointers[i].coords);
}
// TODO(b/329770983): figure out if it's safe to combine events with mismatching metaState
event.setMetaState(event.getMetaState() | msg.body.motion.metaState);
event.addSample(msg.body.motion.eventTime, pointerCoords.data(), msg.body.motion.eventId);
}
std::unique_ptr<TouchModeEvent> createTouchModeEvent(const InputMessage& msg) {
std::unique_ptr<TouchModeEvent> event = std::make_unique<TouchModeEvent>();
event->initialize(msg.body.touchMode.eventId, msg.body.touchMode.isInTouchMode);
return event;
}
std::string outboundMessageToString(const InputMessage& outboundMsg) {
switch (outboundMsg.header.type) {
case InputMessage::Type::FINISHED: {
return android::base::StringPrintf(" Finish: seq=%" PRIu32 " handled=%s",
outboundMsg.header.seq,
toString(outboundMsg.body.finished.handled));
}
case InputMessage::Type::TIMELINE: {
return android::base::
StringPrintf(" Timeline: inputEventId=%" PRId32 " gpuCompletedTime=%" PRId64
", presentTime=%" PRId64,
outboundMsg.body.timeline.eventId,
outboundMsg.body.timeline
.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME],
outboundMsg.body.timeline
.graphicsTimeline[GraphicsTimeline::PRESENT_TIME]);
}
default: {
LOG(FATAL) << "Outbound message must be FINISHED or TIMELINE, got "
<< ftl::enum_string(outboundMsg.header.type);
return "Unreachable";
}
}
}
InputMessage createFinishedMessage(uint32_t seq, bool handled, nsecs_t consumeTime) {
InputMessage msg;
msg.header.type = InputMessage::Type::FINISHED;
msg.header.seq = seq;
msg.body.finished.handled = handled;
msg.body.finished.consumeTime = consumeTime;
return msg;
}
InputMessage createTimelineMessage(int32_t inputEventId, nsecs_t gpuCompletedTime,
nsecs_t presentTime) {
InputMessage msg;
msg.header.type = InputMessage::Type::TIMELINE;
msg.header.seq = 0;
msg.body.timeline.eventId = inputEventId;
msg.body.timeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = gpuCompletedTime;
msg.body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = presentTime;
return msg;
}
bool isPointerEvent(const MotionEvent& motionEvent) {
return (motionEvent.getSource() & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER;
}
} // namespace
using android::base::Result;
// --- InputConsumerNoResampling ---
InputConsumerNoResampling::InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel,
sp<Looper> looper,
InputConsumerCallbacks& callbacks,
std::unique_ptr<Resampler> resampler)
: mChannel{channel},
mLooper{looper},
mCallbacks{callbacks},
mResampler{std::move(resampler)},
mFdEvents(0) {
LOG_ALWAYS_FATAL_IF(mLooper == nullptr);
mCallback = sp<LooperEventCallback>::make(
std::bind(&InputConsumerNoResampling::handleReceiveCallback, this,
std::placeholders::_1));
// In the beginning, there are no pending outbounds events; we only care about receiving
// incoming data.
setFdEvents(ALOOPER_EVENT_INPUT);
}
InputConsumerNoResampling::~InputConsumerNoResampling() {
ensureCalledOnLooperThread(__func__);
consumeBatchedInputEvents(/*requestedFrameTime=*/std::nullopt);
while (!mOutboundQueue.empty()) {
processOutboundEvents();
// This is our last chance to ack the events. If we don't ack them here, we will get an ANR,
// so keep trying to send the events as long as they are present in the queue.
}
setFdEvents(0);
}
int InputConsumerNoResampling::handleReceiveCallback(int events) {
// Allowed return values of this function as documented in LooperCallback::handleEvent
constexpr int REMOVE_CALLBACK = 0;
constexpr int KEEP_CALLBACK = 1;
if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
// This error typically occurs when the publisher has closed the input channel
// as part of removing a window or finishing an IME session, in which case
// the consumer will soon be disposed as well.
if (DEBUG_TRANSPORT_CONSUMER) {
LOG(INFO) << "The channel was hung up or an error occurred: " << mChannel->getName();
}
return REMOVE_CALLBACK;
}
int handledEvents = 0;
if (events & ALOOPER_EVENT_INPUT) {
handleMessages(readAllMessages());
handledEvents |= ALOOPER_EVENT_INPUT;
}
if (events & ALOOPER_EVENT_OUTPUT) {
processOutboundEvents();
handledEvents |= ALOOPER_EVENT_OUTPUT;
}
if (handledEvents != events) {
LOG(FATAL) << "Mismatch: handledEvents=" << handledEvents << ", events=" << events;
}
return KEEP_CALLBACK;
}
void InputConsumerNoResampling::processOutboundEvents() {
while (!mOutboundQueue.empty()) {
const InputMessage& outboundMsg = mOutboundQueue.front();
const status_t result = mChannel->sendMessage(&outboundMsg);
if (result == OK) {
if (outboundMsg.header.type == InputMessage::Type::FINISHED) {
ATRACE_ASYNC_END("InputConsumer processing", /*cookie=*/outboundMsg.header.seq);
}
// Successful send. Erase the entry and keep trying to send more
mOutboundQueue.pop();
continue;
}
// Publisher is busy, try again later. Keep this entry (do not erase)
if (result == WOULD_BLOCK) {
setFdEvents(ALOOPER_EVENT_INPUT | ALOOPER_EVENT_OUTPUT);
return; // try again later
}
// Some other error. Give up
LOG(FATAL) << "Failed to send outbound event on channel '" << mChannel->getName()
<< "'. status=" << statusToString(result) << "(" << result << ")";
}
// The queue is now empty. Tell looper there's no more output to expect.
setFdEvents(ALOOPER_EVENT_INPUT);
}
void InputConsumerNoResampling::finishInputEvent(uint32_t seq, bool handled) {
ensureCalledOnLooperThread(__func__);
mOutboundQueue.push(createFinishedMessage(seq, handled, popConsumeTime(seq)));
// also produce finish events for all batches for this seq (if any)
const auto it = mBatchedSequenceNumbers.find(seq);
if (it != mBatchedSequenceNumbers.end()) {
for (uint32_t subSeq : it->second) {
mOutboundQueue.push(createFinishedMessage(subSeq, handled, popConsumeTime(subSeq)));
}
mBatchedSequenceNumbers.erase(it);
}
processOutboundEvents();
}
bool InputConsumerNoResampling::probablyHasInput() const {
// Ideally, this would only be allowed to run on the looper thread, and in production, it will.
// However, for testing, it's convenient to call this while the looper thread is blocked, so
// we do not call ensureCalledOnLooperThread here.
return (!mBatches.empty()) || mChannel->probablyHasInput();
}
void InputConsumerNoResampling::reportTimeline(int32_t inputEventId, nsecs_t gpuCompletedTime,
nsecs_t presentTime) {
ensureCalledOnLooperThread(__func__);
mOutboundQueue.push(createTimelineMessage(inputEventId, gpuCompletedTime, presentTime));
processOutboundEvents();
}
nsecs_t InputConsumerNoResampling::popConsumeTime(uint32_t seq) {
auto it = mConsumeTimes.find(seq);
// Consume time will be missing if either 'finishInputEvent' is called twice, or if it was
// called for the wrong (synthetic?) input event. Either way, it is a bug that should be fixed.
LOG_ALWAYS_FATAL_IF(it == mConsumeTimes.end(), "Could not find consume time for seq=%" PRIu32,
seq);
nsecs_t consumeTime = it->second;
mConsumeTimes.erase(it);
return consumeTime;
}
void InputConsumerNoResampling::setFdEvents(int events) {
if (mFdEvents != events) {
mFdEvents = events;
if (events != 0) {
mLooper->addFd(mChannel->getFd(), 0, events, mCallback, nullptr);
} else {
mLooper->removeFd(mChannel->getFd());
}
}
}
void InputConsumerNoResampling::handleMessages(std::vector<InputMessage>&& messages) {
// TODO(b/297226446) : add resampling
for (const InputMessage& msg : messages) {
if (msg.header.type == InputMessage::Type::MOTION) {
const int32_t action = msg.body.motion.action;
const DeviceId deviceId = msg.body.motion.deviceId;
const int32_t source = msg.body.motion.source;
const bool batchableEvent = (action == AMOTION_EVENT_ACTION_MOVE ||
action == AMOTION_EVENT_ACTION_HOVER_MOVE) &&
(isFromSource(source, AINPUT_SOURCE_CLASS_POINTER) ||
isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK));
if (batchableEvent) {
// add it to batch
mBatches[deviceId].emplace(msg);
} else {
// consume all pending batches for this device immediately
consumeBatchedInputEvents(deviceId, /*requestedFrameTime=*/std::nullopt);
handleMessage(msg);
}
} else {
// Non-motion events shouldn't force the consumption of pending batched events
handleMessage(msg);
}
}
// At the end of this, if we still have pending batches, notify the receiver about it.
// We need to carefully notify the InputConsumerCallbacks about the pending batch. The receiver
// could choose to consume all events when notified about the batch. That means that the
// "mBatches" variable could change when 'InputConsumerCallbacks::onBatchedInputEventPending' is
// invoked. We also can't notify the InputConsumerCallbacks in a while loop until mBatches is
// empty, because the receiver could choose to not consume the batch immediately.
std::set<int32_t> pendingBatchSources;
for (const auto& [_, pendingMessages] : mBatches) {
// Assume that all messages for a given device has the same source.
pendingBatchSources.insert(pendingMessages.front().body.motion.source);
}
for (const int32_t source : pendingBatchSources) {
const bool sourceStillRemaining =
std::any_of(mBatches.begin(), mBatches.end(), [=](const auto& pair) {
return pair.second.front().body.motion.source == source;
});
if (sourceStillRemaining) {
mCallbacks.onBatchedInputEventPending(source);
}
}
}
std::vector<InputMessage> InputConsumerNoResampling::readAllMessages() {
std::vector<InputMessage> messages;
while (true) {
android::base::Result<InputMessage> result = mChannel->receiveMessage();
if (result.ok()) {
const InputMessage& msg = *result;
const auto [_, inserted] =
mConsumeTimes.emplace(msg.header.seq, systemTime(SYSTEM_TIME_MONOTONIC));
LOG_ALWAYS_FATAL_IF(!inserted, "Already have a consume time for seq=%" PRIu32,
msg.header.seq);
// Trace the event processing timeline - event was just read from the socket
// TODO(b/329777420): distinguish between multiple instances of InputConsumer
// in the same process.
ATRACE_ASYNC_BEGIN("InputConsumer processing", /*cookie=*/msg.header.seq);
messages.push_back(msg);
} else { // !result.ok()
switch (result.error().code()) {
case WOULD_BLOCK: {
return messages;
}
case DEAD_OBJECT: {
LOG(FATAL) << "Got a dead object for " << mChannel->getName();
break;
}
case BAD_VALUE: {
LOG(FATAL) << "Got a bad value for " << mChannel->getName();
break;
}
default: {
LOG(FATAL) << "Unexpected error: " << result.error().message();
break;
}
}
}
}
}
void InputConsumerNoResampling::handleMessage(const InputMessage& msg) const {
switch (msg.header.type) {
case InputMessage::Type::KEY: {
std::unique_ptr<KeyEvent> keyEvent = createKeyEvent(msg);
mCallbacks.onKeyEvent(std::move(keyEvent), msg.header.seq);
break;
}
case InputMessage::Type::MOTION: {
std::unique_ptr<MotionEvent> motionEvent = createMotionEvent(msg);
mCallbacks.onMotionEvent(std::move(motionEvent), msg.header.seq);
break;
}
case InputMessage::Type::FINISHED:
case InputMessage::Type::TIMELINE: {
LOG(FATAL) << "Consumed a " << ftl::enum_string(msg.header.type)
<< " message, which should never be seen by InputConsumer on "
<< mChannel->getName();
break;
}
case InputMessage::Type::FOCUS: {
std::unique_ptr<FocusEvent> focusEvent = createFocusEvent(msg);
mCallbacks.onFocusEvent(std::move(focusEvent), msg.header.seq);
break;
}
case InputMessage::Type::CAPTURE: {
std::unique_ptr<CaptureEvent> captureEvent = createCaptureEvent(msg);
mCallbacks.onCaptureEvent(std::move(captureEvent), msg.header.seq);
break;
}
case InputMessage::Type::DRAG: {
std::unique_ptr<DragEvent> dragEvent = createDragEvent(msg);
mCallbacks.onDragEvent(std::move(dragEvent), msg.header.seq);
break;
}
case InputMessage::Type::TOUCH_MODE: {
std::unique_ptr<TouchModeEvent> touchModeEvent = createTouchModeEvent(msg);
mCallbacks.onTouchModeEvent(std::move(touchModeEvent), msg.header.seq);
break;
}
}
}
std::pair<std::unique_ptr<MotionEvent>, std::optional<uint32_t>>
InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t requestedFrameTime,
std::queue<InputMessage>& messages) {
std::unique_ptr<MotionEvent> motionEvent;
std::optional<uint32_t> firstSeqForBatch;
const nanoseconds resampleLatency =
(mResampler != nullptr) ? mResampler->getResampleLatency() : nanoseconds{0};
const nanoseconds adjustedFrameTime = nanoseconds{requestedFrameTime} - resampleLatency;
while (!messages.empty() &&
(messages.front().body.motion.eventTime <= adjustedFrameTime.count())) {
if (motionEvent == nullptr) {
motionEvent = createMotionEvent(messages.front());
firstSeqForBatch = messages.front().header.seq;
const auto [_, inserted] = mBatchedSequenceNumbers.insert({*firstSeqForBatch, {}});
LOG_IF(FATAL, !inserted)
<< "The sequence " << messages.front().header.seq << " was already present!";
} else {
addSample(*motionEvent, messages.front());
mBatchedSequenceNumbers[*firstSeqForBatch].push_back(messages.front().header.seq);
}
messages.pop();
}
// Check if resampling should be performed.
if (motionEvent != nullptr && isPointerEvent(*motionEvent) && mResampler != nullptr) {
InputMessage* futureSample = nullptr;
if (!messages.empty()) {
futureSample = &messages.front();
}
mResampler->resampleMotionEvent(nanoseconds{requestedFrameTime}, *motionEvent,
futureSample);
}
return std::make_pair(std::move(motionEvent), firstSeqForBatch);
}
bool InputConsumerNoResampling::consumeBatchedInputEvents(
std::optional<DeviceId> deviceId, std::optional<nsecs_t> requestedFrameTime) {
ensureCalledOnLooperThread(__func__);
// When batching is not enabled, we want to consume all events. That's equivalent to having an
// infinite requestedFrameTime.
requestedFrameTime = requestedFrameTime.value_or(std::numeric_limits<nsecs_t>::max());
bool producedEvents = false;
for (auto deviceIdIter = (deviceId.has_value()) ? (mBatches.find(*deviceId))
: (mBatches.begin());
deviceIdIter != mBatches.cend(); ++deviceIdIter) {
std::queue<InputMessage>& messages = deviceIdIter->second;
auto [motion, firstSeqForBatch] = createBatchedMotionEvent(*requestedFrameTime, messages);
if (motion != nullptr) {
LOG_ALWAYS_FATAL_IF(!firstSeqForBatch.has_value());
mCallbacks.onMotionEvent(std::move(motion), *firstSeqForBatch);
producedEvents = true;
} else {
// This is OK, it just means that the requestedFrameTime is too old (all events that we
// have pending are in the future of the requestedFrameTime). Maybe print a warning? If
// there are multiple devices active though, this might be normal and can just be
// ignored, unless none of them resulted in any consumption (in that case, this function
// would already return "false" so we could just leave it up to the caller).
}
if (deviceId.has_value()) {
// We already consumed events for this device. Break here to prevent iterating over the
// other devices.
break;
}
}
std::erase_if(mBatches, [](const auto& pair) { return pair.second.empty(); });
return producedEvents;
}
bool InputConsumerNoResampling::consumeBatchedInputEvents(
std::optional<nsecs_t> requestedFrameTime) {
return consumeBatchedInputEvents(/*deviceId=*/std::nullopt, requestedFrameTime);
}
void InputConsumerNoResampling::ensureCalledOnLooperThread(const char* func) const {
sp<Looper> callingThreadLooper = Looper::getForThread();
if (callingThreadLooper != mLooper) {
LOG(FATAL) << "The function " << func << " can only be called on the looper thread";
}
}
std::string InputConsumerNoResampling::dump() const {
ensureCalledOnLooperThread(__func__);
std::string out;
if (mOutboundQueue.empty()) {
out += "mOutboundQueue: <empty>\n";
} else {
out += "mOutboundQueue:\n";
// Make a copy of mOutboundQueue for printing destructively. Unfortunately std::queue
// doesn't provide a good way to iterate over the entire container.
std::queue<InputMessage> tmpQueue = mOutboundQueue;
while (!tmpQueue.empty()) {
out += std::string(" ") + outboundMessageToString(tmpQueue.front()) + "\n";
tmpQueue.pop();
}
}
if (mBatches.empty()) {
out += "mBatches: <empty>\n";
} else {
out += "mBatches:\n";
for (const auto& [deviceId, messages] : mBatches) {
out += " Device id ";
out += std::to_string(deviceId);
out += ":\n";
// Make a copy of mOutboundQueue for printing destructively. Unfortunately std::queue
// doesn't provide a good way to iterate over the entire container.
std::queue<InputMessage> tmpQueue = messages;
while (!tmpQueue.empty()) {
LOG_ALWAYS_FATAL_IF(tmpQueue.front().header.type != InputMessage::Type::MOTION);
std::unique_ptr<MotionEvent> motion = createMotionEvent(tmpQueue.front());
out += std::string(" ") + streamableToString(*motion) + "\n";
tmpQueue.pop();
}
}
}
return out;
}
} // namespace android