add inputeventconsistencyverifier

This commit is contained in:
houzh 2024-02-04 10:12:44 +00:00
parent 09b5e14fdf
commit c186116720
9 changed files with 932 additions and 12 deletions

View File

@ -737,9 +737,9 @@ nsecs_t MotionEvent::getHistoricalEventTime(size_t historyPos) const{
}
}
nsecs_t MotionEvent::getHistoricalEventTimeNano(size_t historyPos) const{
nsecs_t MotionEvent::getHistoricalEventTimeNanos(size_t historyPos) const{
if(historyPos==HISTORY_CURRENT){
return getEventTimeNano();
return getEventTimeNanos();
}else{
const size_t historySize = getHistorySize();
if(historyPos<0||historyPos>=historySize)return 0;

View File

@ -88,14 +88,17 @@ public:
InputEvent();
virtual ~InputEvent();
virtual int getType()const=0;
virtual InputEvent*copy()const=0;
void initialize(int32_t deviceId, int32_t source);
void initialize(const InputEvent& from);
void setSource(int source){mSource=source;}
int getSource()const{return mSource;}
bool isFromSource(int s)const;
int getDeviceId(){return mDeviceId;}
int getDeviceId()const{return mDeviceId;}
long getSequenceNumber()const{return mSeq;}
virtual nsecs_t getEventTimeNano() const { return mEventTime*NS_PER_MS; }
virtual bool isTainted()const=0;
virtual void setTainted(bool)=0;
virtual nsecs_t getEventTimeNanos() const { return mEventTime*NS_PER_MS; }
virtual nsecs_t getEventTime()const{ return mEventTime;}
virtual void recycle();/*only obtained event can call recycle*/
};
@ -219,9 +222,15 @@ public:
int deviceId, int scancode, int flags, int source/*,std::string characters*/);
static KeyEvent* obtain(const KeyEvent& other);
virtual int getType()const {return EV_KEY;}
KeyEvent*copy()const override{return obtain(*this);}
int getKeyCode()const {return mKeyCode;}
void setKeyCode(int k){mKeyCode=k;}
int getFlags()const{return mFlags;}
inline bool isTainted()const{return (mFlags&FLAG_TAINTED)!=0;}
inline void setTainted(bool tainted){
if(tainted)mFlags|=FLAG_TAINTED;
else mFlags&=~FLAG_TAINTED;
}
inline int32_t getScanCode() const { return mScanCode; }
inline int32_t getMetaState() const { return mMetaState; }
int getAction()const{return mAction;}//key up-->0 down-->1
@ -346,6 +355,12 @@ public:
FLAG_IS_GENERATED_GESTURE = 0x8,
FLAG_TAINTED = 0x80000000,
FLAG_TARGET_ACCESSIBILITY_FOCUS = 0x40000000,
CLASSIFICATION_NONE = 0,
CLASSIFICATION_AMBIGUOUS_GESTURE = 1,
CLASSIFICATION_DEEP_PRESS = 2,
CLASSIFICATION_TWO_FINGER_SWIPE = 3,
CLASSIFICATION_MULTI_FINGER_SWIPE = 4,
CLASSIFICATION_PINCH = 5,
};
private:
static const int HISTORY_CURRENT = -0x80000000;
@ -368,7 +383,7 @@ protected:
public:
MotionEvent();
MotionEvent(const MotionEvent&m);
MotionEvent*copy()const override{return obtain(*this);}
void initialize(int deviceId,int source,int action,int actionButton,
int flags, int edgeFlags,int metaState, int buttonState,
float xOffset, float yOffset, float xPrecision, float yPrecision,
@ -401,6 +416,11 @@ public:
inline nsecs_t getDownTime()const{return mDownTime;}
inline int32_t getFlags() const { return mFlags; }
inline void setFlags(int32_t flags) { mFlags = flags; }
inline bool isTainted()const{return (mFlags&FLAG_TAINTED)!=0;}
inline void setTainted(bool tainted){
if(tainted)mFlags|=FLAG_TAINTED;
else mFlags&=~FLAG_TAINTED;
}
inline int32_t getEdgeFlags() const { return mEdgeFlags; }
inline void setEdgeFlags(int32_t edgeFlags) { mEdgeFlags = edgeFlags; }
inline bool isHoverExitPending()const{return getFlags()&FLAG_HOVER_EXIT_PENDING!=0;}
@ -421,7 +441,7 @@ public:
inline float getYPrecision() const { return mYPrecision; }
inline size_t getHistorySize() const { return mSampleEventTimes.size() - 1; }
nsecs_t getHistoricalEventTime(size_t historicalIndex) const;
nsecs_t getHistoricalEventTimeNano(size_t historicalIndex) const;
nsecs_t getHistoricalEventTimeNanos(size_t historicalIndex) const;
void getPointerCoords(int pointerIndex, PointerCoords& outPointerCoords){
getHistoricalRawPointerCoords(pointerIndex,HISTORY_CURRENT,outPointerCoords);
}

View File

@ -71,7 +71,7 @@ bool UIEventSource::postDelayed(Runnable& run,uint32_t delayedtime){
RUNNER runner;
runner.time = SystemClock::uptimeMillis() + delayedtime;
runner.run = run;
for(auto itr = mRunnables.begin();itr != mRunnables.end();itr++){
if(runner.time < itr->time){
mRunnables.insert(itr,runner);

View File

@ -0,0 +1,676 @@
#include <view/inputeventconsistencyverifier.h>
#include <core/inputdevice.h>
#include <core/uievents.h>
namespace cdroid{
static const char* EVENT_TYPE_KEY = "KeyEvent";
static const char* EVENT_TYPE_TRACKBALL = "TrackballEvent";
static const char* EVENT_TYPE_TOUCH = "TouchEvent";
static const char* EVENT_TYPE_GENERIC_MOTION = "GenericMotionEvent";
InputEventConsistencyVerifier::InputEventConsistencyVerifier(Object* caller, int flags) {
mCaller = caller;
mFlags = flags;
mMostRecentEventIndex = 0;
mCurrentEvent = nullptr;
mKeyStateList = nullptr;
//mLogTag = (logTag != null) ? logTag : "InputEventConsistencyVerifier";
}
/**
* Determines whether the instrumentation should be enabled.
* @return True if it should be enabled.
*/
bool InputEventConsistencyVerifier::isInstrumentationEnabled() {
#ifndef _DEBUG
return true;//IS_ENG_BUILD;
#else
return false;
#endif
}
void InputEventConsistencyVerifier::reset() {
mLastEventSeq = -1;
mLastNestingLevel = 0;
mTrackballDown = false;
mTrackballUnhandled = false;
mTouchEventStreamPointers = 0;
mTouchEventStreamIsTainted = false;
mTouchEventStreamUnhandled = false;
mHoverEntered = false;
mButtonsPressed = 0;
while (mKeyStateList != nullptr) {
KeyState* state = mKeyStateList;
mKeyStateList = state->next;
state->recycle();
}
}
/**
* Checks an arbitrary input event.
* @param event The event.
* @param nestingLevel The nesting level: 0 if called from the base class,
* or 1 from a subclass. If the event was already checked by this consistency verifier
* at a higher nesting level, it will not be checked again. Used to handle the situation
* where a subclass dispatching method delegates to its superclass's dispatching method
* and both dispatching methods call into the consistency verifier.
*/
void InputEventConsistencyVerifier::onInputEvent(InputEvent& event, int nestingLevel) {
if (dynamic_cast<KeyEvent*>(&event)) {
KeyEvent& keyEvent = (KeyEvent&)event;
onKeyEvent(keyEvent, nestingLevel);
} else {
MotionEvent& motionEvent = (MotionEvent&)event;
if (motionEvent.isTouchEvent()) {
onTouchEvent(motionEvent, nestingLevel);
} else if ((motionEvent.getSource() & InputDevice::SOURCE_CLASS_TRACKBALL) != 0) {
onTrackballEvent(motionEvent, nestingLevel);
} else {
onGenericMotionEvent(motionEvent, nestingLevel);
}
}
}
/**
* Checks a key event.
* @param event The event.
* @param nestingLevel The nesting level: 0 if called from the base class,
* or 1 from a subclass. If the event was already checked by this consistency verifier
* at a higher nesting level, it will not be checked again. Used to handle the situation
* where a subclass dispatching method delegates to its superclass's dispatching method
* and both dispatching methods call into the consistency verifier.
*/
void InputEventConsistencyVerifier::onKeyEvent(KeyEvent& event, int nestingLevel) {
if (!startEvent(event, nestingLevel, EVENT_TYPE_KEY)) {
return;
}
try {
ensureMetaStateIsNormalized(event.getMetaState());
const int action = event.getAction();
const int deviceId = event.getDeviceId();
const int source = event.getSource();
const int keyCode = event.getKeyCode();
switch (action) {
case KeyEvent::ACTION_DOWN: {
KeyState* state = findKeyState(deviceId, source, keyCode, /*remove*/ false);
if (state != nullptr) {
// If the key is already down, ensure it is a repeat.
// We don't perform this check when processing raw device input
// because the input dispatcher itself is responsible for setting
// the key repeat count before it delivers input events.
if (state->unhandled) {
state->unhandled = false;
} else if ((mFlags & FLAG_RAW_DEVICE_INPUT) == 0
&& event.getRepeatCount() == 0) {
problem("ACTION_DOWN but key is already down and this event is not a key repeat.");
}
} else {
addKeyState(deviceId, source, keyCode);
}
break;
}
case KeyEvent::ACTION_UP: {
KeyState* state = findKeyState(deviceId, source, keyCode, /*remove*/ true);
if (state == nullptr) {
problem("ACTION_UP but key was not down.");
} else {
state->recycle();
}
break;
}
case KeyEvent::ACTION_MULTIPLE:
break;
default:
problem("Invalid action " + KeyEvent::actionToString(action)
+ " for key event.");
break;
}
}catch(std::exception&e){
}
finishEvent();
}
/**
* Checks a trackball event.
* @param event The event.
* @param nestingLevel The nesting level: 0 if called from the base class,
* or 1 from a subclass. If the event was already checked by this consistency verifier
* at a higher nesting level, it will not be checked again. Used to handle the situation
* where a subclass dispatching method delegates to its superclass's dispatching method
* and both dispatching methods call into the consistency verifier.
*/
void InputEventConsistencyVerifier::onTrackballEvent(MotionEvent& event, int nestingLevel) {
if (!startEvent(event, nestingLevel, EVENT_TYPE_TRACKBALL)) {
return;
}
try {
ensureMetaStateIsNormalized(event.getMetaState());
const int action = event.getAction();
const int source = event.getSource();
if ((source & InputDevice::SOURCE_CLASS_TRACKBALL) != 0) {
switch (action) {
case MotionEvent::ACTION_DOWN:
if (mTrackballDown && !mTrackballUnhandled) {
problem("ACTION_DOWN but trackball is already down.");
} else {
mTrackballDown = true;
mTrackballUnhandled = false;
}
ensureHistorySizeIsZeroForThisAction(event);
ensurePointerCountIsOneForThisAction(event);
break;
case MotionEvent::ACTION_UP:
if (!mTrackballDown) {
problem("ACTION_UP but trackball is not down.");
} else {
mTrackballDown = false;
mTrackballUnhandled = false;
}
ensureHistorySizeIsZeroForThisAction(event);
ensurePointerCountIsOneForThisAction(event);
break;
case MotionEvent::ACTION_MOVE:
ensurePointerCountIsOneForThisAction(event);
break;
default:
problem("Invalid action " + MotionEvent::actionToString(action)
+ " for trackball event.");
break;
}
if (mTrackballDown && event.getPressure(0) <= 0.f) {
problem("Trackball is down but pressure is not greater than 0.");
} else if (!mTrackballDown && event.getPressure(0) != 0) {
problem("Trackball is up but pressure is not equal to 0.");
}
} else {
problem("Source was not SOURCE_CLASS_TRACKBALL.");
}
}catch(std::exception&e){
}
finishEvent();
}
static int bitCount(int number) {
int count = 0;
while (number) {
count += number & 1;
number >>= 1;
}
return count;
}
/**
* Checks a touch event.
* @param event The event.
* @param nestingLevel The nesting level: 0 if called from the base class,
* or 1 from a subclass. If the event was already checked by this consistency verifier
* at a higher nesting level, it will not be checked again. Used to handle the situation
* where a subclass dispatching method delegates to its superclass's dispatching method
* and both dispatching methods call into the consistency verifier.
*/
void InputEventConsistencyVerifier::onTouchEvent(MotionEvent& event, int nestingLevel) {
if (!startEvent(event, nestingLevel, EVENT_TYPE_TOUCH)) {
return;
}
const int action = event.getAction();
const bool newStream = action == MotionEvent::ACTION_DOWN
|| action == MotionEvent::ACTION_CANCEL || action == MotionEvent::ACTION_OUTSIDE;
if (newStream && (mTouchEventStreamIsTainted || mTouchEventStreamUnhandled)) {
mTouchEventStreamIsTainted = false;
mTouchEventStreamUnhandled = false;
mTouchEventStreamPointers = 0;
}
if (mTouchEventStreamIsTainted) {
event.setTainted(true);
}
try {
ensureMetaStateIsNormalized(event.getMetaState());
const int deviceId = event.getDeviceId();
const int source = event.getSource();
if (!newStream && mTouchEventStreamDeviceId != -1
&& (mTouchEventStreamDeviceId != deviceId
|| mTouchEventStreamSource != source)) {
problem("Touch event stream contains events from multiple sources: previous device id %d"
", previous source %x, new device id %d, new source %x",
mTouchEventStreamDeviceId,mTouchEventStreamSource,deviceId,source);
}
mTouchEventStreamDeviceId = deviceId;
mTouchEventStreamSource = source;
const int pointerCount = event.getPointerCount();
if ((source & InputDevice::SOURCE_CLASS_POINTER) != 0) {
switch (action) {
case MotionEvent::ACTION_DOWN:
if (mTouchEventStreamPointers != 0) {
problem("ACTION_DOWN but pointers are already down. "
"Probably missing ACTION_UP from previous gesture.");
}
ensureHistorySizeIsZeroForThisAction(event);
ensurePointerCountIsOneForThisAction(event);
mTouchEventStreamPointers = 1 << event.getPointerId(0);
break;
case MotionEvent::ACTION_UP:
ensureHistorySizeIsZeroForThisAction(event);
ensurePointerCountIsOneForThisAction(event);
mTouchEventStreamPointers = 0;
mTouchEventStreamIsTainted = false;
break;
case MotionEvent::ACTION_MOVE: {
const int expectedPointerCount = bitCount(mTouchEventStreamPointers);
if (pointerCount != expectedPointerCount) {
problem("ACTION_MOVE contained %d pointers but there are currently %d pointers down.",pointerCount,expectedPointerCount);
mTouchEventStreamIsTainted = true;
}
break;
}
case MotionEvent::ACTION_CANCEL:
mTouchEventStreamPointers = 0;
mTouchEventStreamIsTainted = false;
break;
case MotionEvent::ACTION_OUTSIDE:
if (mTouchEventStreamPointers != 0) {
problem("ACTION_OUTSIDE but pointers are still down.");
}
ensureHistorySizeIsZeroForThisAction(event);
ensurePointerCountIsOneForThisAction(event);
mTouchEventStreamIsTainted = false;
break;
default: {
const int actionMasked = event.getActionMasked();
const int actionIndex = event.getActionIndex();
if (actionMasked == MotionEvent::ACTION_POINTER_DOWN) {
if (mTouchEventStreamPointers == 0) {
problem("ACTION_POINTER_DOWN but no other pointers were down.");
mTouchEventStreamIsTainted = true;
}
if (actionIndex < 0 || actionIndex >= pointerCount) {
problem("ACTION_POINTER_DOWN index is %d but the pointer count is %d.",
actionIndex,pointerCount);
mTouchEventStreamIsTainted = true;
} else {
const int id = event.getPointerId(actionIndex);
const int idBit = 1 << id;
if ((mTouchEventStreamPointers & idBit) != 0) {
problem("ACTION_POINTER_DOWN specified pointer id %d which is already down.",id);
mTouchEventStreamIsTainted = true;
} else {
mTouchEventStreamPointers |= idBit;
}
}
ensureHistorySizeIsZeroForThisAction(event);
} else if (actionMasked == MotionEvent::ACTION_POINTER_UP) {
if (actionIndex < 0 || actionIndex >= pointerCount) {
problem("ACTION_POINTER_UP index is % but the pointer count is %d.",actionIndex,pointerCount);
mTouchEventStreamIsTainted = true;
} else {
const int id = event.getPointerId(actionIndex);
const int idBit = 1 << id;
if ((mTouchEventStreamPointers & idBit) == 0) {
problem("ACTION_POINTER_UP specified pointer id %d which is not currently down.",id);
mTouchEventStreamIsTainted = true;
} else {
mTouchEventStreamPointers &= ~idBit;
}
}
ensureHistorySizeIsZeroForThisAction(event);
} else {
problem("Invalid action %s for touch event.",MotionEvent::actionToString(action));
}
break;
}
}
} else {
problem("Source was not SOURCE_CLASS_POINTER.");
}
}catch(std::exception&e){
}
finishEvent();
}
/**
* Checks a generic motion event.
* @param event The event.
* @param nestingLevel The nesting level: 0 if called from the base class,
* or 1 from a subclass. If the event was already checked by this consistency verifier
* at a higher nesting level, it will not be checked again. Used to handle the situation
* where a subclass dispatching method delegates to its superclass's dispatching method
* and both dispatching methods call into the consistency verifier.
*/
void InputEventConsistencyVerifier::onGenericMotionEvent(MotionEvent& event, int nestingLevel) {
if (!startEvent(event, nestingLevel, EVENT_TYPE_GENERIC_MOTION)) {
return;
}
try {
ensureMetaStateIsNormalized(event.getMetaState());
const int action = event.getAction();
const int source = event.getSource();
const int buttonState = event.getButtonState();
const int actionButton = event.getActionButton();
if ((source & InputDevice::SOURCE_CLASS_POINTER) != 0) {
switch (action) {
case MotionEvent::ACTION_HOVER_ENTER:
ensurePointerCountIsOneForThisAction(event);
mHoverEntered = true;
break;
case MotionEvent::ACTION_HOVER_MOVE:
ensurePointerCountIsOneForThisAction(event);
break;
case MotionEvent::ACTION_HOVER_EXIT:
ensurePointerCountIsOneForThisAction(event);
if (!mHoverEntered) {
problem("ACTION_HOVER_EXIT without prior ACTION_HOVER_ENTER");
}
mHoverEntered = false;
break;
case MotionEvent::ACTION_SCROLL:
ensureHistorySizeIsZeroForThisAction(event);
ensurePointerCountIsOneForThisAction(event);
break;
case MotionEvent::ACTION_BUTTON_PRESS:
ensureActionButtonIsNonZeroForThisAction(event);
if ((mButtonsPressed & actionButton) != 0) {
problem("Action button for ACTION_BUTTON_PRESS event is %d"
", but it has already been pressed and "
"has yet to be released.",actionButton);
}
mButtonsPressed |= actionButton;
// The system will automatically mirror the stylus buttons onto the button
// state as the old set of generic buttons for apps targeting pre-M. If
// it looks this has happened, go ahead and set the generic buttons as
// pressed to prevent spurious errors.
if (actionButton == MotionEvent::BUTTON_STYLUS_PRIMARY &&
(buttonState & MotionEvent::BUTTON_SECONDARY) != 0) {
mButtonsPressed |= MotionEvent::BUTTON_SECONDARY;
} else if (actionButton == MotionEvent::BUTTON_STYLUS_SECONDARY &&
(buttonState & MotionEvent::BUTTON_TERTIARY) != 0) {
mButtonsPressed |= MotionEvent::BUTTON_TERTIARY;
}
if (mButtonsPressed != buttonState) {
problem("Reported button state differs from expect button state "
"based on press and release events. Is 0x%08x but expected 0x%08x.",
buttonState, mButtonsPressed);
}
break;
case MotionEvent::ACTION_BUTTON_RELEASE:
ensureActionButtonIsNonZeroForThisAction(event);
if ((mButtonsPressed & actionButton) != actionButton) {
problem("Action button for ACTION_BUTTON_RELEASE event is %d"
", but it was either never pressed or has "
"already been released.",actionButton);
}
mButtonsPressed &= ~actionButton;
// The system will automatically mirror the stylus buttons onto the button
// state as the old set of generic buttons for apps targeting pre-M. If
// it looks this has happened, go ahead and set the generic buttons as
// released to prevent spurious errors.
if (actionButton == MotionEvent::BUTTON_STYLUS_PRIMARY &&
(buttonState & MotionEvent::BUTTON_SECONDARY) == 0) {
mButtonsPressed &= ~MotionEvent::BUTTON_SECONDARY;
} else if (actionButton == MotionEvent::BUTTON_STYLUS_SECONDARY &&
(buttonState & MotionEvent::BUTTON_TERTIARY) == 0) {
mButtonsPressed &= ~MotionEvent::BUTTON_TERTIARY;
}
if (mButtonsPressed != buttonState) {
problem("Reported button state differs from "
"expected button state based on press and release events. "
"Is 0x%08x but expected 0x%08x.",
buttonState, mButtonsPressed);
}
break;
default:
problem("Invalid action for generic pointer event.");
break;
}
} else if ((source & InputDevice::SOURCE_CLASS_JOYSTICK) != 0) {
switch (action) {
case MotionEvent::ACTION_MOVE:
ensurePointerCountIsOneForThisAction(event);
break;
default:
problem("Invalid action for generic joystick event.");
break;
}
}
}catch(std::exception&){
}
finishEvent();
}
/**
* Notifies the verifier that a given event was unhandled and the rest of the
* trace for the event should be ignored.
* This method should only be called if the event was previously checked by
* the consistency verifier using {@link #onInputEvent} and other methods.
* @param event The event.
* @param nestingLevel The nesting level: 0 if called from the base class,
* or 1 from a subclass. If the event was already checked by this consistency verifier
* at a higher nesting level, it will not be checked again. Used to handle the situation
* where a subclass dispatching method delegates to its superclass's dispatching method
* and both dispatching methods call into the consistency verifier.
*/
void InputEventConsistencyVerifier::onUnhandledEvent(InputEvent& event, int nestingLevel) {
if (nestingLevel != mLastNestingLevel) {
return;
}
if (mRecentEventsUnhandled.size()) {
mRecentEventsUnhandled[mMostRecentEventIndex] = true;
}
if (dynamic_cast<KeyEvent*>(&event)) {
const KeyEvent& keyEvent = (KeyEvent&)event;
const int deviceId = keyEvent.getDeviceId();
const int source = keyEvent.getSource();
const int keyCode = keyEvent.getKeyCode();
KeyState* state = findKeyState(deviceId, source, keyCode, /*remove*/ false);
if (state != nullptr) {
state->unhandled = true;
}
} else {
MotionEvent& motionEvent = (MotionEvent&)event;
if (motionEvent.isTouchEvent()) {
mTouchEventStreamUnhandled = true;
} else if ((motionEvent.getSource() & InputDevice::SOURCE_CLASS_TRACKBALL) != 0) {
if (mTrackballDown) {
mTrackballUnhandled = true;
}
}
}
}
void InputEventConsistencyVerifier::ensureMetaStateIsNormalized(int metaState) {
const int normalizedMetaState = KeyEvent::normalizeMetaState(metaState);
if (normalizedMetaState != metaState) {
problem("Metastate not normalized. Was 0x%08x but expected 0x%08x.",
metaState, normalizedMetaState);
}
}
void InputEventConsistencyVerifier::ensurePointerCountIsOneForThisAction(MotionEvent& event) {
const int pointerCount = event.getPointerCount();
if (pointerCount != 1) {
problem("Pointer count is %d but it should always be 1 for %s",
pointerCount, MotionEvent::actionToString(event.getAction()));
}
}
void InputEventConsistencyVerifier::ensureActionButtonIsNonZeroForThisAction(MotionEvent& event) {
const int actionButton = event.getActionButton();
if (actionButton == 0) {
problem("No action button set. Action button should always be non-zero for %s",
MotionEvent::actionToString(event.getAction()));
}
}
void InputEventConsistencyVerifier::ensureHistorySizeIsZeroForThisAction(MotionEvent& event) {
const int historySize = event.getHistorySize();
if (historySize != 0) {
problem("History size is %d but it should always be 0 for %s",
historySize,MotionEvent::actionToString(event.getAction()));
}
}
bool InputEventConsistencyVerifier::startEvent(InputEvent& event, int nestingLevel,const std::string& eventType) {
// Ignore the event if we already checked it at a higher nesting level.
const int seq = event.getSequenceNumber();
if (seq == mLastEventSeq && nestingLevel < mLastNestingLevel
&& eventType == mLastEventType) {
return false;
}
if (nestingLevel > 0) {
mLastEventSeq = seq;
mLastEventType = eventType;
mLastNestingLevel = nestingLevel;
} else {
mLastEventSeq = -1;
mLastEventType.clear();// = nullptr;
mLastNestingLevel = 0;
}
mCurrentEvent = &event;
mCurrentEventType = eventType;
return true;
}
void InputEventConsistencyVerifier::finishEvent() {
if (mViolationMessage.str().length() != 0) {
if (!mCurrentEvent->isTainted()) {
// Write a log message only if the event was not already tainted.
//mViolationMessage.append("\n in ").append(mCaller);
mViolationMessage<<("\n ");
appendEvent(mViolationMessage, 0, *mCurrentEvent, false);
if (RECENT_EVENTS_TO_LOG != 0 && mRecentEvents.size()) {
mViolationMessage<<("\n -- recent events --");
for (int i = 0; i < RECENT_EVENTS_TO_LOG; i++) {
const int index = (mMostRecentEventIndex + RECENT_EVENTS_TO_LOG - i)
% RECENT_EVENTS_TO_LOG;
InputEvent* event = mRecentEvents[index];
if (event == nullptr) {
break;
}
mViolationMessage<<("\n ");
appendEvent(mViolationMessage, i + 1, *event, mRecentEventsUnhandled[index]);
}
}
LOGD("%s",mViolationMessage.str().c_str());
// Taint the event so that we do not generate additional violations from it
// further downstream.
mCurrentEvent->setTainted(true);
}
mViolationMessage.clear();//setLength(0);
}
if (RECENT_EVENTS_TO_LOG != 0) {
if (mRecentEvents.empty()) {
mRecentEvents.resize(RECENT_EVENTS_TO_LOG);
mRecentEventsUnhandled.resize(RECENT_EVENTS_TO_LOG);
}
const int index = (mMostRecentEventIndex + 1) % RECENT_EVENTS_TO_LOG;
mMostRecentEventIndex = index;
if (mRecentEvents[index] != nullptr) {
mRecentEvents[index]->recycle();
}
mRecentEvents[index] = mCurrentEvent->copy();
mRecentEventsUnhandled[index] = false;
}
mCurrentEvent = nullptr;
mCurrentEventType.clear();
}
void InputEventConsistencyVerifier::appendEvent(std::ostringstream& message, int index,
const InputEvent& event, bool unhandled) {
message<<index<<": sent at "<<(event.getEventTimeNanos());
message<<", ";
if (unhandled) {
message<<"(unhandled) ";
}
//message<<event;
}
void InputEventConsistencyVerifier::problem(const std::string& message,...) {
va_list args;
va_start(args, message);
LogPrintf(0,"tag","fun",0,message.c_str(),args);
va_end(args);
if (mViolationMessage.str().length() == 0) {
mViolationMessage<<mCurrentEventType<<": ";
} else {
mViolationMessage<<"\n ";
}
mViolationMessage<<message;
}
InputEventConsistencyVerifier::KeyState* InputEventConsistencyVerifier::findKeyState(int deviceId, int source, int keyCode, bool remove) {
KeyState* last = nullptr;
KeyState* state = mKeyStateList;
while (state != nullptr) {
if (state->deviceId == deviceId && state->source == source
&& state->keyCode == keyCode) {
if (remove) {
if (last != nullptr) {
last->next = state->next;
} else {
mKeyStateList = state->next;
}
state->next = nullptr;
}
return state;
}
last = state;
state = state->next;
}
return nullptr;
}
void InputEventConsistencyVerifier::addKeyState(int deviceId, int source, int keyCode) {
KeyState* state = KeyState::obtain(deviceId, source, keyCode);
state->next = mKeyStateList;
mKeyStateList = state;
}
InputEventConsistencyVerifier::KeyState* InputEventConsistencyVerifier::KeyState::mRecycledList = nullptr;
InputEventConsistencyVerifier::KeyState::KeyState() {
}
InputEventConsistencyVerifier::KeyState* InputEventConsistencyVerifier::KeyState::obtain(int deviceId, int source, int keyCode) {
KeyState* state;
state = mRecycledList;
if (state != nullptr) {
mRecycledList = state->next;
} else {
state = new KeyState();
}
state->deviceId = deviceId;
state->source = source;
state->keyCode = keyCode;
state->unhandled = false;
return state;
}
void InputEventConsistencyVerifier::KeyState::recycle() {
next = mRecycledList;
mRecycledList = next;
}
}/*endof namespace*/

View File

@ -0,0 +1,196 @@
#ifndef __INPUTEVENT_CONSISTENCYVERIFIER_H__
#define __INPUTEVENT_CONSISTENCYVERIFIER_H__
#include <core/uievents.h>
#include <sstream>
#include <stdarg.h>
namespace cdroid{
class Object;
class InputEventConsistencyVerifier {
private:
class KeyState;
// The number of recent events to log when a problem is detected.
// Can be set to 0 to disable logging recent events but the runtime overhead of
// this feature is negligible on current hardware.
static constexpr int RECENT_EVENTS_TO_LOG = 5;
// The object to which the verifier is attached.
Object* mCaller;
// Consistency verifier flags.
int mFlags;
// Tag for logging which a client can set to help distinguish the output
// from different verifiers since several can be active at the same time.
// If not provided defaults to the simple class name.
std::string mLogTag;
// The most recently checked event and the nesting level at which it was checked.
// This is only set when the verifier is called from a nesting level greater than 0
// so that the verifier can detect when it has been asked to verify the same event twice.
// It does not make sense to examine the contents of the last event since it may have
// been recycled.
int mLastEventSeq;
std::string mLastEventType;
int mLastNestingLevel;
// Copy of the most recent events.
std::vector<InputEvent*> mRecentEvents;
std::vector<bool> mRecentEventsUnhandled;
int mMostRecentEventIndex;
// Current event and its type.
InputEvent* mCurrentEvent;
std::string mCurrentEventType;
// Linked list of key state objects.
KeyState* mKeyStateList;
// Current state of the trackball.
bool mTrackballDown;
bool mTrackballUnhandled;
// Bitfield of pointer ids that are currently down.
// Assumes that the largest possible pointer id is 31, which is potentially subject to change.
// (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h)
int mTouchEventStreamPointers;
// The device id and source of the current stream of touch events.
int mTouchEventStreamDeviceId = -1;
int mTouchEventStreamSource;
// Set to true when we discover that the touch event stream is inconsistent.
// Reset on down or cancel.
bool mTouchEventStreamIsTainted;
// Set to true if the touch event stream is partially unhandled.
bool mTouchEventStreamUnhandled;
// Set to true if we received hover enter.
bool mHoverEntered;
// The bitset of buttons which we've received ACTION_BUTTON_PRESS for.
int mButtonsPressed;
// The current violation message.
std::ostringstream mViolationMessage;
/**
* Indicates that the verifier is intended to act on raw device input event streams.
* Disables certain checks for invariants that are established by the input dispatcher
* itself as it delivers input events, such as key repeating behavior.
*/
private:
void ensureMetaStateIsNormalized(int metaState);
void ensurePointerCountIsOneForThisAction(MotionEvent& event);
void ensureActionButtonIsNonZeroForThisAction(MotionEvent& event);
void ensureHistorySizeIsZeroForThisAction(MotionEvent& event);
bool startEvent(InputEvent& event, int nestingLevel, const std::string& eventType);
void finishEvent();
static void appendEvent(std::ostringstream& message, int index,const InputEvent& event, bool unhandled);
void problem(const std::string& message,...);
KeyState* findKeyState(int deviceId, int source, int keyCode, bool remove);
void addKeyState(int deviceId, int source, int keyCode);
public:
static const int FLAG_RAW_DEVICE_INPUT = 1 << 0;
/**
* Creates an input consistency verifier.
* @param caller The object to which the verifier is attached.
* @param flags Flags to the verifier, or 0 if none.
* @param logTag Tag for logging. If null defaults to the short class name.
*/
InputEventConsistencyVerifier(Object* caller, int flags);
/**
* Determines whether the instrumentation should be enabled.
* @return True if it should be enabled.
*/
static bool isInstrumentationEnabled();
void reset();
/**
* Checks an arbitrary input event.
* @param event The event.
* @param nestingLevel The nesting level: 0 if called from the base class,
* or 1 from a subclass. If the event was already checked by this consistency verifier
* at a higher nesting level, it will not be checked again. Used to handle the situation
* where a subclass dispatching method delegates to its superclass's dispatching method
* and both dispatching methods call into the consistency verifier.
*/
void onInputEvent(InputEvent& event, int nestingLevel);
/**
* Checks a key event.
* @param event The event.
* @param nestingLevel The nesting level: 0 if called from the base class,
* or 1 from a subclass. If the event was already checked by this consistency verifier
* at a higher nesting level, it will not be checked again. Used to handle the situation
* where a subclass dispatching method delegates to its superclass's dispatching method
* and both dispatching methods call into the consistency verifier.
*/
void onKeyEvent(KeyEvent& event, int nestingLevel);
/**
* Checks a trackball event.
* @param event The event.
* @param nestingLevel The nesting level: 0 if called from the base class,
* or 1 from a subclass. If the event was already checked by this consistency verifier
* at a higher nesting level, it will not be checked again. Used to handle the situation
* where a subclass dispatching method delegates to its superclass's dispatching method
* and both dispatching methods call into the consistency verifier.
*/
void onTrackballEvent(MotionEvent& event, int nestingLevel);
/**
* Checks a touch event.
* @param event The event.
* @param nestingLevel The nesting level: 0 if called from the base class,
* or 1 from a subclass. If the event was already checked by this consistency verifier
* at a higher nesting level, it will not be checked again. Used to handle the situation
* where a subclass dispatching method delegates to its superclass's dispatching method
* and both dispatching methods call into the consistency verifier.
*/
void onTouchEvent(MotionEvent& event, int nestingLevel);
/**
* Checks a generic motion event.
* @param event The event.
* @param nestingLevel The nesting level: 0 if called from the base class,
* or 1 from a subclass. If the event was already checked by this consistency verifier
* at a higher nesting level, it will not be checked again. Used to handle the situation
* where a subclass dispatching method delegates to its superclass's dispatching method
* and both dispatching methods call into the consistency verifier.
*/
void onGenericMotionEvent(MotionEvent& event, int nestingLevel);
/**
* Notifies the verifier that a given event was unhandled and the rest of the
* trace for the event should be ignored.
* This method should only be called if the event was previously checked by
* the consistency verifier using {@link #onInputEvent} and other methods.
* @param event The event.
* @param nestingLevel The nesting level: 0 if called from the base class,
* or 1 from a subclass. If the event was already checked by this consistency verifier
* at a higher nesting level, it will not be checked again. Used to handle the situation
* where a subclass dispatching method delegates to its superclass's dispatching method
* and both dispatching methods call into the consistency verifier.
*/
void onUnhandledEvent(InputEvent& event, int nestingLevel);
};
class InputEventConsistencyVerifier::KeyState {
private:
static KeyState* mRecycledList;
private:
friend InputEventConsistencyVerifier;
KeyState();
public:
KeyState* next;
int deviceId;
int source;
int keyCode;
bool unhandled;
static KeyState* obtain(int deviceId, int source, int keyCode);
void recycle();
};
}/*endof namespace*/
#endif/*__INPUTEVENT_CONSISTENCYVERIFIER_H__*/

View File

@ -316,7 +316,7 @@ void VelocityTrackerImpl::addMovement(const MotionEvent& event) {
size_t historySize = event.getHistorySize();
memset(positions,0,sizeof(positions));
for (size_t h = 0; h < historySize; h++) {
eventTime = event.getHistoricalEventTimeNano(h);
eventTime = event.getHistoricalEventTimeNanos(h);
for (size_t i = 0; i < pointerCount; i++) {
uint32_t index = pointerIndex[i];
positions[index].x = event.getHistoricalRawX(i, h);
@ -325,7 +325,7 @@ void VelocityTrackerImpl::addMovement(const MotionEvent& event) {
addMovement(eventTime, idBits, positions);
}
eventTime = event.getEventTimeNano();
eventTime = event.getEventTimeNanos();
for (size_t i = 0; i < pointerCount; i++) {
uint32_t index = pointerIndex[i];
positions[index].x = event.getRawX(i);

View File

@ -318,6 +318,9 @@ void View::initView(){
mPendingCheckForTap = nullptr;
mUnsetPressedState = nullptr;;
mPendingCheckForLongPress = nullptr;
mInputEventConsistencyVerifier = nullptr;
if(InputEventConsistencyVerifier::isInstrumentationEnabled())
mInputEventConsistencyVerifier = new InputEventConsistencyVerifier(nullptr,0);
mRenderNode = new RenderNode();
mScrollX = mScrollY = 0;
@ -396,6 +399,7 @@ View::~View(){
delete mTooltipInfo;
delete mScrollIndicatorDrawable;
delete mDefaultFocusHighlight;
delete mInputEventConsistencyVerifier;
delete mBackground;
delete mBackgroundTint;
@ -5713,12 +5717,16 @@ KeyEvent::DispatcherState* View::getKeyDispatcherState()const{
}
bool View::dispatchKeyEvent(KeyEvent&event){
if(mInputEventConsistencyVerifier)
mInputEventConsistencyVerifier->onKeyEvent(event,0);
if (mListenerInfo && mListenerInfo->mOnKeyListener && (mViewFlags & ENABLED_MASK) == ENABLED
&& mListenerInfo->mOnKeyListener(*this, event.getKeyCode(), event)) {
return true;
}
const bool result = event.dispatch(this,(mAttachInfo? &mAttachInfo->mKeyDispatchState : nullptr),this);
LOGV("%s.%s=%d",event.getLabel(event.getKeyCode()),KeyEvent::actionToString(event.getAction()).c_str(),result);
if(mInputEventConsistencyVerifier && (result==false))
mInputEventConsistencyVerifier->onUnhandledEvent(event, 0);
return result;
}
@ -5899,6 +5907,8 @@ bool View::dispatchGenericMotionEventInternal(MotionEvent& event){
}
break;
}
if(mInputEventConsistencyVerifier)
mInputEventConsistencyVerifier->onUnhandledEvent(event, 0);
return false;
}
@ -5936,7 +5946,9 @@ bool View::pointInHoveredChild(MotionEvent& event) {
}
bool View::dispatchGenericMotionEvent(MotionEvent&event){
int source = event.getSource();
const int source = event.getSource();
if (mInputEventConsistencyVerifier)
mInputEventConsistencyVerifier->onGenericMotionEvent(event, 0);
if ((source & InputDevice::SOURCE_CLASS_POINTER) != 0) {
int action = event.getAction();
if (action == MotionEvent::ACTION_HOVER_ENTER
@ -5954,12 +5966,17 @@ bool View::dispatchGenericMotionEvent(MotionEvent&event){
if (dispatchGenericMotionEventInternal(event)) {
return true;
}
if (mInputEventConsistencyVerifier)
mInputEventConsistencyVerifier->onUnhandledEvent(event, 0);
return false;
}
bool View::dispatchTouchEvent(MotionEvent&event){
bool result = false;
const int actionMasked = event.getActionMasked();
if (mInputEventConsistencyVerifier)
mInputEventConsistencyVerifier->onTouchEvent(event, 0);
if (actionMasked == MotionEvent::ACTION_UP ||
actionMasked == MotionEvent::ACTION_CANCEL ||
(actionMasked == MotionEvent::ACTION_DOWN && !result)) {
@ -5979,7 +5996,8 @@ bool View::dispatchTouchEvent(MotionEvent&event){
if(!result&& onTouchEvent(event)){
result=true;
}
if (!result && mInputEventConsistencyVerifier)
mInputEventConsistencyVerifier->onUnhandledEvent(event, 0);
if (actionMasked == MotionEvent::ACTION_UP ||
actionMasked == MotionEvent::ACTION_CANCEL ||
(actionMasked == MotionEvent::ACTION_DOWN && !result)) {

View File

@ -25,6 +25,7 @@
#include <view/soundeffectconstants.h>
#include <view/hapticfeedbackconstants.h>
#include <view/accessibility/accessibilityevent.h>
#include <view/inputeventconsistencyverifier.h>
#include <animation/animation.h>
#include <animation/statelistanimator.h>
#include <set>
@ -357,6 +358,7 @@ private:
bool mBoundsChangedmDefaultFocusHighlightSizeChanged;
ViewOverlay* mOverlay;
InputEventConsistencyVerifier* mInputEventConsistencyVerifier;
ViewTreeObserver* mFloatingTreeObserver;
StateListAnimator* mStateListAnimator;
ViewPropertyAnimator* mAnimator;

View File

@ -2843,6 +2843,8 @@ void ViewGroup::onDescendantInvalidated(View* child,View* target){
}
bool ViewGroup::dispatchKeyEvent(KeyEvent&event){
if (mInputEventConsistencyVerifier)
mInputEventConsistencyVerifier->onKeyEvent(event, 1);
if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
== (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
if (View::dispatchKeyEvent(event)) {
@ -2854,6 +2856,8 @@ bool ViewGroup::dispatchKeyEvent(KeyEvent&event){
return true;
}
}
if (mInputEventConsistencyVerifier)
mInputEventConsistencyVerifier->onUnhandledEvent(event, 1);
return View::dispatchKeyEvent(event);
}
@ -2876,6 +2880,9 @@ bool ViewGroup::dispatchTouchEvent(MotionEvent&ev){
const float scrolledXFloat = xf + mScrollX;
const float scrolledYFloat = yf + mScrollY;
if (mInputEventConsistencyVerifier)
mInputEventConsistencyVerifier->onTouchEvent(ev, 1);
if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
ev.setTargetAccessibilityFocus(false);
}
@ -3010,7 +3017,8 @@ bool ViewGroup::dispatchTouchEvent(MotionEvent&ev){
int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
removePointersFromTouchTargets(idBitsToRemove);
}
if (!handled && mInputEventConsistencyVerifier)
mInputEventConsistencyVerifier->onUnhandledEvent(ev, 1);
return handled;
}