Move display id into MotionEvent

Display id is now part of MotionEvent.

Test: atest view.MotionEventTest view.KeyEventTest
Bug: 64258305

Change-Id: Ifadd6b34f4dd5a91669baf146daa62944d1de974
diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/core/java/android/hardware/input/InputManagerInternal.java
index 4ea0f55..24f6989 100644
--- a/core/java/android/hardware/input/InputManagerInternal.java
+++ b/core/java/android/hardware/input/InputManagerInternal.java
@@ -30,7 +30,14 @@
  * @hide Only for use within the system server.
  */
 public abstract class InputManagerInternal {
-    public abstract boolean injectInputEvent(InputEvent event, int displayId, int mode);
+    /**
+     * Inject an input event.
+     *
+     * @param event The InputEvent to inject
+     * @param mode Synchronous or asynchronous mode
+     * @return True if injection has succeeded
+     */
+    public abstract boolean injectInputEvent(InputEvent event, int mode);
 
     /**
      * Called by the display manager to set information about the displays as needed
diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
index d2e3510..0982d65 100644
--- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
@@ -218,7 +218,7 @@
         }
 
         @Override
-        public void onInputEvent(InputEvent event, int displayId) {
+        public void onInputEvent(InputEvent event) {
             if (mInputMethodSession == null) {
                 // The session has been finished.
                 finishInputEvent(event, false);
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 8588df7..c65710d 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -287,7 +287,7 @@
             }
 
             @Override
-            public void onInputEvent(InputEvent event, int displayId) {
+            public void onInputEvent(InputEvent event) {
                 boolean handled = false;
                 try {
                     if (event instanceof MotionEvent
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
index e2ad3ad..1f2aab9 100644
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -51,7 +51,7 @@
      * zero indicates that the event didn't come from a physical device
      * and maps to the default keymap.  The other numbers are arbitrary and
      * you shouldn't depend on the values.
-     * 
+     *
      * @return The device id.
      * @see InputDevice#getDevice
      */
@@ -59,7 +59,7 @@
 
     /**
      * Gets the device that this event came from.
-     * 
+     *
      * @return The device, or null if unknown.
      */
     public final InputDevice getDevice() {
@@ -68,7 +68,7 @@
 
     /**
      * Gets the source of the event.
-     * 
+     *
      * @return The event source or {@link InputDevice#SOURCE_UNKNOWN} if unknown.
      * @see InputDevice#getSources
      */
@@ -234,7 +234,7 @@
                 throw new IllegalStateException("Unexpected input event type token in parcel.");
             }
         }
-        
+
         public InputEvent[] newArray(int size) {
             return new InputEvent[size];
         }
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
index c566a65..20ab539 100644
--- a/core/java/android/view/InputEventReceiver.java
+++ b/core/java/android/view/InputEventReceiver.java
@@ -111,10 +111,9 @@
      * to indicate whether the event was handled.  No new input events will be received
      * until {@link #finishInputEvent} is called.
      *
-     * @param displayId The display id on which input event triggered.
      * @param event The input event that was received.
      */
-    public void onInputEvent(InputEvent event, int displayId) {
+    public void onInputEvent(InputEvent event) {
         finishInputEvent(event, false);
     }
 
@@ -181,9 +180,9 @@
 
     // Called from native code.
     @SuppressWarnings("unused")
-    private void dispatchInputEvent(int seq, InputEvent event, int displayId) {
+    private void dispatchInputEvent(int seq, InputEvent event) {
         mSeqMap.put(event.getSequenceNumber(), seq);
-        onInputEvent(event, displayId);
+        onInputEvent(event);
     }
 
     // Called from native code.
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index a597405..dec0ecc 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1556,8 +1556,8 @@
      * @hide
      */
     public static KeyEvent obtain(long downTime, long eventTime, int action,
-                    int code, int repeat, int metaState,
-                    int deviceId, int scancode, int flags, int source, String characters) {
+            int code, int repeat, int metaState,
+            int deviceId, int scancode, int flags, int source, String characters) {
         KeyEvent ev = obtain();
         ev.mDownTime = downTime;
         ev.mEventTime = eventTime;
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 1d7c1de..9148c27 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -16,11 +16,14 @@
 
 package android.view;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import android.annotation.TestApi;
 import android.graphics.Matrix;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
+import android.util.Log;
 import android.util.SparseArray;
 
 import dalvik.annotation.optimization.CriticalNative;
@@ -172,6 +175,7 @@
  * </p>
  */
 public final class MotionEvent extends InputEvent implements Parcelable {
+    private static final String TAG = "MotionEvent";
     private static final long NS_PER_MS = 1000000;
     private static final String LABEL_PREFIX = "AXIS_";
 
@@ -1470,7 +1474,7 @@
     private MotionEvent mNext;
 
     private static native long nativeInitialize(long nativePtr,
-            int deviceId, int source, int action, int flags, int edgeFlags,
+            int deviceId, int source, int displayId, int action, int flags, int edgeFlags,
             int metaState, int buttonState,
             float xOffset, float yOffset, float xPrecision, float yPrecision,
             long downTimeNanos, long eventTimeNanos,
@@ -1514,7 +1518,11 @@
     @CriticalNative
     private static native int nativeGetSource(long nativePtr);
     @CriticalNative
-    private static native int nativeSetSource(long nativePtr, int source);
+    private static native void nativeSetSource(long nativePtr, int source);
+    @CriticalNative
+    private static native int nativeGetDisplayId(long nativePtr);
+    @CriticalNative
+    private static native void nativeSetDisplayId(long nativePtr, int displayId);
     @CriticalNative
     private static native int nativeGetAction(long nativePtr);
     @CriticalNative
@@ -1623,19 +1631,26 @@
      * @param edgeFlags A bitfield indicating which edges, if any, were touched by this
      * MotionEvent.
      * @param source The source of this event.
+     * @param displayId The display ID associated with this event.
      * @param flags The motion event flags.
+     * @hide
      */
     static public MotionEvent obtain(long downTime, long eventTime,
             int action, int pointerCount, PointerProperties[] pointerProperties,
             PointerCoords[] pointerCoords, int metaState, int buttonState,
             float xPrecision, float yPrecision, int deviceId,
-            int edgeFlags, int source, int flags) {
+            int edgeFlags, int source, int displayId, int flags) {
         MotionEvent ev = obtain();
         ev.mNativePtr = nativeInitialize(ev.mNativePtr,
-                deviceId, source, action, flags, edgeFlags, metaState, buttonState,
+                deviceId, source, displayId, action, flags, edgeFlags, metaState, buttonState,
                 0, 0, xPrecision, yPrecision,
                 downTime * NS_PER_MS, eventTime * NS_PER_MS,
                 pointerCount, pointerProperties, pointerCoords);
+        if (ev.mNativePtr == 0) {
+            Log.e(TAG, "Could not initialize MotionEvent");
+            ev.recycle();
+            return null;
+        }
         return ev;
     }
 
@@ -1649,6 +1664,44 @@
      * must be obtained from {@link SystemClock#uptimeMillis()}.
      * @param action The kind of action being performed, such as {@link #ACTION_DOWN}.
      * @param pointerCount The number of pointers that will be in this event.
+     * @param pointerProperties An array of <em>pointerCount</em> values providing
+     * a {@link PointerProperties} property object for each pointer, which must
+     * include the pointer identifier.
+     * @param pointerCoords An array of <em>pointerCount</em> values providing
+     * a {@link PointerCoords} coordinate object for each pointer.
+     * @param metaState The state of any meta / modifier keys that were in effect when
+     * the event was generated.
+     * @param buttonState The state of buttons that are pressed.
+     * @param xPrecision The precision of the X coordinate being reported.
+     * @param yPrecision The precision of the Y coordinate being reported.
+     * @param deviceId The id for the device that this event came from.  An id of
+     * zero indicates that the event didn't come from a physical device; other
+     * numbers are arbitrary and you shouldn't depend on the values.
+     * @param edgeFlags A bitfield indicating which edges, if any, were touched by this
+     * MotionEvent.
+     * @param source The source of this event.
+     * @param flags The motion event flags.
+     */
+    public static MotionEvent obtain(long downTime, long eventTime,
+            int action, int pointerCount, PointerProperties[] pointerProperties,
+            PointerCoords[] pointerCoords, int metaState, int buttonState,
+            float xPrecision, float yPrecision, int deviceId,
+            int edgeFlags, int source, int flags) {
+        return obtain(downTime, eventTime, action, pointerCount, pointerProperties, pointerCoords,
+                metaState, buttonState, xPrecision, yPrecision, deviceId, edgeFlags, source,
+                DEFAULT_DISPLAY, flags);
+    }
+
+    /**
+     * Create a new MotionEvent, filling in all of the basic values that
+     * define the motion.
+     *
+     * @param downTime The time (in ms) when the user originally pressed down to start
+     * a stream of position events.  This must be obtained from {@link SystemClock#uptimeMillis()}.
+     * @param eventTime The the time (in ms) when this specific event was generated.  This
+     * must be obtained from {@link SystemClock#uptimeMillis()}.
+     * @param action The kind of action being performed, such as {@link #ACTION_DOWN}.
+     * @param pointerCount The number of pointers that will be in this event.
      * @param pointerIds An array of <em>pointerCount</em> values providing
      * an identifier for each pointer.
      * @param pointerCoords An array of <em>pointerCount</em> values providing
@@ -1733,7 +1786,8 @@
             pc[0].size = size;
 
             ev.mNativePtr = nativeInitialize(ev.mNativePtr,
-                    deviceId, InputDevice.SOURCE_UNKNOWN, action, 0, edgeFlags, metaState, 0,
+                    deviceId, InputDevice.SOURCE_UNKNOWN, DEFAULT_DISPLAY,
+                    action, 0, edgeFlags, metaState, 0,
                     0, 0, xPrecision, yPrecision,
                     downTime * NS_PER_MS, eventTime * NS_PER_MS,
                     1, pp, pc);
@@ -1888,6 +1942,16 @@
         nativeSetSource(mNativePtr, source);
     }
 
+    /** @hide */
+    public int getDisplayId() {
+        return nativeGetDisplayId(mNativePtr);
+    }
+
+    /** @hide */
+    public void setDisplayId(int displayId) {
+        nativeSetDisplayId(mNativePtr, displayId);
+    }
+
     /**
      * Return the kind of action being performed.
      * Consider using {@link #getActionMasked} and {@link #getActionIndex} to retrieve
@@ -3023,7 +3087,7 @@
     /**
      * Adds all of the movement samples of the specified event to this one if
      * it is compatible.  To be compatible, the event must have the same device id,
-     * source, action, flags, pointer count, pointer properties.
+     * source, display id, action, flags, pointer count, pointer properties.
      *
      * Only applies to {@link #ACTION_MOVE} or {@link #ACTION_HOVER_MOVE} events.
      *
@@ -3043,6 +3107,7 @@
 
         if (nativeGetDeviceId(mNativePtr) != nativeGetDeviceId(event.mNativePtr)
                 || nativeGetSource(mNativePtr) != nativeGetSource(event.mNativePtr)
+                || nativeGetDisplayId(mNativePtr) != nativeGetDisplayId(event.mNativePtr)
                 || nativeGetFlags(mNativePtr) != nativeGetFlags(event.mNativePtr)) {
             return false;
         }
@@ -3128,6 +3193,7 @@
             }
             ev.mNativePtr = nativeInitialize(ev.mNativePtr,
                     nativeGetDeviceId(mNativePtr), nativeGetSource(mNativePtr),
+                    nativeGetDisplayId(mNativePtr),
                     nativeGetAction(mNativePtr), nativeGetFlags(mNativePtr),
                     nativeGetEdgeFlags(mNativePtr), nativeGetMetaState(mNativePtr),
                     nativeGetButtonState(mNativePtr),
@@ -3172,7 +3238,6 @@
                     >> ACTION_POINTER_INDEX_SHIFT;
             int newActionPointerIndex = -1;
             int newPointerCount = 0;
-            int newIdBits = 0;
             for (int i = 0; i < oldPointerCount; i++) {
                 nativeGetPointerProperties(mNativePtr, i, pp[newPointerCount]);
                 final int idBit = 1 << pp[newPointerCount].id;
@@ -3182,7 +3247,6 @@
                     }
                     map[newPointerCount] = i;
                     newPointerCount += 1;
-                    newIdBits |= idBit;
                 }
             }
 
@@ -3221,6 +3285,7 @@
                 if (h == 0) {
                     ev.mNativePtr = nativeInitialize(ev.mNativePtr,
                             nativeGetDeviceId(mNativePtr), nativeGetSource(mNativePtr),
+                            nativeGetDisplayId(mNativePtr),
                             newAction, nativeGetFlags(mNativePtr),
                             nativeGetEdgeFlags(mNativePtr), nativeGetMetaState(mNativePtr),
                             nativeGetButtonState(mNativePtr),
@@ -3266,6 +3331,7 @@
             msg.append(", downTime=").append(getDownTime());
             msg.append(", deviceId=").append(getDeviceId());
             msg.append(", source=0x").append(Integer.toHexString(getSource()));
+            msg.append(", displayId=").append(getDisplayId());
         }
         msg.append(" }");
         return msg.toString();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 01d9265..9564e7c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -7098,7 +7098,7 @@
         }
 
         @Override
-        public void onInputEvent(InputEvent event, int displayId) {
+        public void onInputEvent(InputEvent event) {
             enqueueInputEvent(event, this, 0, true);
         }
 
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index a6f36bb..f364d82 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -16,8 +16,6 @@
 
 package android.view;
 
-import static android.view.Display.DEFAULT_DISPLAY;
-
 /**
  * Constants for interfacing with WindowManagerService and WindowManagerPolicyInternal.
  * @hide
@@ -75,15 +73,6 @@
          * copy() must be made and the copy must be recycled.
          **/
         void onPointerEvent(MotionEvent motionEvent);
-
-        /**
-         * @see #onPointerEvent(MotionEvent)
-         **/
-        default void onPointerEvent(MotionEvent motionEvent, int displayId) {
-            if (displayId == DEFAULT_DISPLAY) {
-                onPointerEvent(motionEvent);
-            }
-        }
     }
 
     /** Screen turned off because of a device admin */
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 8fee7ba..fb6dd93 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -236,9 +236,8 @@
     for (;;) {
         uint32_t seq;
         InputEvent* inputEvent;
-        int32_t displayId;
         status_t status = mInputConsumer.consume(&mInputEventFactory,
-                consumeBatches, frameTime, &seq, &inputEvent, &displayId);
+                consumeBatches, frameTime, &seq, &inputEvent);
         if (status) {
             if (status == WOULD_BLOCK) {
                 if (!skipCallbacks && !mBatchedInputEventPending
@@ -315,8 +314,7 @@
                     ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName().c_str());
                 }
                 env->CallVoidMethod(receiverObj.get(),
-                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj,
-                        displayId);
+                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
                 if (env->ExceptionCheck()) {
                     ALOGE("Exception dispatching input event.");
                     skipCallbacks = true;
@@ -423,7 +421,7 @@
 
     gInputEventReceiverClassInfo.dispatchInputEvent = GetMethodIDOrDie(env,
             gInputEventReceiverClassInfo.clazz,
-            "dispatchInputEvent", "(ILandroid/view/InputEvent;I)V");
+            "dispatchInputEvent", "(ILandroid/view/InputEvent;)V");
     gInputEventReceiverClassInfo.dispatchBatchedInputEventPending = GetMethodIDOrDie(env,
             gInputEventReceiverClassInfo.clazz, "dispatchBatchedInputEventPending", "()V");
 
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index effeed6..095252c 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -39,8 +39,6 @@
 
 // Log debug messages about the dispatch cycle.
 static const bool kDebugDispatchCycle = false;
-// Display id for default(primary) display.
-static const int32_t kDefaultDisplayId = 0;
 
 static struct {
     jclass clazz;
@@ -138,7 +136,7 @@
         publishedSeq = mNextPublishedSeq++;
         status_t status = mInputPublisher.publishMotionEvent(publishedSeq,
                 event->getDeviceId(), event->getSource(),
-                kDefaultDisplayId /* TODO(multi-display): propagate display id */,
+                event->getDisplayId(),
                 event->getAction(), event->getActionButton(), event->getFlags(),
                 event->getEdgeFlags(), event->getMetaState(), event->getButtonState(),
                 event->getXOffset(), event->getYOffset(),
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 64bf0dc..ecf8119 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -333,7 +333,7 @@
 
 static jlong android_view_MotionEvent_nativeInitialize(JNIEnv* env, jclass clazz,
         jlong nativePtr,
-        jint deviceId, jint source, jint action, jint flags, jint edgeFlags,
+        jint deviceId, jint source, jint displayId, jint action, jint flags, jint edgeFlags,
         jint metaState, jint buttonState,
         jfloat xOffset, jfloat yOffset, jfloat xPrecision, jfloat yPrecision,
         jlong downTimeNanos, jlong eventTimeNanos,
@@ -372,8 +372,8 @@
         env->DeleteLocalRef(pointerCoordsObj);
     }
 
-    event->initialize(deviceId, source, action, 0, flags, edgeFlags, metaState, buttonState,
-            xOffset, yOffset, xPrecision, yPrecision,
+    event->initialize(deviceId, source, displayId, action, 0, flags, edgeFlags, metaState,
+            buttonState, xOffset, yOffset, xPrecision, yPrecision,
             downTimeNanos, eventTimeNanos, pointerCount, pointerProperties, rawPointerCoords);
 
     return reinterpret_cast<jlong>(event);
@@ -598,6 +598,16 @@
     event->setSource(source);
 }
 
+static jint android_view_MotionEvent_nativeGetDisplayId(jlong nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->getDisplayId();
+}
+
+static void android_view_MotionEvent_nativeSetDisplayId(jlong nativePtr, jint displayId) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->setDisplayId(displayId);
+}
+
 static jint android_view_MotionEvent_nativeGetAction(jlong nativePtr) {
     MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
     return event->getAction();
@@ -737,7 +747,7 @@
 static const JNINativeMethod gMotionEventMethods[] = {
     /* name, signature, funcPtr */
     { "nativeInitialize",
-            "(JIIIIIIIFFFFJJI[Landroid/view/MotionEvent$PointerProperties;"
+            "(JIIIIIIIIFFFFJJI[Landroid/view/MotionEvent$PointerProperties;"
                     "[Landroid/view/MotionEvent$PointerCoords;)J",
             (void*)android_view_MotionEvent_nativeInitialize },
     { "nativeDispose",
@@ -792,8 +802,14 @@
             "(J)I",
             (void*)android_view_MotionEvent_nativeGetSource },
     { "nativeSetSource",
-            "(JI)I",
+            "(JI)V",
             (void*)android_view_MotionEvent_nativeSetSource },
+    { "nativeGetDisplayId",
+            "(J)I",
+            (void*)android_view_MotionEvent_nativeGetDisplayId },
+    { "nativeSetDisplayId",
+            "(JI)V",
+            (void*)android_view_MotionEvent_nativeSetDisplayId },
     { "nativeGetAction",
             "(J)I",
             (void*)android_view_MotionEvent_nativeGetAction },
diff --git a/core/tests/coretests/src/android/view/KeyEventTest.java b/core/tests/coretests/src/android/view/KeyEventTest.java
new file mode 100644
index 0000000..aabf816
--- /dev/null
+++ b/core/tests/coretests/src/android/view/KeyEventTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static org.junit.Assert.assertEquals;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KeyEventTest {
+
+    @Test
+    public void testObtain() {
+        KeyEvent keyEvent = KeyEvent.obtain(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0,
+                0, 0, 0, 0, 0, InputDevice.SOURCE_KEYBOARD, null);
+        assertEquals(KeyEvent.ACTION_DOWN, keyEvent.getAction());
+        assertEquals(KeyEvent.KEYCODE_0, keyEvent.getKeyCode());
+        assertEquals(InputDevice.SOURCE_KEYBOARD, keyEvent.getSource());
+    }
+}
diff --git a/core/tests/coretests/src/android/view/MotionEventTest.java b/core/tests/coretests/src/android/view/MotionEventTest.java
new file mode 100644
index 0000000..1a480c7
--- /dev/null
+++ b/core/tests/coretests/src/android/view/MotionEventTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.TOOL_TYPE_FINGER;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MotionEventTest {
+
+    @Test
+    public void testObtainWithDisplayId() {
+        final int pointerCount = 1;
+        PointerProperties[] properties = new PointerProperties[pointerCount];
+        final PointerCoords[] coords = new PointerCoords[pointerCount];
+        for (int i = 0; i < pointerCount; i++) {
+            final PointerCoords c = new PointerCoords();
+            c.x = i * 10;
+            c.y = i * 20;
+            coords[i] = c;
+            final PointerProperties p = new PointerProperties();
+            p.id = i;
+            p.toolType = TOOL_TYPE_FINGER;
+            properties[i] = p;
+        }
+
+        int displayId = 2;
+        MotionEvent motionEvent = MotionEvent.obtain(0, 0, ACTION_DOWN,
+                pointerCount, properties, coords,
+                0, 0, 0, 0, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, displayId, 0);
+        assertEquals(displayId, motionEvent.getDisplayId());
+
+        displayId = 5;
+        motionEvent.setDisplayId(displayId);
+        assertEquals(displayId, motionEvent.getDisplayId());
+        motionEvent.recycle();
+
+        // If invalid PointerProperties object is passed to obtain,
+        // there should not be a native crash, and instead it should just return null
+        properties[0] = null;
+        motionEvent = MotionEvent.obtain(0, 0, ACTION_DOWN,
+                pointerCount, properties, coords,
+                0, 0, 0, 0, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, displayId, 0);
+        assertNull(motionEvent);
+    }
+}