Merge "Add new MotionEvent actions for button press and release." into mnc-dev
diff --git a/api/current.txt b/api/current.txt
index e81652d..c083398 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -35364,6 +35364,7 @@
     method public static java.lang.String axisToString(int);
     method public final int findPointerIndex(int);
     method public final int getAction();
+    method public final int getActionButton();
     method public final int getActionIndex();
     method public final int getActionMasked();
     method public final float getAxisValue(int);
@@ -35427,7 +35428,6 @@
     method public final float getY(int);
     method public final float getYPrecision();
     method public final boolean isButtonPressed(int);
-    method public final boolean isStylusButtonPressed();
     method public static android.view.MotionEvent obtain(long, long, int, int, android.view.MotionEvent.PointerProperties[], android.view.MotionEvent.PointerCoords[], int, int, float, float, int, int, int, int);
     method public static deprecated android.view.MotionEvent obtain(long, long, int, int, int[], android.view.MotionEvent.PointerCoords[], int, float, float, int, int, int, int);
     method public static android.view.MotionEvent obtain(long, long, int, float, float, float, float, int, float, float, int, int);
@@ -35443,6 +35443,8 @@
     method public final void setSource(int);
     method public final void transform(android.graphics.Matrix);
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final int ACTION_BUTTON_PRESS = 11; // 0xb
+    field public static final int ACTION_BUTTON_RELEASE = 12; // 0xc
     field public static final int ACTION_CANCEL = 3; // 0x3
     field public static final int ACTION_DOWN = 0; // 0x0
     field public static final int ACTION_HOVER_ENTER = 9; // 0x9
@@ -35511,6 +35513,8 @@
     field public static final int BUTTON_FORWARD = 16; // 0x10
     field public static final int BUTTON_PRIMARY = 1; // 0x1
     field public static final int BUTTON_SECONDARY = 2; // 0x2
+    field public static final int BUTTON_STYLUS_PRIMARY = 32; // 0x20
+    field public static final int BUTTON_STYLUS_SECONDARY = 64; // 0x40
     field public static final int BUTTON_TERTIARY = 4; // 0x4
     field public static final android.os.Parcelable.Creator<android.view.MotionEvent> CREATOR;
     field public static final int EDGE_BOTTOM = 2; // 0x2
diff --git a/api/system-current.txt b/api/system-current.txt
index 6cbd7d0..d7c95da 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -37625,6 +37625,7 @@
     method public static java.lang.String axisToString(int);
     method public final int findPointerIndex(int);
     method public final int getAction();
+    method public final int getActionButton();
     method public final int getActionIndex();
     method public final int getActionMasked();
     method public final float getAxisValue(int);
@@ -37688,7 +37689,6 @@
     method public final float getY(int);
     method public final float getYPrecision();
     method public final boolean isButtonPressed(int);
-    method public final boolean isStylusButtonPressed();
     method public static android.view.MotionEvent obtain(long, long, int, int, android.view.MotionEvent.PointerProperties[], android.view.MotionEvent.PointerCoords[], int, int, float, float, int, int, int, int);
     method public static deprecated android.view.MotionEvent obtain(long, long, int, int, int[], android.view.MotionEvent.PointerCoords[], int, float, float, int, int, int, int);
     method public static android.view.MotionEvent obtain(long, long, int, float, float, float, float, int, float, float, int, int);
@@ -37704,6 +37704,8 @@
     method public final void setSource(int);
     method public final void transform(android.graphics.Matrix);
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final int ACTION_BUTTON_PRESS = 11; // 0xb
+    field public static final int ACTION_BUTTON_RELEASE = 12; // 0xc
     field public static final int ACTION_CANCEL = 3; // 0x3
     field public static final int ACTION_DOWN = 0; // 0x0
     field public static final int ACTION_HOVER_ENTER = 9; // 0x9
@@ -37772,6 +37774,8 @@
     field public static final int BUTTON_FORWARD = 16; // 0x10
     field public static final int BUTTON_PRIMARY = 1; // 0x1
     field public static final int BUTTON_SECONDARY = 2; // 0x2
+    field public static final int BUTTON_STYLUS_PRIMARY = 32; // 0x20
+    field public static final int BUTTON_STYLUS_SECONDARY = 64; // 0x40
     field public static final int BUTTON_TERTIARY = 4; // 0x4
     field public static final android.os.Parcelable.Creator<android.view.MotionEvent> CREATOR;
     field public static final int EDGE_BOTTOM = 2; // 0x2
diff --git a/core/java/android/view/InputEventConsistencyVerifier.java b/core/java/android/view/InputEventConsistencyVerifier.java
index c5e4c21..46ef379 100644
--- a/core/java/android/view/InputEventConsistencyVerifier.java
+++ b/core/java/android/view/InputEventConsistencyVerifier.java
@@ -97,6 +97,9 @@
     // Set to true if we received hover enter.
     private boolean mHoverEntered;
 
+    // The bitset of buttons which we've received ACTION_BUTTON_PRESS for.
+    private int mButtonsPressed;
+
     // The current violation message.
     private StringBuilder mViolationMessage;
 
@@ -148,6 +151,7 @@
         mTouchEventStreamIsTainted = false;
         mTouchEventStreamUnhandled = false;
         mHoverEntered = false;
+        mButtonsPressed = 0;
 
         while (mKeyStateList != null) {
             final KeyState state = mKeyStateList;
@@ -466,6 +470,8 @@
 
             final int action = event.getAction();
             final int source = event.getSource();
+            final int buttonState = event.getButtonState();
+            final int actionButton = event.getActionButton();
             if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
                 switch (action) {
                     case MotionEvent.ACTION_HOVER_ENTER:
@@ -486,6 +492,62 @@
                         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 " +
+                                    actionButton + ", but it has already been pressed and " +
+                                    "has yet to be released.");
+                        }
+
+                        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(String.format("Reported button state differs from " +
+                                    "expected 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 " +
+                                    actionButton + ", but it was either never pressed or has " +
+                                    "already been released.");
+                        }
+
+                        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(String.format("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;
@@ -563,6 +625,15 @@
         }
     }
 
+    private void ensureActionButtonIsNonZeroForThisAction(MotionEvent event) {
+        final int actionButton = event.getActionButton();
+        if (actionButton == 0) {
+            problem("No action button set. Action button should always be non-zero for " +
+                    MotionEvent.actionToString(event.getAction()));
+
+        }
+    }
+
     private void ensureHistorySizeIsZeroForThisAction(MotionEvent event) {
         final int historySize = event.getHistorySize();
         if (historySize != 0) {
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 5df596a..4394cd8 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -303,6 +303,32 @@
     public static final int ACTION_HOVER_EXIT       = 10;
 
     /**
+     * Constant for {@link #getActionMasked}: A button has been pressed.
+     *
+     * <p>
+     * Use {@link #getActionButton()} to get which button was pressed.
+     * </p><p>
+     * This action is not a touch event so it is delivered to
+     * {@link View#onGenericMotionEvent(MotionEvent)} rather than
+     * {@link View#onTouchEvent(MotionEvent)}.
+     * </p>
+     */
+    public static final int ACTION_BUTTON_PRESS   = 11;
+
+    /**
+     * Constant for {@link #getActionMasked}: A button has been released.
+     *
+     * <p>
+     * Use {@link #getActionButton()} to get which button was released.
+     * </p><p>
+     * This action is not a touch event so it is delivered to
+     * {@link View#onGenericMotionEvent(MotionEvent)} rather than
+     * {@link View#onTouchEvent(MotionEvent)}.
+     * </p>
+     */
+    public static final int ACTION_BUTTON_RELEASE  = 12;
+
+    /**
      * Bits in the action code that represent a pointer index, used with
      * {@link #ACTION_POINTER_DOWN} and {@link #ACTION_POINTER_UP}.  Shifting
      * down by {@link #ACTION_POINTER_INDEX_SHIFT} provides the actual pointer
@@ -1174,14 +1200,14 @@
     public static final int BUTTON_PRIMARY = 1 << 0;
 
     /**
-     * Button constant: Secondary button (right mouse button, stylus first button).
+     * Button constant: Secondary button (right mouse button).
      *
      * @see #getButtonState
      */
     public static final int BUTTON_SECONDARY = 1 << 1;
 
     /**
-     * Button constant: Tertiary button (middle mouse button, stylus second button).
+     * Button constant: Tertiary button (middle mouse button).
      *
      * @see #getButtonState
      */
@@ -1209,6 +1235,20 @@
      */
     public static final int BUTTON_FORWARD = 1 << 4;
 
+    /**
+     * Button constant: Primary stylus button pressed.
+     *
+     * @see #getButtonState
+     */
+    public static final int BUTTON_STYLUS_PRIMARY = 1 << 5;
+
+    /**
+     * Button constant: Secondary stylus button pressed.
+     *
+     * @see #getButtonState
+     */
+    public static final int BUTTON_STYLUS_SECONDARY = 1 << 6;
+
     // NOTE: If you add a new axis here you must also add it to:
     //  native/include/android/input.h
 
@@ -1220,8 +1260,8 @@
         "BUTTON_TERTIARY",
         "BUTTON_BACK",
         "BUTTON_FORWARD",
-        "0x00000020",
-        "0x00000040",
+        "BUTTON_STYLUS_PRIMARY",
+        "BUTTON_STYLUS_SECONDARY",
         "0x00000080",
         "0x00000100",
         "0x00000200",
@@ -1357,6 +1397,8 @@
     private static native void nativeSetEdgeFlags(long nativePtr, int action);
     private static native int nativeGetMetaState(long nativePtr);
     private static native int nativeGetButtonState(long nativePtr);
+    private static native void nativeSetButtonState(long nativePtr, int buttonState);
+    private static native int nativeGetActionButton(long nativePtr);
     private static native void nativeOffsetLocation(long nativePtr, float deltaX, float deltaY);
     private static native float nativeGetXOffset(long nativePtr);
     private static native float nativeGetYOffset(long nativePtr);
@@ -2212,12 +2254,36 @@
      * @see #BUTTON_TERTIARY
      * @see #BUTTON_FORWARD
      * @see #BUTTON_BACK
+     * @see #BUTTON_STYLUS_PRIMARY
+     * @see #BUTTON_STYLUS_SECONDARY
      */
     public final int getButtonState() {
         return nativeGetButtonState(mNativePtr);
     }
 
     /**
+     * Sets the bitfield indicating which buttons are pressed.
+     *
+     * @see #getButtonState()
+     * @hide
+     */
+    public final void setButtonState(int buttonState) {
+        nativeSetButtonState(mNativePtr, buttonState);
+    }
+
+    /**
+     * Gets which button has been modified during a press or release action.
+     *
+     * For actions other than {@link #ACTION_BUTTON_PRESS} and {@link #ACTION_BUTTON_RELEASE}
+     * the returned value is undefined.
+     *
+     * @see #getButtonState()
+     */
+    public final int getActionButton() {
+        return nativeGetActionButton(mNativePtr);
+    }
+
+    /**
      * Returns the original raw X coordinate of this event.  For touch
      * events on the screen, this is the original location of the event
      * on the screen, before it had been adjusted for the containing window
@@ -3013,6 +3079,7 @@
     public String toString() {
         StringBuilder msg = new StringBuilder();
         msg.append("MotionEvent { action=").append(actionToString(getAction()));
+        msg.append(", actionButton=").append(buttonStateToString(getActionButton()));
 
         final int pointerCount = getPointerCount();
         for (int i = 0; i < pointerCount; i++) {
@@ -3066,6 +3133,10 @@
                 return "ACTION_HOVER_ENTER";
             case ACTION_HOVER_EXIT:
                 return "ACTION_HOVER_EXIT";
+            case ACTION_BUTTON_PRESS:
+                return "ACTION_BUTTON_PRESS";
+            case ACTION_BUTTON_RELEASE:
+                return "ACTION_BUTTON_RELEASE";
         }
         int index = (action & ACTION_POINTER_INDEX_MASK) >> ACTION_POINTER_INDEX_SHIFT;
         switch (action & ACTION_MASK) {
@@ -3172,6 +3243,8 @@
      * @see #BUTTON_TERTIARY
      * @see #BUTTON_FORWARD
      * @see #BUTTON_BACK
+     * @see #BUTTON_STYLUS_PRIMARY
+     * @see #BUTTON_STYLUS_SECONDARY
      */
     public final boolean isButtonPressed(int button) {
         if (button == 0) {
@@ -3180,18 +3253,6 @@
         return (getButtonState() & button) == button;
     }
 
-    /**
-     * Checks if a stylus is being used and if the first stylus button is
-     * pressed.
-     *
-     * @return True if the tool is a stylus and if the first stylus button is
-     *         pressed.
-     * @see #BUTTON_SECONDARY
-     */
-    public final boolean isStylusButtonPressed() {
-        return (isButtonPressed(BUTTON_SECONDARY) && getToolType(0) == TOOL_TYPE_STYLUS);
-    }
-
     public static final Parcelable.Creator<MotionEvent> CREATOR
             = new Parcelable.Creator<MotionEvent>() {
         public MotionEvent createFromParcel(Parcel in) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 9e16b4b..6ca320a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5221,8 +5221,8 @@
      * @return True if the event was consumed.
      */
     private boolean performStylusActionOnButtonPress(MotionEvent event) {
-        if (isStylusButtonPressable() && !mInStylusButtonPress
-                && !mHasPerformedLongPress && event.isStylusButtonPressed()) {
+        if (isStylusButtonPressable() && !mInStylusButtonPress && !mHasPerformedLongPress
+                && event.isButtonPressed(MotionEvent.BUTTON_STYLUS_SECONDARY)) {
             if (performStylusButtonPress()) {
                 mInStylusButtonPress = true;
                 setPressed(true, event.getX(), event.getY());
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7c635b9..1cbd886 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -40,6 +40,7 @@
 import android.hardware.display.DisplayManager.DisplayListener;
 import android.media.AudioManager;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.Handler;
@@ -5352,7 +5353,7 @@
         //Log.d(TAG, ">>>>>> CALLING relayout");
         if (params != null && mOrigWindowType != params.type) {
             // For compatibility with old apps, don't crash here.
-            if (mTargetSdkVersion < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+            if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                 Slog.w(TAG, "Window type can not be changed after "
                         + "the window is added; ignoring change of " + mView);
                 params.type = mOrigWindowType;
@@ -5777,6 +5778,7 @@
 
     void enqueueInputEvent(InputEvent event,
             InputEventReceiver receiver, int flags, boolean processImmediately) {
+        adjustInputEventForCompatibility(event);
         QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
 
         // Always enqueue the input event in order, regardless of its time stamp.
@@ -5882,6 +5884,19 @@
         recycleQueuedInputEvent(q);
     }
 
+    private void adjustInputEventForCompatibility(InputEvent e) {
+        if (mTargetSdkVersion < Build.VERSION_CODES.MNC && e instanceof MotionEvent) {
+            MotionEvent motion = (MotionEvent) e;
+            final int mask =
+                MotionEvent.BUTTON_STYLUS_PRIMARY | MotionEvent.BUTTON_STYLUS_SECONDARY;
+            final int buttonState = motion.getButtonState();
+            final int compatButtonState = (buttonState & mask) >> 4;
+            if (compatButtonState != 0) {
+                motion.setButtonState(buttonState | compatButtonState);
+            }
+        }
+    }
+
     static boolean isTerminalInputEvent(InputEvent event) {
         if (event instanceof KeyEvent) {
             final KeyEvent keyEvent = (KeyEvent)event;
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 265daeb..d61dee7 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -135,7 +135,8 @@
     for (size_t i = 0; i <= event->getHistorySize(); i++) {
         publishedSeq = mNextPublishedSeq++;
         status_t status = mInputPublisher.publishMotionEvent(publishedSeq,
-                event->getDeviceId(), event->getSource(), event->getAction(), event->getFlags(),
+                event->getDeviceId(), event->getSource(),
+                event->getAction(), event->getActionButton(), event->getFlags(),
                 event->getEdgeFlags(), event->getMetaState(), event->getButtonState(),
                 event->getXOffset(), event->getYOffset(),
                 event->getXPrecision(), event->getYPrecision(),
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index e622768..1f28643 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -370,7 +370,7 @@
         env->DeleteLocalRef(pointerCoordsObj);
     }
 
-    event->initialize(deviceId, source, action, flags, edgeFlags, metaState, buttonState,
+    event->initialize(deviceId, source, action, flags, edgeFlags, metaState, buttonState, 0,
             xOffset, yOffset, xPrecision, yPrecision,
             downTimeNanos, eventTimeNanos, pointerCount, pointerProperties, rawPointerCoords);
 
@@ -456,6 +456,12 @@
     event->setAction(action);
 }
 
+static int android_view_MotionEvent_nativeGetActionButton(JNIEnv* env, jclass clazz,
+        jlong nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->getActionButton();
+}
+
 static jboolean android_view_MotionEvent_nativeIsTouchEvent(JNIEnv* env, jclass clazz,
         jlong nativePtr) {
     MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
@@ -498,6 +504,12 @@
     return event->getButtonState();
 }
 
+static void android_view_MotionEvent_nativeSetButtonState(JNIEnv* env, jclass clazz,
+        jlong nativePtr, jint buttonState) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    event->setButtonState(buttonState);
+}
+
 static void android_view_MotionEvent_nativeOffsetLocation(JNIEnv* env, jclass clazz,
         jlong nativePtr, jfloat deltaX, jfloat deltaY) {
     MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
@@ -764,6 +776,9 @@
     { "nativeSetAction",
             "(JI)V",
             (void*)android_view_MotionEvent_nativeSetAction },
+    { "nativeGetActionButton",
+            "(J)I",
+            (void*)android_view_MotionEvent_nativeGetActionButton},
     { "nativeIsTouchEvent",
             "(J)Z",
             (void*)android_view_MotionEvent_nativeIsTouchEvent },
@@ -785,6 +800,9 @@
     { "nativeGetButtonState",
             "(J)I",
             (void*)android_view_MotionEvent_nativeGetButtonState },
+    { "nativeSetButtonState",
+            "(JI)V",
+            (void*)android_view_MotionEvent_nativeSetButtonState },
     { "nativeOffsetLocation",
             "(JFF)V",
             (void*)android_view_MotionEvent_nativeOffsetLocation },