Loading core/java/android/view/GestureDetector.java +6 −0 Original line number Original line Diff line number Diff line Loading @@ -590,8 +590,14 @@ public class GestureDetector { mHandler.removeMessages(SHOW_PRESS); mHandler.removeMessages(SHOW_PRESS); mHandler.removeMessages(LONG_PRESS); mHandler.removeMessages(LONG_PRESS); break; break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_CANCEL: cancel(); cancel(); break; } if (!handled && mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(ev, 0); } } return handled; return handled; } } Loading core/java/android/view/InputEventConsistencyVerifier.java +83 −6 Original line number Original line Diff line number Diff line Loading @@ -54,6 +54,7 @@ public final class InputEventConsistencyVerifier { // Copy of the most recent events. // Copy of the most recent events. private InputEvent[] mRecentEvents; private InputEvent[] mRecentEvents; private boolean[] mRecentEventsUnhandled; private int mMostRecentEventIndex; private int mMostRecentEventIndex; // Current event and its type. // Current event and its type. Loading @@ -65,6 +66,7 @@ public final class InputEventConsistencyVerifier { // Current state of the trackball. // Current state of the trackball. private boolean mTrackballDown; private boolean mTrackballDown; private boolean mTrackballUnhandled; // Bitfield of pointer ids that are currently down. // Bitfield of pointer ids that are currently down. // Assumes that the largest possible pointer id is 31, which is potentially subject to change. // Assumes that the largest possible pointer id is 31, which is potentially subject to change. Loading @@ -79,6 +81,9 @@ public final class InputEventConsistencyVerifier { // Reset on down or cancel. // Reset on down or cancel. private boolean mTouchEventStreamIsTainted; private boolean mTouchEventStreamIsTainted; // Set to true if the touch event stream is partially unhandled. private boolean mTouchEventStreamUnhandled; // Set to true if we received hover enter. // Set to true if we received hover enter. private boolean mHoverEntered; private boolean mHoverEntered; Loading Loading @@ -117,9 +122,17 @@ public final class InputEventConsistencyVerifier { mLastEvent = null; mLastEvent = null; mLastNestingLevel = 0; mLastNestingLevel = 0; mTrackballDown = false; mTrackballDown = false; mTrackballUnhandled = false; mTouchEventStreamPointers = 0; mTouchEventStreamPointers = 0; mTouchEventStreamIsTainted = false; mTouchEventStreamIsTainted = false; mTouchEventStreamUnhandled = false; mHoverEntered = false; mHoverEntered = false; while (mKeyStateList != null) { final KeyState state = mKeyStateList; mKeyStateList = state.next; state.recycle(); } } } /** /** Loading Loading @@ -176,7 +189,9 @@ public final class InputEventConsistencyVerifier { // We don't perform this check when processing raw device input // We don't perform this check when processing raw device input // because the input dispatcher itself is responsible for setting // because the input dispatcher itself is responsible for setting // the key repeat count before it delivers input events. // the key repeat count before it delivers input events. if ((mFlags & FLAG_RAW_DEVICE_INPUT) == 0 if (state.unhandled) { state.unhandled = false; } else if ((mFlags & FLAG_RAW_DEVICE_INPUT) == 0 && event.getRepeatCount() == 0) { && event.getRepeatCount() == 0) { problem("ACTION_DOWN but key is already down and this event " problem("ACTION_DOWN but key is already down and this event " + "is not a key repeat."); + "is not a key repeat."); Loading Loading @@ -229,10 +244,11 @@ public final class InputEventConsistencyVerifier { if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { switch (action) { switch (action) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_DOWN: if (mTrackballDown) { if (mTrackballDown && !mTrackballUnhandled) { problem("ACTION_DOWN but trackball is already down."); problem("ACTION_DOWN but trackball is already down."); } else { } else { mTrackballDown = true; mTrackballDown = true; mTrackballUnhandled = false; } } ensureHistorySizeIsZeroForThisAction(event); ensureHistorySizeIsZeroForThisAction(event); ensurePointerCountIsOneForThisAction(event); ensurePointerCountIsOneForThisAction(event); Loading @@ -242,6 +258,7 @@ public final class InputEventConsistencyVerifier { problem("ACTION_UP but trackball is not down."); problem("ACTION_UP but trackball is not down."); } else { } else { mTrackballDown = false; mTrackballDown = false; mTrackballUnhandled = false; } } ensureHistorySizeIsZeroForThisAction(event); ensureHistorySizeIsZeroForThisAction(event); ensurePointerCountIsOneForThisAction(event); ensurePointerCountIsOneForThisAction(event); Loading Loading @@ -285,11 +302,13 @@ public final class InputEventConsistencyVerifier { final int action = event.getAction(); final int action = event.getAction(); final boolean newStream = action == MotionEvent.ACTION_DOWN final boolean newStream = action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_CANCEL; || action == MotionEvent.ACTION_CANCEL; if (mTouchEventStreamIsTainted) { if (mTouchEventStreamIsTainted || mTouchEventStreamUnhandled) { if (newStream) { if (newStream) { mTouchEventStreamIsTainted = false; mTouchEventStreamIsTainted = false; mTouchEventStreamUnhandled = false; mTouchEventStreamPointers = 0; } else { } else { finishEvent(true); finishEvent(mTouchEventStreamIsTainted); return; return; } } } } Loading Loading @@ -467,6 +486,48 @@ public final class InputEventConsistencyVerifier { } } } } /** * 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. */ public void onUnhandledEvent(InputEvent event, int nestingLevel) { if (nestingLevel != mLastNestingLevel) { return; } if (mRecentEventsUnhandled != null) { mRecentEventsUnhandled[mMostRecentEventIndex] = true; } if (event instanceof KeyEvent) { final KeyEvent keyEvent = (KeyEvent)event; final int deviceId = keyEvent.getDeviceId(); final int source = keyEvent.getSource(); final int keyCode = keyEvent.getKeyCode(); final KeyState state = findKeyState(deviceId, source, keyCode, /*remove*/ false); if (state != null) { state.unhandled = true; } } else { final MotionEvent motionEvent = (MotionEvent)event; if (motionEvent.isTouchEvent()) { mTouchEventStreamUnhandled = true; } else if ((motionEvent.getSource() & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { if (mTrackballDown) { mTrackballUnhandled = true; } } } } private void ensureMetaStateIsNormalized(int metaState) { private void ensureMetaStateIsNormalized(int metaState) { final int normalizedMetaState = KeyEvent.normalizeMetaState(metaState); final int normalizedMetaState = KeyEvent.normalizeMetaState(metaState); if (normalizedMetaState != metaState) { if (normalizedMetaState != metaState) { Loading Loading @@ -518,7 +579,8 @@ public final class InputEventConsistencyVerifier { private void finishEvent(boolean tainted) { private void finishEvent(boolean tainted) { if (mViolationMessage != null && mViolationMessage.length() != 0) { if (mViolationMessage != null && mViolationMessage.length() != 0) { mViolationMessage.append("\n in ").append(mCaller); mViolationMessage.append("\n in ").append(mCaller); mViolationMessage.append("\n ").append(mCurrentEvent); mViolationMessage.append("\n "); appendEvent(mViolationMessage, 0, mCurrentEvent, false); if (RECENT_EVENTS_TO_LOG != 0 && mRecentEvents != null) { if (RECENT_EVENTS_TO_LOG != 0 && mRecentEvents != null) { mViolationMessage.append("\n -- recent events --"); mViolationMessage.append("\n -- recent events --"); Loading @@ -529,7 +591,8 @@ public final class InputEventConsistencyVerifier { if (event == null) { if (event == null) { break; break; } } mViolationMessage.append("\n ").append(i + 1).append(": ").append(event); mViolationMessage.append("\n "); appendEvent(mViolationMessage, i + 1, event, mRecentEventsUnhandled[index]); } } } } Loading @@ -547,6 +610,7 @@ public final class InputEventConsistencyVerifier { if (RECENT_EVENTS_TO_LOG != 0) { if (RECENT_EVENTS_TO_LOG != 0) { if (mRecentEvents == null) { if (mRecentEvents == null) { mRecentEvents = new InputEvent[RECENT_EVENTS_TO_LOG]; mRecentEvents = new InputEvent[RECENT_EVENTS_TO_LOG]; mRecentEventsUnhandled = new boolean[RECENT_EVENTS_TO_LOG]; } } final int index = (mMostRecentEventIndex + 1) % RECENT_EVENTS_TO_LOG; final int index = (mMostRecentEventIndex + 1) % RECENT_EVENTS_TO_LOG; mMostRecentEventIndex = index; mMostRecentEventIndex = index; Loading @@ -554,12 +618,23 @@ public final class InputEventConsistencyVerifier { mRecentEvents[index].recycle(); mRecentEvents[index].recycle(); } } mRecentEvents[index] = mCurrentEvent.copy(); mRecentEvents[index] = mCurrentEvent.copy(); mRecentEventsUnhandled[index] = false; } } mCurrentEvent = null; mCurrentEvent = null; mCurrentEventType = null; mCurrentEventType = null; } } private static void appendEvent(StringBuilder message, int index, InputEvent event, boolean unhandled) { message.append(index).append(": sent at ").append(event.getEventTimeNano()); message.append(", "); if (unhandled) { message.append("(unhandled) "); } message.append(event); } private void problem(String message) { private void problem(String message) { if (mViolationMessage == null) { if (mViolationMessage == null) { mViolationMessage = new StringBuilder(); mViolationMessage = new StringBuilder(); Loading Loading @@ -608,6 +683,7 @@ public final class InputEventConsistencyVerifier { public int deviceId; public int deviceId; public int source; public int source; public int keyCode; public int keyCode; public boolean unhandled; private KeyState() { private KeyState() { } } Loading @@ -625,6 +701,7 @@ public final class InputEventConsistencyVerifier { state.deviceId = deviceId; state.deviceId = deviceId; state.source = source; state.source = source; state.keyCode = keyCode; state.keyCode = keyCode; state.unhandled = false; return state; return state; } } Loading core/java/android/view/ScaleGestureDetector.java +8 −4 Original line number Original line Diff line number Diff line Loading @@ -183,15 +183,15 @@ public class ScaleGestureDetector { } } final int action = event.getActionMasked(); final int action = event.getActionMasked(); boolean handled = true; if (action == MotionEvent.ACTION_DOWN) { if (action == MotionEvent.ACTION_DOWN) { reset(); // Start fresh reset(); // Start fresh } } if (mInvalidGesture) return false; boolean handled = true; if (mInvalidGesture) { if (!mGestureInProgress) { handled = false; } else if (!mGestureInProgress) { switch (action) { switch (action) { case MotionEvent.ACTION_DOWN: { case MotionEvent.ACTION_DOWN: { mActiveId0 = event.getPointerId(0); mActiveId0 = event.getPointerId(0); Loading Loading @@ -467,6 +467,10 @@ public class ScaleGestureDetector { break; break; } } } } if (!handled && mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } return handled; return handled; } } Loading core/java/android/view/View.java +39 −11 Original line number Original line Diff line number Diff line Loading @@ -4614,8 +4614,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility return true; return true; } } return event.dispatch(this, mAttachInfo != null if (event.dispatch(this, mAttachInfo != null ? mAttachInfo.mKeyDispatchState : null, this); ? mAttachInfo.mKeyDispatchState : null, this)) { return true; } if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } return false; } } /** /** Loading @@ -4640,16 +4647,22 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mInputEventConsistencyVerifier.onTouchEvent(event, 0); mInputEventConsistencyVerifier.onTouchEvent(event, 0); } } if (!onFilterTouchEventForSecurity(event)) { if (onFilterTouchEventForSecurity(event)) { return false; } //noinspection SimplifiableIfStatement //noinspection SimplifiableIfStatement if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && mOnTouchListener.onTouch(this, event)) { mOnTouchListener.onTouch(this, event)) { return true; return true; } } return onTouchEvent(event); if (onTouchEvent(event)) { return true; } } if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } return false; } } /** /** Loading Loading @@ -4682,7 +4695,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } } //Log.i("view", "view=" + this + ", " + event.toString()); //Log.i("view", "view=" + this + ", " + event.toString()); return onTrackballEvent(event); if (onTrackballEvent(event)) { return true; } if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } return false; } } /** /** Loading Loading @@ -4723,7 +4743,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility && mOnGenericMotionListener.onGenericMotion(this, event)) { && mOnGenericMotionListener.onGenericMotion(this, event)) { return true; return true; } } return onGenericMotionEvent(event); if (onGenericMotionEvent(event)) { return true; } if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } return false; } } /** /** Loading core/java/android/view/ViewGroup.java +151 −132 Original line number Original line Diff line number Diff line Loading @@ -1131,9 +1131,17 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) { if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) { return super.dispatchKeyEvent(event); if (super.dispatchKeyEvent(event)) { return true; } } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) { } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) { return mFocused.dispatchKeyEvent(event); if (mFocused.dispatchKeyEvent(event)) { return true; } } if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 1); } } return false; return false; } } Loading Loading @@ -1161,9 +1169,17 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) { if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) { return super.dispatchTrackballEvent(event); if (super.dispatchTrackballEvent(event)) { return true; } } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) { } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) { return mFocused.dispatchTrackballEvent(event); if (mFocused.dispatchTrackballEvent(event)) { return true; } } if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 1); } } return false; return false; } } Loading Loading @@ -1344,10 +1360,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager mInputEventConsistencyVerifier.onTouchEvent(ev, 1); mInputEventConsistencyVerifier.onTouchEvent(ev, 1); } } if (!onFilterTouchEventForSecurity(ev)) { boolean handled = false; return false; if (onFilterTouchEventForSecurity(ev)) { } final int action = ev.getAction(); final int action = ev.getAction(); final int actionMasked = action & MotionEvent.ACTION_MASK; final int actionMasked = action & MotionEvent.ACTION_MASK; Loading @@ -1367,7 +1381,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (!disallowIntercept) { if (!disallowIntercept) { intercepted = onInterceptTouchEvent(ev); intercepted = onInterceptTouchEvent(ev); ev.setAction(action); // restore action in case onInterceptTouchEvent() changed it ev.setAction(action); // restore action in case it was changed } else { } else { intercepted = false; intercepted = false; } } Loading Loading @@ -1399,7 +1413,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final int childrenCount = mChildrenCount; final int childrenCount = mChildrenCount; if (childrenCount != 0) { if (childrenCount != 0) { // Find a child that can receive the event. Scan children from front to back. // Find a child that can receive the event. // Scan children from front to back. final View[] children = mChildren; final View[] children = mChildren; final float x = ev.getX(actionIndex); final float x = ev.getX(actionIndex); final float y = ev.getY(actionIndex); final float y = ev.getY(actionIndex); Loading Loading @@ -1446,7 +1461,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } // Dispatch to touch targets. // Dispatch to touch targets. boolean handled = false; if (mFirstTouchTarget == null) { if (mFirstTouchTarget == null) { // No touch targets so treat this as an ordinary view. // No touch targets so treat this as an ordinary view. handled = dispatchTransformedTouchEvent(ev, canceled, null, handled = dispatchTransformedTouchEvent(ev, canceled, null, Loading @@ -1461,7 +1475,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { handled = true; handled = true; } else { } else { final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted; final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted; if (dispatchTransformedTouchEvent(ev, cancelChild, if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) { target.child, target.pointerIdBits)) { handled = true; handled = true; Loading Loading @@ -1492,7 +1507,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final int idBitsToRemove = 1 << ev.getPointerId(actionIndex); final int idBitsToRemove = 1 << ev.getPointerId(actionIndex); removePointersFromTouchTargets(idBitsToRemove); removePointersFromTouchTargets(idBitsToRemove); } } } if (!handled && mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1); } return handled; return handled; } } Loading Loading
core/java/android/view/GestureDetector.java +6 −0 Original line number Original line Diff line number Diff line Loading @@ -590,8 +590,14 @@ public class GestureDetector { mHandler.removeMessages(SHOW_PRESS); mHandler.removeMessages(SHOW_PRESS); mHandler.removeMessages(LONG_PRESS); mHandler.removeMessages(LONG_PRESS); break; break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_CANCEL: cancel(); cancel(); break; } if (!handled && mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(ev, 0); } } return handled; return handled; } } Loading
core/java/android/view/InputEventConsistencyVerifier.java +83 −6 Original line number Original line Diff line number Diff line Loading @@ -54,6 +54,7 @@ public final class InputEventConsistencyVerifier { // Copy of the most recent events. // Copy of the most recent events. private InputEvent[] mRecentEvents; private InputEvent[] mRecentEvents; private boolean[] mRecentEventsUnhandled; private int mMostRecentEventIndex; private int mMostRecentEventIndex; // Current event and its type. // Current event and its type. Loading @@ -65,6 +66,7 @@ public final class InputEventConsistencyVerifier { // Current state of the trackball. // Current state of the trackball. private boolean mTrackballDown; private boolean mTrackballDown; private boolean mTrackballUnhandled; // Bitfield of pointer ids that are currently down. // Bitfield of pointer ids that are currently down. // Assumes that the largest possible pointer id is 31, which is potentially subject to change. // Assumes that the largest possible pointer id is 31, which is potentially subject to change. Loading @@ -79,6 +81,9 @@ public final class InputEventConsistencyVerifier { // Reset on down or cancel. // Reset on down or cancel. private boolean mTouchEventStreamIsTainted; private boolean mTouchEventStreamIsTainted; // Set to true if the touch event stream is partially unhandled. private boolean mTouchEventStreamUnhandled; // Set to true if we received hover enter. // Set to true if we received hover enter. private boolean mHoverEntered; private boolean mHoverEntered; Loading Loading @@ -117,9 +122,17 @@ public final class InputEventConsistencyVerifier { mLastEvent = null; mLastEvent = null; mLastNestingLevel = 0; mLastNestingLevel = 0; mTrackballDown = false; mTrackballDown = false; mTrackballUnhandled = false; mTouchEventStreamPointers = 0; mTouchEventStreamPointers = 0; mTouchEventStreamIsTainted = false; mTouchEventStreamIsTainted = false; mTouchEventStreamUnhandled = false; mHoverEntered = false; mHoverEntered = false; while (mKeyStateList != null) { final KeyState state = mKeyStateList; mKeyStateList = state.next; state.recycle(); } } } /** /** Loading Loading @@ -176,7 +189,9 @@ public final class InputEventConsistencyVerifier { // We don't perform this check when processing raw device input // We don't perform this check when processing raw device input // because the input dispatcher itself is responsible for setting // because the input dispatcher itself is responsible for setting // the key repeat count before it delivers input events. // the key repeat count before it delivers input events. if ((mFlags & FLAG_RAW_DEVICE_INPUT) == 0 if (state.unhandled) { state.unhandled = false; } else if ((mFlags & FLAG_RAW_DEVICE_INPUT) == 0 && event.getRepeatCount() == 0) { && event.getRepeatCount() == 0) { problem("ACTION_DOWN but key is already down and this event " problem("ACTION_DOWN but key is already down and this event " + "is not a key repeat."); + "is not a key repeat."); Loading Loading @@ -229,10 +244,11 @@ public final class InputEventConsistencyVerifier { if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { switch (action) { switch (action) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_DOWN: if (mTrackballDown) { if (mTrackballDown && !mTrackballUnhandled) { problem("ACTION_DOWN but trackball is already down."); problem("ACTION_DOWN but trackball is already down."); } else { } else { mTrackballDown = true; mTrackballDown = true; mTrackballUnhandled = false; } } ensureHistorySizeIsZeroForThisAction(event); ensureHistorySizeIsZeroForThisAction(event); ensurePointerCountIsOneForThisAction(event); ensurePointerCountIsOneForThisAction(event); Loading @@ -242,6 +258,7 @@ public final class InputEventConsistencyVerifier { problem("ACTION_UP but trackball is not down."); problem("ACTION_UP but trackball is not down."); } else { } else { mTrackballDown = false; mTrackballDown = false; mTrackballUnhandled = false; } } ensureHistorySizeIsZeroForThisAction(event); ensureHistorySizeIsZeroForThisAction(event); ensurePointerCountIsOneForThisAction(event); ensurePointerCountIsOneForThisAction(event); Loading Loading @@ -285,11 +302,13 @@ public final class InputEventConsistencyVerifier { final int action = event.getAction(); final int action = event.getAction(); final boolean newStream = action == MotionEvent.ACTION_DOWN final boolean newStream = action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_CANCEL; || action == MotionEvent.ACTION_CANCEL; if (mTouchEventStreamIsTainted) { if (mTouchEventStreamIsTainted || mTouchEventStreamUnhandled) { if (newStream) { if (newStream) { mTouchEventStreamIsTainted = false; mTouchEventStreamIsTainted = false; mTouchEventStreamUnhandled = false; mTouchEventStreamPointers = 0; } else { } else { finishEvent(true); finishEvent(mTouchEventStreamIsTainted); return; return; } } } } Loading Loading @@ -467,6 +486,48 @@ public final class InputEventConsistencyVerifier { } } } } /** * 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. */ public void onUnhandledEvent(InputEvent event, int nestingLevel) { if (nestingLevel != mLastNestingLevel) { return; } if (mRecentEventsUnhandled != null) { mRecentEventsUnhandled[mMostRecentEventIndex] = true; } if (event instanceof KeyEvent) { final KeyEvent keyEvent = (KeyEvent)event; final int deviceId = keyEvent.getDeviceId(); final int source = keyEvent.getSource(); final int keyCode = keyEvent.getKeyCode(); final KeyState state = findKeyState(deviceId, source, keyCode, /*remove*/ false); if (state != null) { state.unhandled = true; } } else { final MotionEvent motionEvent = (MotionEvent)event; if (motionEvent.isTouchEvent()) { mTouchEventStreamUnhandled = true; } else if ((motionEvent.getSource() & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { if (mTrackballDown) { mTrackballUnhandled = true; } } } } private void ensureMetaStateIsNormalized(int metaState) { private void ensureMetaStateIsNormalized(int metaState) { final int normalizedMetaState = KeyEvent.normalizeMetaState(metaState); final int normalizedMetaState = KeyEvent.normalizeMetaState(metaState); if (normalizedMetaState != metaState) { if (normalizedMetaState != metaState) { Loading Loading @@ -518,7 +579,8 @@ public final class InputEventConsistencyVerifier { private void finishEvent(boolean tainted) { private void finishEvent(boolean tainted) { if (mViolationMessage != null && mViolationMessage.length() != 0) { if (mViolationMessage != null && mViolationMessage.length() != 0) { mViolationMessage.append("\n in ").append(mCaller); mViolationMessage.append("\n in ").append(mCaller); mViolationMessage.append("\n ").append(mCurrentEvent); mViolationMessage.append("\n "); appendEvent(mViolationMessage, 0, mCurrentEvent, false); if (RECENT_EVENTS_TO_LOG != 0 && mRecentEvents != null) { if (RECENT_EVENTS_TO_LOG != 0 && mRecentEvents != null) { mViolationMessage.append("\n -- recent events --"); mViolationMessage.append("\n -- recent events --"); Loading @@ -529,7 +591,8 @@ public final class InputEventConsistencyVerifier { if (event == null) { if (event == null) { break; break; } } mViolationMessage.append("\n ").append(i + 1).append(": ").append(event); mViolationMessage.append("\n "); appendEvent(mViolationMessage, i + 1, event, mRecentEventsUnhandled[index]); } } } } Loading @@ -547,6 +610,7 @@ public final class InputEventConsistencyVerifier { if (RECENT_EVENTS_TO_LOG != 0) { if (RECENT_EVENTS_TO_LOG != 0) { if (mRecentEvents == null) { if (mRecentEvents == null) { mRecentEvents = new InputEvent[RECENT_EVENTS_TO_LOG]; mRecentEvents = new InputEvent[RECENT_EVENTS_TO_LOG]; mRecentEventsUnhandled = new boolean[RECENT_EVENTS_TO_LOG]; } } final int index = (mMostRecentEventIndex + 1) % RECENT_EVENTS_TO_LOG; final int index = (mMostRecentEventIndex + 1) % RECENT_EVENTS_TO_LOG; mMostRecentEventIndex = index; mMostRecentEventIndex = index; Loading @@ -554,12 +618,23 @@ public final class InputEventConsistencyVerifier { mRecentEvents[index].recycle(); mRecentEvents[index].recycle(); } } mRecentEvents[index] = mCurrentEvent.copy(); mRecentEvents[index] = mCurrentEvent.copy(); mRecentEventsUnhandled[index] = false; } } mCurrentEvent = null; mCurrentEvent = null; mCurrentEventType = null; mCurrentEventType = null; } } private static void appendEvent(StringBuilder message, int index, InputEvent event, boolean unhandled) { message.append(index).append(": sent at ").append(event.getEventTimeNano()); message.append(", "); if (unhandled) { message.append("(unhandled) "); } message.append(event); } private void problem(String message) { private void problem(String message) { if (mViolationMessage == null) { if (mViolationMessage == null) { mViolationMessage = new StringBuilder(); mViolationMessage = new StringBuilder(); Loading Loading @@ -608,6 +683,7 @@ public final class InputEventConsistencyVerifier { public int deviceId; public int deviceId; public int source; public int source; public int keyCode; public int keyCode; public boolean unhandled; private KeyState() { private KeyState() { } } Loading @@ -625,6 +701,7 @@ public final class InputEventConsistencyVerifier { state.deviceId = deviceId; state.deviceId = deviceId; state.source = source; state.source = source; state.keyCode = keyCode; state.keyCode = keyCode; state.unhandled = false; return state; return state; } } Loading
core/java/android/view/ScaleGestureDetector.java +8 −4 Original line number Original line Diff line number Diff line Loading @@ -183,15 +183,15 @@ public class ScaleGestureDetector { } } final int action = event.getActionMasked(); final int action = event.getActionMasked(); boolean handled = true; if (action == MotionEvent.ACTION_DOWN) { if (action == MotionEvent.ACTION_DOWN) { reset(); // Start fresh reset(); // Start fresh } } if (mInvalidGesture) return false; boolean handled = true; if (mInvalidGesture) { if (!mGestureInProgress) { handled = false; } else if (!mGestureInProgress) { switch (action) { switch (action) { case MotionEvent.ACTION_DOWN: { case MotionEvent.ACTION_DOWN: { mActiveId0 = event.getPointerId(0); mActiveId0 = event.getPointerId(0); Loading Loading @@ -467,6 +467,10 @@ public class ScaleGestureDetector { break; break; } } } } if (!handled && mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } return handled; return handled; } } Loading
core/java/android/view/View.java +39 −11 Original line number Original line Diff line number Diff line Loading @@ -4614,8 +4614,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility return true; return true; } } return event.dispatch(this, mAttachInfo != null if (event.dispatch(this, mAttachInfo != null ? mAttachInfo.mKeyDispatchState : null, this); ? mAttachInfo.mKeyDispatchState : null, this)) { return true; } if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } return false; } } /** /** Loading @@ -4640,16 +4647,22 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mInputEventConsistencyVerifier.onTouchEvent(event, 0); mInputEventConsistencyVerifier.onTouchEvent(event, 0); } } if (!onFilterTouchEventForSecurity(event)) { if (onFilterTouchEventForSecurity(event)) { return false; } //noinspection SimplifiableIfStatement //noinspection SimplifiableIfStatement if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && mOnTouchListener.onTouch(this, event)) { mOnTouchListener.onTouch(this, event)) { return true; return true; } } return onTouchEvent(event); if (onTouchEvent(event)) { return true; } } if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } return false; } } /** /** Loading Loading @@ -4682,7 +4695,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } } //Log.i("view", "view=" + this + ", " + event.toString()); //Log.i("view", "view=" + this + ", " + event.toString()); return onTrackballEvent(event); if (onTrackballEvent(event)) { return true; } if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } return false; } } /** /** Loading Loading @@ -4723,7 +4743,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility && mOnGenericMotionListener.onGenericMotion(this, event)) { && mOnGenericMotionListener.onGenericMotion(this, event)) { return true; return true; } } return onGenericMotionEvent(event); if (onGenericMotionEvent(event)) { return true; } if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } return false; } } /** /** Loading
core/java/android/view/ViewGroup.java +151 −132 Original line number Original line Diff line number Diff line Loading @@ -1131,9 +1131,17 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) { if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) { return super.dispatchKeyEvent(event); if (super.dispatchKeyEvent(event)) { return true; } } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) { } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) { return mFocused.dispatchKeyEvent(event); if (mFocused.dispatchKeyEvent(event)) { return true; } } if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 1); } } return false; return false; } } Loading Loading @@ -1161,9 +1169,17 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) { if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) { return super.dispatchTrackballEvent(event); if (super.dispatchTrackballEvent(event)) { return true; } } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) { } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) { return mFocused.dispatchTrackballEvent(event); if (mFocused.dispatchTrackballEvent(event)) { return true; } } if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 1); } } return false; return false; } } Loading Loading @@ -1344,10 +1360,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager mInputEventConsistencyVerifier.onTouchEvent(ev, 1); mInputEventConsistencyVerifier.onTouchEvent(ev, 1); } } if (!onFilterTouchEventForSecurity(ev)) { boolean handled = false; return false; if (onFilterTouchEventForSecurity(ev)) { } final int action = ev.getAction(); final int action = ev.getAction(); final int actionMasked = action & MotionEvent.ACTION_MASK; final int actionMasked = action & MotionEvent.ACTION_MASK; Loading @@ -1367,7 +1381,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (!disallowIntercept) { if (!disallowIntercept) { intercepted = onInterceptTouchEvent(ev); intercepted = onInterceptTouchEvent(ev); ev.setAction(action); // restore action in case onInterceptTouchEvent() changed it ev.setAction(action); // restore action in case it was changed } else { } else { intercepted = false; intercepted = false; } } Loading Loading @@ -1399,7 +1413,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final int childrenCount = mChildrenCount; final int childrenCount = mChildrenCount; if (childrenCount != 0) { if (childrenCount != 0) { // Find a child that can receive the event. Scan children from front to back. // Find a child that can receive the event. // Scan children from front to back. final View[] children = mChildren; final View[] children = mChildren; final float x = ev.getX(actionIndex); final float x = ev.getX(actionIndex); final float y = ev.getY(actionIndex); final float y = ev.getY(actionIndex); Loading Loading @@ -1446,7 +1461,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } // Dispatch to touch targets. // Dispatch to touch targets. boolean handled = false; if (mFirstTouchTarget == null) { if (mFirstTouchTarget == null) { // No touch targets so treat this as an ordinary view. // No touch targets so treat this as an ordinary view. handled = dispatchTransformedTouchEvent(ev, canceled, null, handled = dispatchTransformedTouchEvent(ev, canceled, null, Loading @@ -1461,7 +1475,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { handled = true; handled = true; } else { } else { final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted; final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted; if (dispatchTransformedTouchEvent(ev, cancelChild, if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) { target.child, target.pointerIdBits)) { handled = true; handled = true; Loading Loading @@ -1492,7 +1507,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final int idBitsToRemove = 1 << ev.getPointerId(actionIndex); final int idBitsToRemove = 1 << ev.getPointerId(actionIndex); removePointersFromTouchTargets(idBitsToRemove); removePointersFromTouchTargets(idBitsToRemove); } } } if (!handled && mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1); } return handled; return handled; } } Loading