/* * Copyright (C) 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. */ #define LOG_TAG "InputVerifier" #include #include namespace android { /** * Log all of the movements that are sent to this verifier. Helps to identify the streams that lead * to inconsistent events. * Enable this via "adb shell setprop log.tag.InputVerifierLogEvents DEBUG" */ static bool logEvents() { return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "LogEvents", ANDROID_LOG_INFO); } // --- InputVerifier --- InputVerifier::InputVerifier(const std::string& name) : mName(name){}; void InputVerifier::processMovement(int32_t deviceId, int32_t action, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, int32_t flags) { if (logEvents()) { LOG(ERROR) << "Processing " << MotionEvent::actionToString(action) << " for device " << deviceId << " (" << pointerCount << " pointer" << (pointerCount == 1 ? "" : "s") << ") on " << mName; } switch (MotionEvent::getActionMasked(action)) { case AMOTION_EVENT_ACTION_DOWN: { auto [it, inserted] = mTouchingPointerIdsByDevice.insert({deviceId, {}}); if (!inserted) { LOG(FATAL) << "Got ACTION_DOWN, but already have touching pointers " << it->second << " for device " << deviceId << " on " << mName; } it->second.set(pointerProperties[0].id); break; } case AMOTION_EVENT_ACTION_POINTER_DOWN: { auto it = mTouchingPointerIdsByDevice.find(deviceId); if (it == mTouchingPointerIdsByDevice.end()) { LOG(FATAL) << "Got POINTER_DOWN, but no touching pointers for device " << deviceId << " on " << mName; } it->second.set(pointerProperties[MotionEvent::getActionIndex(action)].id); break; } case AMOTION_EVENT_ACTION_MOVE: { ensureTouchingPointersMatch(deviceId, pointerCount, pointerProperties, "MOVE"); break; } case AMOTION_EVENT_ACTION_POINTER_UP: { auto it = mTouchingPointerIdsByDevice.find(deviceId); if (it == mTouchingPointerIdsByDevice.end()) { LOG(FATAL) << "Got POINTER_UP, but no touching pointers for device " << deviceId << " on " << mName; } it->second.reset(pointerProperties[MotionEvent::getActionIndex(action)].id); break; } case AMOTION_EVENT_ACTION_UP: { auto it = mTouchingPointerIdsByDevice.find(deviceId); if (it == mTouchingPointerIdsByDevice.end()) { LOG(FATAL) << "Got ACTION_UP, but no record for deviceId " << deviceId << " on " << mName; } const auto& [_, touchingPointerIds] = *it; if (touchingPointerIds.count() != 1) { LOG(FATAL) << "Got ACTION_UP, but we have pointers: " << touchingPointerIds << " for deviceId " << deviceId << " on " << mName; } const int32_t pointerId = pointerProperties[0].id; if (!touchingPointerIds.test(pointerId)) { LOG(FATAL) << "Got ACTION_UP, but pointerId " << pointerId << " is not touching. Touching pointers: " << touchingPointerIds << " for deviceId " << deviceId << " on " << mName; } mTouchingPointerIdsByDevice.erase(it); break; } case AMOTION_EVENT_ACTION_CANCEL: { if ((flags & AMOTION_EVENT_FLAG_CANCELED) != AMOTION_EVENT_FLAG_CANCELED) { LOG(FATAL) << "For ACTION_CANCEL, must set FLAG_CANCELED"; } ensureTouchingPointersMatch(deviceId, pointerCount, pointerProperties, "CANCEL"); mTouchingPointerIdsByDevice.erase(deviceId); break; } } } void InputVerifier::ensureTouchingPointersMatch(int32_t deviceId, uint32_t pointerCount, const PointerProperties* pointerProperties, const char* action) const { auto it = mTouchingPointerIdsByDevice.find(deviceId); if (it == mTouchingPointerIdsByDevice.end()) { LOG(FATAL) << "Got " << action << ", but no touching pointers for device " << deviceId << " on " << mName; } const auto& [_, touchingPointerIds] = *it; for (size_t i = 0; i < pointerCount; i++) { const int32_t pointerId = pointerProperties[i].id; if (!touchingPointerIds.test(pointerId)) { LOG(FATAL) << "Got " << action << " for pointerId " << pointerId << " but the touching pointers are " << touchingPointerIds << " on " << mName; } } }; } // namespace android