Make input system aware of multiple displays.

The input system needs to know about the window that has
focus, even if it is on a secondary display.  So now we
send it the list of all windows and indicate which display
they are on.  We filter the list of windows as necessary
when delivering touch events.

To keep things simple, monitor input channels and input
filters are not supported except on the main display.
We also do not pass the display id to applications; it is
only used inside the input system for now.

Properly scale touch coordinates based on the viewport.
This will be needed to ensure that touch works on external
display as well as when the internal display is being used
to simulate a different resolution.

Change-Id: I1815579a52fcc852c519b5391fc7ab8767c2dc59
diff --git a/include/androidfw/Input.h b/include/androidfw/Input.h
index 2c91fab..e88835e 100644
--- a/include/androidfw/Input.h
+++ b/include/androidfw/Input.h
@@ -49,6 +49,15 @@
 };
 
 enum {
+    /* Used when a motion event is not associated with any display.
+     * Typically used for non-pointer events. */
+    ADISPLAY_ID_NONE = -1,
+
+    /* The default display id. */
+    ADISPLAY_ID_DEFAULT = 0,
+};
+
+enum {
     /*
      * Indicates that an input device has switches.
      * This input source flag is hidden from the API because switches are only used by the system
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index 8604f95..87a6c1b 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -166,6 +166,10 @@
     return true;
 }
 
+static bool isMainDisplay(int32_t displayId) {
+    return displayId == ADISPLAY_ID_DEFAULT || displayId == ADISPLAY_ID_NONE;
+}
+
 static void dumpRegion(String8& dump, const SkRegion& region) {
     if (region.isEmpty()) {
         dump.append("<empty>");
@@ -423,11 +427,12 @@
                 && (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
                 && mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY
                 && mInputTargetWaitApplicationHandle != NULL) {
+            int32_t displayId = motionEntry->displayId;
             int32_t x = int32_t(motionEntry->pointerCoords[0].
                     getAxisValue(AMOTION_EVENT_AXIS_X));
             int32_t y = int32_t(motionEntry->pointerCoords[0].
                     getAxisValue(AMOTION_EVENT_AXIS_Y));
-            sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(x, y);
+            sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y);
             if (touchedWindowHandle != NULL
                     && touchedWindowHandle->inputApplicationHandle
                             != mInputTargetWaitApplicationHandle) {
@@ -444,28 +449,31 @@
     return needWake;
 }
 
-sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t x, int32_t y) {
+sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId,
+        int32_t x, int32_t y) {
     // Traverse windows from front to back to find touched window.
     size_t numWindows = mWindowHandles.size();
     for (size_t i = 0; i < numWindows; i++) {
         sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
         const InputWindowInfo* windowInfo = windowHandle->getInfo();
-        int32_t flags = windowInfo->layoutParamsFlags;
+        if (windowInfo->displayId == displayId) {
+            int32_t flags = windowInfo->layoutParamsFlags;
 
-        if (windowInfo->visible) {
-            if (!(flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
-                bool isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
-                        | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
-                if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
-                    // Found window.
-                    return windowHandle;
+            if (windowInfo->visible) {
+                if (!(flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
+                    bool isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
+                            | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
+                    if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
+                        // Found window.
+                        return windowHandle;
+                    }
                 }
             }
-        }
 
-        if (flags & InputWindowInfo::FLAG_SYSTEM_ERROR) {
-            // Error window is on top but not visible, so touch is dropped.
-            return NULL;
+            if (flags & InputWindowInfo::FLAG_SYSTEM_ERROR) {
+                // Error window is on top but not visible, so touch is dropped.
+                return NULL;
+            }
         }
     }
     return NULL;
@@ -826,7 +834,10 @@
         return true;
     }
 
-    addMonitoringTargetsLocked(inputTargets);
+    // TODO: support sending secondary display events to input monitors
+    if (isMainDisplay(entry->displayId)) {
+        addMonitoringTargetsLocked(inputTargets);
+    }
 
     // Dispatch the motion.
     if (conflictingPointerActions) {
@@ -1117,6 +1128,7 @@
     //
     bool screenWasOff = false; // original policy: policyFlags & POLICY_FLAG_BRIGHT_HERE;
 
+    int32_t displayId = entry->displayId;
     int32_t action = entry->action;
     int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
 
@@ -1126,9 +1138,10 @@
     sp<InputWindowHandle> newHoverWindowHandle;
 
     bool isSplit = mTouchState.split;
-    bool switchedDevice = mTouchState.deviceId >= 0
+    bool switchedDevice = mTouchState.deviceId >= 0 && mTouchState.displayId >= 0
             && (mTouchState.deviceId != entry->deviceId
-                    || mTouchState.source != entry->source);
+                    || mTouchState.source != entry->source
+                    || mTouchState.displayId != displayId);
     bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE
             || maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER
             || maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
@@ -1152,6 +1165,7 @@
         mTempTouchState.down = down;
         mTempTouchState.deviceId = entry->deviceId;
         mTempTouchState.source = entry->source;
+        mTempTouchState.displayId = displayId;
         isSplit = false;
     } else {
         mTempTouchState.copyFrom(mTouchState);
@@ -1174,8 +1188,11 @@
         for (size_t i = 0; i < numWindows; i++) {
             sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
             const InputWindowInfo* windowInfo = windowHandle->getInfo();
-            int32_t flags = windowInfo->layoutParamsFlags;
+            if (windowInfo->displayId != displayId) {
+                continue; // wrong display
+            }
 
+            int32_t flags = windowInfo->layoutParamsFlags;
             if (flags & InputWindowInfo::FLAG_SYSTEM_ERROR) {
                 if (topErrorWindowHandle == NULL) {
                     topErrorWindowHandle = windowHandle;
@@ -1300,7 +1317,8 @@
 
             sp<InputWindowHandle> oldTouchedWindowHandle =
                     mTempTouchState.getFirstForegroundWindowHandle();
-            sp<InputWindowHandle> newTouchedWindowHandle = findTouchedWindowAtLocked(x, y);
+            sp<InputWindowHandle> newTouchedWindowHandle =
+                    findTouchedWindowAtLocked(displayId, x, y);
             if (oldTouchedWindowHandle != newTouchedWindowHandle
                     && newTouchedWindowHandle != NULL) {
 #if DEBUG_FOCUS
@@ -1438,8 +1456,10 @@
         if (foregroundWindowHandle->getInfo()->hasWallpaper) {
             for (size_t i = 0; i < mWindowHandles.size(); i++) {
                 sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
-                if (windowHandle->getInfo()->layoutParamsType
-                        == InputWindowInfo::TYPE_WALLPAPER) {
+                const InputWindowInfo* info = windowHandle->getInfo();
+                if (info->displayId == displayId
+                        && windowHandle->getInfo()->layoutParamsType
+                                == InputWindowInfo::TYPE_WALLPAPER) {
                     mTempTouchState.addOrUpdateWindow(windowHandle,
                             InputTarget::FLAG_WINDOW_IS_OBSCURED
                                     | InputTarget::FLAG_DISPATCH_AS_IS,
@@ -1495,6 +1515,7 @@
                         || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
                     mTouchState.deviceId = entry->deviceId;
                     mTouchState.source = entry->source;
+                    mTouchState.displayId = displayId;
                 }
             } else if (maskedAction == AMOTION_EVENT_ACTION_UP
                     || maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
@@ -1610,6 +1631,7 @@
 
 bool InputDispatcher::isWindowObscuredAtPointLocked(
         const sp<InputWindowHandle>& windowHandle, int32_t x, int32_t y) const {
+    int32_t displayId = windowHandle->getInfo()->displayId;
     size_t numWindows = mWindowHandles.size();
     for (size_t i = 0; i < numWindows; i++) {
         sp<InputWindowHandle> otherHandle = mWindowHandles.itemAt(i);
@@ -1618,7 +1640,8 @@
         }
 
         const InputWindowInfo* otherInfo = otherHandle->getInfo();
-        if (otherInfo->visible && ! otherInfo->isTrustedOverlay()
+        if (otherInfo->displayId == displayId
+                && otherInfo->visible && !otherInfo->isTrustedOverlay()
                 && otherInfo->frameContainsPoint(x, y)) {
             return true;
         }
@@ -1845,7 +1868,7 @@
         }
         if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE
                 && !connection->inputState.isHovering(
-                        motionEntry->deviceId, motionEntry->source)) {
+                        motionEntry->deviceId, motionEntry->source, motionEntry->displayId)) {
 #if DEBUG_DISPATCH_CYCLE
         ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter event",
                 connection->getInputChannelName());
@@ -2271,6 +2294,7 @@
             originalMotionEntry->xPrecision,
             originalMotionEntry->yPrecision,
             originalMotionEntry->downTime,
+            originalMotionEntry->displayId,
             splitPointerCount, splitPointerProperties, splitPointerCoords);
 
     if (originalMotionEntry->injectionState) {
@@ -2351,7 +2375,7 @@
     { // acquire lock
         mLock.lock();
 
-        if (mInputFilterEnabled) {
+        if (shouldSendKeyToInputFilterLocked(args)) {
             mLock.unlock();
 
             policyFlags |= POLICY_FLAG_FILTERED;
@@ -2377,6 +2401,10 @@
     }
 }
 
+bool InputDispatcher::shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) {
+    return mInputFilterEnabled;
+}
+
 void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
 #if DEBUG_INBOUND_EVENT_DETAILS
     ALOGD("notifyMotion - eventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, "
@@ -2415,7 +2443,7 @@
     { // acquire lock
         mLock.lock();
 
-        if (mInputFilterEnabled) {
+        if (shouldSendMotionToInputFilterLocked(args)) {
             mLock.unlock();
 
             MotionEvent event;
@@ -2438,6 +2466,7 @@
                 args->deviceId, args->source, policyFlags,
                 args->action, args->flags, args->metaState, args->buttonState,
                 args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime,
+                args->displayId,
                 args->pointerCount, args->pointerProperties, args->pointerCoords);
 
         needWake = enqueueInboundEventLocked(newEntry);
@@ -2449,6 +2478,11 @@
     }
 }
 
+bool InputDispatcher::shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) {
+    // TODO: support sending secondary display events to input filter
+    return mInputFilterEnabled && isMainDisplay(args->displayId);
+}
+
 void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) {
 #if DEBUG_INBOUND_EVENT_DETAILS
     ALOGD("notifySwitch - eventTime=%lld, policyFlags=0x%x, switchCode=%d, switchValue=%d",
@@ -2532,6 +2566,7 @@
 
     case AINPUT_EVENT_TYPE_MOTION: {
         const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event);
+        int32_t displayId = ADISPLAY_ID_DEFAULT;
         int32_t action = motionEvent->getAction();
         size_t pointerCount = motionEvent->getPointerCount();
         const PointerProperties* pointerProperties = motionEvent->getPointerProperties();
@@ -2553,8 +2588,8 @@
                 motionEvent->getMetaState(), motionEvent->getButtonState(),
                 motionEvent->getEdgeFlags(),
                 motionEvent->getXPrecision(), motionEvent->getYPrecision(),
-                motionEvent->getDownTime(), uint32_t(pointerCount),
-                pointerProperties, samplePointerCoords);
+                motionEvent->getDownTime(), displayId,
+                uint32_t(pointerCount), pointerProperties, samplePointerCoords);
         lastInjectedEntry = firstInjectedEntry;
         for (size_t i = motionEvent->getHistorySize(); i > 0; i--) {
             sampleEventTimes += 1;
@@ -2565,8 +2600,8 @@
                     motionEvent->getMetaState(), motionEvent->getButtonState(),
                     motionEvent->getEdgeFlags(),
                     motionEvent->getXPrecision(), motionEvent->getYPrecision(),
-                    motionEvent->getDownTime(), uint32_t(pointerCount),
-                    pointerProperties, samplePointerCoords);
+                    motionEvent->getDownTime(), displayId,
+                    uint32_t(pointerCount), pointerProperties, samplePointerCoords);
             lastInjectedEntry->next = nextInjectedEntry;
             lastInjectedEntry = nextInjectedEntry;
         }
@@ -2939,6 +2974,12 @@
 #endif
             return true;
         }
+        if (fromWindowHandle->getInfo()->displayId != toWindowHandle->getInfo()->displayId) {
+#if DEBUG_FOCUS
+            ALOGD("Cannot transfer focus because windows are on different displays.");
+#endif
+            return false;
+        }
 
         bool found = false;
         for (size_t i = 0; i < mTouchState.windows.size(); i++) {
@@ -3040,6 +3081,7 @@
     dump.appendFormat(INDENT "TouchSplit: %s\n", toString(mTouchState.split));
     dump.appendFormat(INDENT "TouchDeviceId: %d\n", mTouchState.deviceId);
     dump.appendFormat(INDENT "TouchSource: 0x%08x\n", mTouchState.source);
+    dump.appendFormat(INDENT "TouchDisplayId: %d\n", mTouchState.displayId);
     if (!mTouchState.windows.isEmpty()) {
         dump.append(INDENT "TouchedWindows:\n");
         for (size_t i = 0; i < mTouchState.windows.size(); i++) {
@@ -3059,11 +3101,12 @@
             const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
             const InputWindowInfo* windowInfo = windowHandle->getInfo();
 
-            dump.appendFormat(INDENT2 "%d: name='%s', paused=%s, hasFocus=%s, hasWallpaper=%s, "
+            dump.appendFormat(INDENT2 "%d: name='%s', displayId=%d, "
+                    "paused=%s, hasFocus=%s, hasWallpaper=%s, "
                     "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
                     "frame=[%d,%d][%d,%d], scale=%f, "
                     "touchableRegion=",
-                    i, windowInfo->name.string(),
+                    i, windowInfo->name.string(), windowInfo->displayId,
                     toString(windowInfo->paused),
                     toString(windowInfo->hasFocus),
                     toString(windowInfo->hasWallpaper),
@@ -3802,14 +3845,14 @@
         int32_t deviceId, uint32_t source, uint32_t policyFlags, int32_t action, int32_t flags,
         int32_t metaState, int32_t buttonState,
         int32_t edgeFlags, float xPrecision, float yPrecision,
-        nsecs_t downTime, uint32_t pointerCount,
+        nsecs_t downTime, int32_t displayId, uint32_t pointerCount,
         const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) :
         EventEntry(TYPE_MOTION, eventTime, policyFlags),
         eventTime(eventTime),
         deviceId(deviceId), source(source), action(action), flags(flags),
         metaState(metaState), buttonState(buttonState), edgeFlags(edgeFlags),
         xPrecision(xPrecision), yPrecision(yPrecision),
-        downTime(downTime), pointerCount(pointerCount) {
+        downTime(downTime), displayId(displayId), pointerCount(pointerCount) {
     for (uint32_t i = 0; i < pointerCount; i++) {
         this->pointerProperties[i].copyFrom(pointerProperties[i]);
         this->pointerCoords[i].copyFrom(pointerCoords[i]);
@@ -3820,8 +3863,8 @@
 }
 
 void InputDispatcher::MotionEntry::appendDescription(String8& msg) const {
-    msg.appendFormat("MotionEvent(action=%d, deviceId=%d, source=0x%08x)",
-            action, deviceId, source);
+    msg.appendFormat("MotionEvent(action=%d, deviceId=%d, source=0x%08x, displayId=%d)",
+            action, deviceId, source, displayId);
 }
 
 
@@ -3864,11 +3907,13 @@
     return mKeyMementos.isEmpty() && mMotionMementos.isEmpty();
 }
 
-bool InputDispatcher::InputState::isHovering(int32_t deviceId, uint32_t source) const {
+bool InputDispatcher::InputState::isHovering(int32_t deviceId, uint32_t source,
+        int32_t displayId) const {
     for (size_t i = 0; i < mMotionMementos.size(); i++) {
         const MotionMemento& memento = mMotionMementos.itemAt(i);
         if (memento.deviceId == deviceId
                 && memento.source == source
+                && memento.displayId == displayId
                 && memento.hovering) {
             return true;
         }
@@ -4025,6 +4070,7 @@
         const MotionMemento& memento = mMotionMementos.itemAt(i);
         if (memento.deviceId == entry->deviceId
                 && memento.source == entry->source
+                && memento.displayId == entry->displayId
                 && memento.hovering == hovering) {
             return i;
         }
@@ -4055,6 +4101,7 @@
     memento.xPrecision = entry->xPrecision;
     memento.yPrecision = entry->yPrecision;
     memento.downTime = entry->downTime;
+    memento.displayId = entry->displayId;
     memento.setPointers(entry);
     memento.hovering = hovering;
     memento.policyFlags = entry->policyFlags;
@@ -4090,6 +4137,7 @@
                             : AMOTION_EVENT_ACTION_CANCEL,
                     memento.flags, 0, 0, 0,
                     memento.xPrecision, memento.yPrecision, memento.downTime,
+                    memento.displayId,
                     memento.pointerCount, memento.pointerProperties, memento.pointerCoords));
         }
     }
@@ -4108,7 +4156,8 @@
             for (size_t j = 0; j < other.mMotionMementos.size(); ) {
                 const MotionMemento& otherMemento = other.mMotionMementos.itemAt(j);
                 if (memento.deviceId == otherMemento.deviceId
-                        && memento.source == otherMemento.source) {
+                        && memento.source == otherMemento.source
+                        && memento.displayId == otherMemento.displayId) {
                     other.mMotionMementos.removeAt(j);
                 } else {
                     j += 1;
@@ -4240,7 +4289,7 @@
 // --- InputDispatcher::TouchState ---
 
 InputDispatcher::TouchState::TouchState() :
-    down(false), split(false), deviceId(-1), source(0) {
+    down(false), split(false), deviceId(-1), source(0), displayId(-1) {
 }
 
 InputDispatcher::TouchState::~TouchState() {
@@ -4251,6 +4300,7 @@
     split = false;
     deviceId = -1;
     source = 0;
+    displayId = -1;
     windows.clear();
 }
 
@@ -4259,6 +4309,7 @@
     split = other.split;
     deviceId = other.deviceId;
     source = other.source;
+    displayId = other.displayId;
     windows = other.windows;
 }
 
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index d0824fc..af7ff5e 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -511,15 +511,17 @@
         float xPrecision;
         float yPrecision;
         nsecs_t downTime;
+        int32_t displayId;
         uint32_t pointerCount;
         PointerProperties pointerProperties[MAX_POINTERS];
         PointerCoords pointerCoords[MAX_POINTERS];
 
         MotionEntry(nsecs_t eventTime,
-                int32_t deviceId, uint32_t source, uint32_t policyFlags, int32_t action,
-                int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags,
+                int32_t deviceId, uint32_t source, uint32_t policyFlags,
+                int32_t action, int32_t flags,
+                int32_t metaState, int32_t buttonState, int32_t edgeFlags,
                 float xPrecision, float yPrecision,
-                nsecs_t downTime, uint32_t pointerCount,
+                nsecs_t downTime, int32_t displayId, uint32_t pointerCount,
                 const PointerProperties* pointerProperties, const PointerCoords* pointerCoords);
         virtual void appendDescription(String8& msg) const;
 
@@ -696,7 +698,7 @@
 
         // Returns true if the specified source is known to have received a hover enter
         // motion event.
-        bool isHovering(int32_t deviceId, uint32_t source) const;
+        bool isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const;
 
         // Records tracking information for a key event that has just been published.
         // Returns true if the event should be delivered, false if it is inconsistent
@@ -752,6 +754,7 @@
             float xPrecision;
             float yPrecision;
             nsecs_t downTime;
+            int32_t displayId;
             uint32_t pointerCount;
             PointerProperties pointerProperties[MAX_POINTERS];
             PointerCoords pointerCoords[MAX_POINTERS];
@@ -867,7 +870,7 @@
     // to transfer focus to a new application.
     EventEntry* mNextUnblockedEvent;
 
-    sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t x, int32_t y);
+    sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y);
 
     // All registered connections mapped by channel file descriptor.
     KeyedVector<int, sp<Connection> > mConnectionsByFd;
@@ -899,6 +902,10 @@
     bool runCommandsLockedInterruptible();
     CommandEntry* postCommandLocked(Command command);
 
+    // Input filter processing.
+    bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args);
+    bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args);
+
     // Inbound event processing.
     void drainInboundQueueLocked();
     void releasePendingEventLocked();
@@ -928,6 +935,7 @@
         bool split;
         int32_t deviceId; // id of the device that is currently down, others are rejected
         uint32_t source;  // source of the device that is current down, others are rejected
+        int32_t displayId; // id to the display that currently has a touch, others are rejected
         Vector<TouchedWindow> windows;
 
         TouchState();
diff --git a/services/input/InputListener.cpp b/services/input/InputListener.cpp
index 657a6b9..c2705b0 100644
--- a/services/input/InputListener.cpp
+++ b/services/input/InputListener.cpp
@@ -69,12 +69,12 @@
 NotifyMotionArgs::NotifyMotionArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source,
         uint32_t policyFlags,
         int32_t action, int32_t flags, int32_t metaState, int32_t buttonState,
-        int32_t edgeFlags, uint32_t pointerCount,
+        int32_t edgeFlags, int32_t displayId, uint32_t pointerCount,
         const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
         float xPrecision, float yPrecision, nsecs_t downTime) :
         eventTime(eventTime), deviceId(deviceId), source(source), policyFlags(policyFlags),
         action(action), flags(flags), metaState(metaState), buttonState(buttonState),
-        edgeFlags(edgeFlags), pointerCount(pointerCount),
+        edgeFlags(edgeFlags), displayId(displayId), pointerCount(pointerCount),
         xPrecision(xPrecision), yPrecision(yPrecision), downTime(downTime) {
     for (uint32_t i = 0; i < pointerCount; i++) {
         this->pointerProperties[i].copyFrom(pointerProperties[i]);
@@ -87,7 +87,8 @@
         policyFlags(other.policyFlags),
         action(other.action), flags(other.flags),
         metaState(other.metaState), buttonState(other.buttonState),
-        edgeFlags(other.edgeFlags), pointerCount(other.pointerCount),
+        edgeFlags(other.edgeFlags), displayId(other.displayId),
+        pointerCount(other.pointerCount),
         xPrecision(other.xPrecision), yPrecision(other.yPrecision), downTime(other.downTime) {
     for (uint32_t i = 0; i < pointerCount; i++) {
         pointerProperties[i].copyFrom(other.pointerProperties[i]);
diff --git a/services/input/InputListener.h b/services/input/InputListener.h
index b1dc0b8..486852b 100644
--- a/services/input/InputListener.h
+++ b/services/input/InputListener.h
@@ -88,6 +88,7 @@
     int32_t metaState;
     int32_t buttonState;
     int32_t edgeFlags;
+    int32_t displayId;
     uint32_t pointerCount;
     PointerProperties pointerProperties[MAX_POINTERS];
     PointerCoords pointerCoords[MAX_POINTERS];
@@ -99,7 +100,7 @@
 
     NotifyMotionArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source, uint32_t policyFlags,
             int32_t action, int32_t flags, int32_t metaState, int32_t buttonState,
-            int32_t edgeFlags, uint32_t pointerCount,
+            int32_t edgeFlags, int32_t displayId, uint32_t pointerCount,
             const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
             float xPrecision, float yPrecision, nsecs_t downTime);
 
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 513dc13..d56b9a9 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -2431,6 +2431,7 @@
 
     mPointerVelocityControl.move(when, &deltaX, &deltaY);
 
+    int32_t displayId;
     if (mPointerController != NULL) {
         if (moved || scrolled || buttonsChanged) {
             mPointerController->setPresentation(
@@ -2451,9 +2452,11 @@
         mPointerController->getPosition(&x, &y);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+        displayId = ADISPLAY_ID_DEFAULT;
     } else {
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY);
+        displayId = ADISPLAY_ID_NONE;
     }
 
     pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
@@ -2485,7 +2488,8 @@
 
         NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
                 motionEventAction, 0, metaState, currentButtonState, 0,
-                1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, downTime);
+                displayId, 1, &pointerProperties, &pointerCoords,
+                mXPrecision, mYPrecision, downTime);
         getListener()->notifyMotion(&args);
 
         // Send hover move after UP to tell the application that the mouse is hovering now.
@@ -2494,7 +2498,8 @@
             NotifyMotionArgs hoverArgs(when, getDeviceId(), mSource, policyFlags,
                     AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
                     metaState, currentButtonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                    1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, downTime);
+                    displayId, 1, &pointerProperties, &pointerCoords,
+                    mXPrecision, mYPrecision, downTime);
             getListener()->notifyMotion(&hoverArgs);
         }
 
@@ -2506,7 +2511,8 @@
             NotifyMotionArgs scrollArgs(when, getDeviceId(), mSource, policyFlags,
                     AMOTION_EVENT_ACTION_SCROLL, 0, metaState, currentButtonState,
                     AMOTION_EVENT_EDGE_FLAG_NONE,
-                    1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, downTime);
+                    displayId, 1, &pointerProperties, &pointerCoords,
+                    mXPrecision, mYPrecision, downTime);
             getListener()->notifyMotion(&scrollArgs);
         }
     }
@@ -2539,7 +2545,8 @@
 TouchInputMapper::TouchInputMapper(InputDevice* device) :
         InputMapper(device),
         mSource(0), mDeviceMode(DEVICE_MODE_DISABLED),
-        mSurfaceOrientation(-1), mSurfaceWidth(-1), mSurfaceHeight(-1) {
+        mSurfaceWidth(-1), mSurfaceHeight(-1), mSurfaceLeft(0), mSurfaceTop(0),
+        mSurfaceOrientation(DISPLAY_ORIENTATION_0) {
 }
 
 TouchInputMapper::~TouchInputMapper() {
@@ -2601,6 +2608,8 @@
     dumpSurface(dump);
 
     dump.appendFormat(INDENT3 "Translation and Scaling Factors:\n");
+    dump.appendFormat(INDENT4 "XTranslate: %0.3f\n", mXTranslate);
+    dump.appendFormat(INDENT4 "YTranslate: %0.3f\n", mYTranslate);
     dump.appendFormat(INDENT4 "XScale: %0.3f\n", mXScale);
     dump.appendFormat(INDENT4 "YScale: %0.3f\n", mYScale);
     dump.appendFormat(INDENT4 "XPrecision: %0.3f\n", mXPrecision);
@@ -2810,7 +2819,7 @@
         ALOG_ASSERT(false);
     }
 
-    dump.appendFormat(INDENT4 "AssociatedDisplay: present=%s, isExternal=%s\n",
+    dump.appendFormat(INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s\n",
             toString(mParameters.hasAssociatedDisplay),
             toString(mParameters.associatedDisplayIsExternal));
     dump.appendFormat(INDENT4 "OrientationAware: %s\n",
@@ -2869,10 +2878,15 @@
         return;
     }
 
+    // Raw width and height in the natural orientation.
+    int32_t rawWidth = mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1;
+    int32_t rawHeight = mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1;
+
     // Get associated display dimensions.
+    bool viewportChanged = false;
+    DisplayViewport newViewport;
     if (mParameters.hasAssociatedDisplay) {
-        if (!mConfig.getDisplayInfo(mParameters.associatedDisplayIsExternal,
-                &mAssociatedDisplayViewport)) {
+        if (!mConfig.getDisplayInfo(mParameters.associatedDisplayIsExternal, &newViewport)) {
             ALOGI(INDENT "Touch device '%s' could not query the properties of its associated "
                     "display.  The device will be inoperable until the display size "
                     "becomes available.",
@@ -2880,25 +2894,77 @@
             mDeviceMode = DEVICE_MODE_DISABLED;
             return;
         }
-    }
-
-    // Configure dimensions.
-    int32_t width, height, orientation;
-    if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) {
-        width = mAssociatedDisplayViewport.logicalRight - mAssociatedDisplayViewport.logicalLeft;
-        height = mAssociatedDisplayViewport.logicalBottom - mAssociatedDisplayViewport.logicalTop;
-        if (mAssociatedDisplayViewport.orientation == DISPLAY_ORIENTATION_90
-                || mAssociatedDisplayViewport.orientation == DISPLAY_ORIENTATION_270) {
-            int32_t temp = height;
-            height = width;
-            width = temp;
-        }
-        orientation = mParameters.orientationAware ?
-                mAssociatedDisplayViewport.orientation : DISPLAY_ORIENTATION_0;
     } else {
-        width = mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1;
-        height = mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1;
-        orientation = DISPLAY_ORIENTATION_0;
+        newViewport.setNonDisplayViewport(rawWidth, rawHeight);
+    }
+    if (mViewport != newViewport) {
+        mViewport = newViewport;
+        viewportChanged = true;
+
+        if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) {
+            // Convert rotated viewport to natural surface coordinates.
+            int32_t naturalLogicalWidth, naturalLogicalHeight;
+            int32_t naturalPhysicalWidth, naturalPhysicalHeight;
+            int32_t naturalPhysicalLeft, naturalPhysicalTop;
+            int32_t naturalDeviceWidth, naturalDeviceHeight;
+            switch (mViewport.orientation) {
+            case DISPLAY_ORIENTATION_90:
+                naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop;
+                naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
+                naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
+                naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
+                naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom;
+                naturalPhysicalTop = mViewport.physicalLeft;
+                naturalDeviceWidth = mViewport.deviceHeight;
+                naturalDeviceHeight = mViewport.deviceWidth;
+                break;
+            case DISPLAY_ORIENTATION_180:
+                naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft;
+                naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop;
+                naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
+                naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
+                naturalPhysicalLeft = mViewport.deviceWidth - mViewport.physicalRight;
+                naturalPhysicalTop = mViewport.deviceHeight - mViewport.physicalBottom;
+                naturalDeviceWidth = mViewport.deviceWidth;
+                naturalDeviceHeight = mViewport.deviceHeight;
+                break;
+            case DISPLAY_ORIENTATION_270:
+                naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop;
+                naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
+                naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
+                naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
+                naturalPhysicalLeft = mViewport.physicalTop;
+                naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight;
+                naturalDeviceWidth = mViewport.deviceHeight;
+                naturalDeviceHeight = mViewport.deviceWidth;
+                break;
+            case DISPLAY_ORIENTATION_0:
+            default:
+                naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft;
+                naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop;
+                naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
+                naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
+                naturalPhysicalLeft = mViewport.physicalLeft;
+                naturalPhysicalTop = mViewport.physicalTop;
+                naturalDeviceWidth = mViewport.deviceWidth;
+                naturalDeviceHeight = mViewport.deviceHeight;
+                break;
+            }
+
+            mSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth;
+            mSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight;
+            mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth;
+            mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight;
+
+            mSurfaceOrientation = mParameters.orientationAware ?
+                    mViewport.orientation : DISPLAY_ORIENTATION_0;
+        } else {
+            mSurfaceWidth = rawWidth;
+            mSurfaceHeight = rawHeight;
+            mSurfaceLeft = 0;
+            mSurfaceTop = 0;
+            mSurfaceOrientation = DISPLAY_ORIENTATION_0;
+        }
     }
 
     // If moving between pointer modes, need to reset some state.
@@ -2918,22 +2984,17 @@
         mPointerController.clear();
     }
 
-    bool orientationChanged = mSurfaceOrientation != orientation;
-    if (orientationChanged) {
-        mSurfaceOrientation = orientation;
-    }
-
-    bool sizeChanged = mSurfaceWidth != width || mSurfaceHeight != height;
-    if (sizeChanged || deviceModeChanged) {
-        ALOGI("Device reconfigured: id=%d, name='%s', surface size is now %dx%d, mode is %d",
-                getDeviceId(), getDeviceName().string(), width, height, mDeviceMode);
-
-        mSurfaceWidth = width;
-        mSurfaceHeight = height;
+    if (viewportChanged || deviceModeChanged) {
+        ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, "
+                "display id %d",
+                getDeviceId(), getDeviceName().string(), mSurfaceWidth, mSurfaceHeight,
+                mSurfaceOrientation, mDeviceMode, mViewport.displayId);
 
         // Configure X and Y factors.
-        mXScale = float(width) / (mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1);
-        mYScale = float(height) / (mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1);
+        mXScale = float(mSurfaceWidth) / rawWidth;
+        mYScale = float(mSurfaceHeight) / rawHeight;
+        mXTranslate = -mSurfaceLeft;
+        mYTranslate = -mSurfaceTop;
         mXPrecision = 1.0f / mXScale;
         mYPrecision = 1.0f / mYScale;
 
@@ -2950,7 +3011,7 @@
         mGeometricScale = avg(mXScale, mYScale);
 
         // Size of diagonal axis.
-        float diagonalSize = hypotf(width, height);
+        float diagonalSize = hypotf(mSurfaceWidth, mSurfaceHeight);
 
         // Size factors.
         if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) {
@@ -3102,50 +3163,38 @@
             mOrientedRanges.distance.fuzz =
                     mRawPointerAxes.distance.fuzz * mDistanceScale;
         }
-    }
 
-    if (orientationChanged || sizeChanged || deviceModeChanged) {
-        // Compute oriented surface dimensions, precision, scales and ranges.
+        // Compute oriented precision, scales and ranges.
         // Note that the maximum value reported is an inclusive maximum value so it is one
         // unit less than the total width or height of surface.
         switch (mSurfaceOrientation) {
         case DISPLAY_ORIENTATION_90:
         case DISPLAY_ORIENTATION_270:
-            mOrientedSurfaceWidth = mSurfaceHeight;
-            mOrientedSurfaceHeight = mSurfaceWidth;
-
             mOrientedXPrecision = mYPrecision;
             mOrientedYPrecision = mXPrecision;
 
-            mOrientedRanges.x.min = 0;
-            mOrientedRanges.x.max = (mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue)
-                    * mYScale;
+            mOrientedRanges.x.min = mYTranslate;
+            mOrientedRanges.x.max = mSurfaceHeight + mYTranslate - 1;
             mOrientedRanges.x.flat = 0;
             mOrientedRanges.x.fuzz = mYScale;
 
-            mOrientedRanges.y.min = 0;
-            mOrientedRanges.y.max = (mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue)
-                    * mXScale;
+            mOrientedRanges.y.min = mXTranslate;
+            mOrientedRanges.y.max = mSurfaceWidth + mXTranslate - 1;
             mOrientedRanges.y.flat = 0;
             mOrientedRanges.y.fuzz = mXScale;
             break;
 
         default:
-            mOrientedSurfaceWidth = mSurfaceWidth;
-            mOrientedSurfaceHeight = mSurfaceHeight;
-
             mOrientedXPrecision = mXPrecision;
             mOrientedYPrecision = mYPrecision;
 
-            mOrientedRanges.x.min = 0;
-            mOrientedRanges.x.max = (mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue)
-                    * mXScale;
+            mOrientedRanges.x.min = mXTranslate;
+            mOrientedRanges.x.max = mSurfaceWidth + mXTranslate - 1;
             mOrientedRanges.x.flat = 0;
             mOrientedRanges.x.fuzz = mXScale;
 
-            mOrientedRanges.y.min = 0;
-            mOrientedRanges.y.max = (mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue)
-                    * mYScale;
+            mOrientedRanges.y.min = mYTranslate;
+            mOrientedRanges.y.max = mSurfaceHeight + mYTranslate - 1;
             mOrientedRanges.y.flat = 0;
             mOrientedRanges.y.fuzz = mYScale;
             break;
@@ -3153,10 +3202,8 @@
 
         // Compute pointer gesture detection parameters.
         if (mDeviceMode == DEVICE_MODE_POINTER) {
-            int32_t rawWidth = mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1;
-            int32_t rawHeight = mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1;
             float rawDiagonal = hypotf(rawWidth, rawHeight);
-            float displayDiagonal = hypotf(width, height);
+            float displayDiagonal = hypotf(mSurfaceWidth, mSurfaceHeight);
 
             // Scale movements such that one whole swipe of the touch pad covers a
             // given area relative to the diagonal size of the display when no acceleration
@@ -3191,8 +3238,21 @@
 }
 
 void TouchInputMapper::dumpSurface(String8& dump) {
+    dump.appendFormat(INDENT3 "Viewport: displayId=%d, orientation=%d, "
+            "logicalFrame=[%d, %d, %d, %d], "
+            "physicalFrame=[%d, %d, %d, %d], "
+            "deviceSize=[%d, %d]\n",
+            mViewport.displayId, mViewport.orientation,
+            mViewport.logicalLeft, mViewport.logicalTop,
+            mViewport.logicalRight, mViewport.logicalBottom,
+            mViewport.physicalLeft, mViewport.physicalTop,
+            mViewport.physicalRight, mViewport.physicalBottom,
+            mViewport.deviceWidth, mViewport.deviceHeight);
+
     dump.appendFormat(INDENT3 "SurfaceWidth: %dpx\n", mSurfaceWidth);
     dump.appendFormat(INDENT3 "SurfaceHeight: %dpx\n", mSurfaceHeight);
+    dump.appendFormat(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft);
+    dump.appendFormat(INDENT3 "SurfaceTop: %d\n", mSurfaceTop);
     dump.appendFormat(INDENT3 "SurfaceOrientation: %d\n", mSurfaceOrientation);
 }
 
@@ -4087,28 +4147,28 @@
         float x, y;
         switch (mSurfaceOrientation) {
         case DISPLAY_ORIENTATION_90:
-            x = float(in.y - mRawPointerAxes.y.minValue) * mYScale;
-            y = float(mRawPointerAxes.x.maxValue - in.x) * mXScale;
+            x = float(in.y - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
+            y = float(mRawPointerAxes.x.maxValue - in.x) * mXScale + mXTranslate;
             orientation -= M_PI_2;
             if (orientation < - M_PI_2) {
                 orientation += M_PI;
             }
             break;
         case DISPLAY_ORIENTATION_180:
-            x = float(mRawPointerAxes.x.maxValue - in.x) * mXScale;
-            y = float(mRawPointerAxes.y.maxValue - in.y) * mYScale;
+            x = float(mRawPointerAxes.x.maxValue - in.x) * mXScale + mXTranslate;
+            y = float(mRawPointerAxes.y.maxValue - in.y) * mYScale + mYTranslate;
             break;
         case DISPLAY_ORIENTATION_270:
-            x = float(mRawPointerAxes.y.maxValue - in.y) * mYScale;
-            y = float(in.x - mRawPointerAxes.x.minValue) * mXScale;
+            x = float(mRawPointerAxes.y.maxValue - in.y) * mYScale + mYTranslate;
+            y = float(in.x - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
             orientation += M_PI_2;
             if (orientation > M_PI_2) {
                 orientation -= M_PI;
             }
             break;
         default:
-            x = float(in.x - mRawPointerAxes.x.minValue) * mXScale;
-            y = float(in.y - mRawPointerAxes.y.minValue) * mYScale;
+            x = float(in.x - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
+            y = float(in.y - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
             break;
         }
 
@@ -4364,7 +4424,8 @@
         NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
                 AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
                 metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                1, &pointerProperties, &pointerCoords, 0, 0, mPointerGesture.downTime);
+                mViewport.displayId, 1, &pointerProperties, &pointerCoords,
+                0, 0, mPointerGesture.downTime);
         getListener()->notifyMotion(&args);
     }
 
@@ -5272,6 +5333,7 @@
         // Send up.
         NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
                  AMOTION_EVENT_ACTION_UP, 0, metaState, mLastButtonState, 0,
+                 mViewport.displayId,
                  1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
                  mOrientedXPrecision, mOrientedYPrecision,
                  mPointerSimple.downTime);
@@ -5284,6 +5346,7 @@
         // Send hover exit.
         NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
                 AMOTION_EVENT_ACTION_HOVER_EXIT, 0, metaState, mLastButtonState, 0,
+                mViewport.displayId,
                 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
                 mOrientedXPrecision, mOrientedYPrecision,
                 mPointerSimple.downTime);
@@ -5298,6 +5361,7 @@
             // Send down.
             NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
                     AMOTION_EVENT_ACTION_DOWN, 0, metaState, mCurrentButtonState, 0,
+                    mViewport.displayId,
                     1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
                     mOrientedXPrecision, mOrientedYPrecision,
                     mPointerSimple.downTime);
@@ -5307,6 +5371,7 @@
         // Send move.
         NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
                 AMOTION_EVENT_ACTION_MOVE, 0, metaState, mCurrentButtonState, 0,
+                mViewport.displayId,
                 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
                 mOrientedXPrecision, mOrientedYPrecision,
                 mPointerSimple.downTime);
@@ -5320,6 +5385,7 @@
             // Send hover enter.
             NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
                     AMOTION_EVENT_ACTION_HOVER_ENTER, 0, metaState, mCurrentButtonState, 0,
+                    mViewport.displayId,
                     1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
                     mOrientedXPrecision, mOrientedYPrecision,
                     mPointerSimple.downTime);
@@ -5329,6 +5395,7 @@
         // Send hover move.
         NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
                 AMOTION_EVENT_ACTION_HOVER_MOVE, 0, metaState, mCurrentButtonState, 0,
+                mViewport.displayId,
                 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
                 mOrientedXPrecision, mOrientedYPrecision,
                 mPointerSimple.downTime);
@@ -5349,6 +5416,7 @@
 
         NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags,
                 AMOTION_EVENT_ACTION_SCROLL, 0, metaState, mCurrentButtonState, 0,
+                mViewport.displayId,
                 1, &mPointerSimple.currentProperties, &pointerCoords,
                 mOrientedXPrecision, mOrientedYPrecision,
                 mPointerSimple.downTime);
@@ -5410,7 +5478,8 @@
 
     NotifyMotionArgs args(when, getDeviceId(), source, policyFlags,
             action, flags, metaState, buttonState, edgeFlags,
-            pointerCount, pointerProperties, pointerCoords, xPrecision, yPrecision, downTime);
+            mViewport.displayId, pointerCount, pointerProperties, pointerCoords,
+            xPrecision, yPrecision, downTime);
     getListener()->notifyMotion(&args);
 }
 
@@ -6213,7 +6282,7 @@
         }
     }
 
-    // Moving a joystick axis should not wake the devide because joysticks can
+    // Moving a joystick axis should not wake the device because joysticks can
     // be fairly noisy even when not in use.  On the other hand, pushing a gamepad
     // button will likely wake the device.
     // TODO: Use the input device configuration to control this behavior more finely.
@@ -6221,7 +6290,7 @@
 
     NotifyMotionArgs args(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, policyFlags,
             AMOTION_EVENT_ACTION_MOVE, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-            1, &pointerProperties, &pointerCoords, 0, 0, 0);
+            ADISPLAY_ID_NONE, 1, &pointerProperties, &pointerCoords, 0, 0, 0);
     getListener()->notifyMotion(&args);
 }
 
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index e345a5fb..e1a8dd8 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -62,11 +62,14 @@
     int32_t physicalTop;
     int32_t physicalRight;
     int32_t physicalBottom;
+    int32_t deviceWidth;
+    int32_t deviceHeight;
 
     DisplayViewport() :
-            displayId(-1), orientation(DISPLAY_ORIENTATION_0),
+            displayId(ADISPLAY_ID_NONE), orientation(DISPLAY_ORIENTATION_0),
             logicalLeft(0), logicalTop(0), logicalRight(0), logicalBottom(0),
-            physicalLeft(0), physicalTop(0), physicalRight(0), physicalBottom(0) {
+            physicalLeft(0), physicalTop(0), physicalRight(0), physicalBottom(0),
+            deviceWidth(0), deviceHeight(0) {
     }
 
     bool operator==(const DisplayViewport& other) const {
@@ -79,12 +82,33 @@
                 && physicalLeft == other.physicalLeft
                 && physicalTop == other.physicalTop
                 && physicalRight == other.physicalRight
-                && physicalBottom == other.physicalBottom;
+                && physicalBottom == other.physicalBottom
+                && deviceWidth == other.deviceWidth
+                && deviceHeight == other.deviceHeight;
     }
 
     bool operator!=(const DisplayViewport& other) const {
         return !(*this == other);
     }
+
+    inline bool isValid() const {
+        return displayId >= 0;
+    }
+
+    void setNonDisplayViewport(int32_t width, int32_t height) {
+        displayId = ADISPLAY_ID_NONE;
+        orientation = DISPLAY_ORIENTATION_0;
+        logicalLeft = 0;
+        logicalTop = 0;
+        logicalRight = width;
+        logicalBottom = height;
+        physicalLeft = 0;
+        physicalTop = 0;
+        physicalRight = width;
+        physicalBottom = height;
+        deviceWidth = width;
+        deviceHeight = height;
+    }
 };
 
 /*
@@ -1297,18 +1321,30 @@
     virtual void syncTouch(nsecs_t when, bool* outHavePointerIds) = 0;
 
 private:
-    // The surface orientation and width and height set by configureSurface().
-    int32_t mSurfaceOrientation;
+    // The current viewport.
+    // The components of the viewport are specified in the display's rotated orientation.
+    DisplayViewport mViewport;
+
+    // The surface orientation, width and height set by configureSurface().
+    // The width and height are derived from the viewport but are specified
+    // in the natural orientation.
+    // The surface origin specifies how the surface coordinates should be translated
+    // to align with the logical display coordinate space.
+    // The orientation may be different from the viewport orientation as it specifies
+    // the rotation of the surface coordinates required to produce the viewport's
+    // requested orientation, so it will depend on whether the device is orientation aware.
     int32_t mSurfaceWidth;
     int32_t mSurfaceHeight;
-
-    // The associated display viewport set by configureSurface().
-    DisplayViewport mAssociatedDisplayViewport;
+    int32_t mSurfaceLeft;
+    int32_t mSurfaceTop;
+    int32_t mSurfaceOrientation;
 
     // Translation and scaling factors, orientation-independent.
+    float mXTranslate;
     float mXScale;
     float mXPrecision;
 
+    float mYTranslate;
     float mYScale;
     float mYPrecision;
 
@@ -1369,8 +1405,6 @@
     } mOrientedRanges;
 
     // Oriented dimensions and precision.
-    float mOrientedSurfaceWidth;
-    float mOrientedSurfaceHeight;
     float mOrientedXPrecision;
     float mOrientedYPrecision;
 
diff --git a/services/input/InputWindow.h b/services/input/InputWindow.h
index 824a64b..3118099 100644
--- a/services/input/InputWindow.h
+++ b/services/input/InputWindow.h
@@ -132,6 +132,7 @@
     int32_t ownerPid;
     int32_t ownerUid;
     int32_t inputFeatures;
+    int32_t displayId;
 
     bool touchableRegionContainsPoint(int32_t x, int32_t y) const;
     bool frameContainsPoint(int32_t x, int32_t y) const;
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index 03516af..c6dbbf3 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -151,6 +151,8 @@
         v.physicalTop = 0;
         v.physicalRight = isRotated ? height : width;
         v.physicalBottom = isRotated ? width : height;
+        v.deviceWidth = isRotated ? height : width;
+        v.deviceHeight = isRotated ? width : height;
         mConfig.setDisplayInfo(false /*external*/, v);
         mConfig.setDisplayInfo(true /*external*/, v);
     }
@@ -487,6 +489,7 @@
                 return OK;
             }
         }
+        outAxisInfo->clear();
         return -1;
     }
 
diff --git a/services/java/com/android/server/display/DisplayDevice.java b/services/java/com/android/server/display/DisplayDevice.java
index 8eeefb4..f5aa3d4 100644
--- a/services/java/com/android/server/display/DisplayDevice.java
+++ b/services/java/com/android/server/display/DisplayDevice.java
@@ -176,6 +176,12 @@
         } else {
             viewport.physicalFrame.setEmpty();
         }
+
+        boolean isRotated = (mCurrentOrientation == Surface.ROTATION_90
+                || mCurrentOrientation == Surface.ROTATION_270);
+        DisplayDeviceInfo info = getDisplayDeviceInfoLocked();
+        viewport.deviceWidth = isRotated ? info.height : info.width;
+        viewport.deviceHeight = isRotated ? info.width : info.height;
     }
 
     /**
diff --git a/services/java/com/android/server/display/DisplayViewport.java b/services/java/com/android/server/display/DisplayViewport.java
index ed4016d..5080556 100644
--- a/services/java/com/android/server/display/DisplayViewport.java
+++ b/services/java/com/android/server/display/DisplayViewport.java
@@ -44,12 +44,20 @@
     // should be scaled or translated after rotation.
     public final Rect physicalFrame = new Rect();
 
+    // The full width and height of the display device, rotated in the same
+    // manner as physicalFrame.  This expresses the full native size of the display device.
+    // The physical frame should usually fit within this area.
+    public int deviceWidth;
+    public int deviceHeight;
+
     public void copyFrom(DisplayViewport viewport) {
         valid = viewport.valid;
         displayId = viewport.displayId;
         orientation = viewport.orientation;
         logicalFrame.set(viewport.logicalFrame);
         physicalFrame.set(viewport.physicalFrame);
+        deviceWidth = viewport.deviceWidth;
+        deviceHeight = viewport.deviceHeight;
     }
 
     // For debugging purposes.
@@ -60,6 +68,8 @@
                 + ", orientation=" + orientation
                 + ", logicalFrame=" + logicalFrame
                 + ", physicalFrame=" + physicalFrame
+                + ", deviceWidth=" + deviceWidth
+                + ", deviceHeight=" + deviceHeight
                 + "}";
     }
 }
diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java
index 95655a5..805818a 100644
--- a/services/java/com/android/server/input/InputManagerService.java
+++ b/services/java/com/android/server/input/InputManagerService.java
@@ -149,7 +149,8 @@
     private static native void nativeSetDisplayViewport(int ptr, boolean external,
             int displayId, int rotation,
             int logicalLeft, int logicalTop, int logicalRight, int logicalBottom,
-            int physicalLeft, int physicalTop, int physicalRight, int physicalBottom);
+            int physicalLeft, int physicalTop, int physicalRight, int physicalBottom,
+            int deviceWidth, int deviceHeight);
 
     private static native int nativeGetScanCodeState(int ptr,
             int deviceId, int sourceMask, int scanCode);
@@ -305,7 +306,8 @@
                 viewport.logicalFrame.left, viewport.logicalFrame.top,
                 viewport.logicalFrame.right, viewport.logicalFrame.bottom,
                 viewport.physicalFrame.left, viewport.physicalFrame.top,
-                viewport.physicalFrame.right, viewport.physicalFrame.bottom);
+                viewport.physicalFrame.right, viewport.physicalFrame.bottom,
+                viewport.deviceWidth, viewport.deviceHeight);
     }
 
     /**
diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java
index 5ff8a9b..aa18ee4 100644
--- a/services/java/com/android/server/wm/InputMonitor.java
+++ b/services/java/com/android/server/wm/InputMonitor.java
@@ -19,11 +19,13 @@
 import com.android.server.input.InputManagerService;
 import com.android.server.input.InputApplicationHandle;
 import com.android.server.input.InputWindowHandle;
+import com.android.server.wm.WindowManagerService.AllWindowsIterator;
 
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.Slog;
+import android.view.Display;
 import android.view.InputChannel;
 import android.view.KeyEvent;
 import android.view.WindowManager;
@@ -225,10 +227,11 @@
             addInputWindowHandleLw(mService.mFakeWindows.get(i).mWindowHandle);
         }
 
-        // TODO(multidisplay): Input only occurs on the default display.
-        final WindowList windows = mService.getDefaultWindowList();
-        for (int i = windows.size() - 1; i >= 0; i--) {
-            final WindowState child = windows.get(i);
+        // Add all windows on the default display.
+        final AllWindowsIterator iterator = mService.new AllWindowsIterator(
+                WindowManagerService.REVERSE_ITERATOR);
+        while (iterator.hasNext()) {
+            final WindowState child = iterator.next();
             final InputChannel inputChannel = child.mInputChannel;
             final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
             if (inputChannel == null || inputWindowHandle == null || child.mRemoved) {
@@ -243,15 +246,16 @@
             final boolean isVisible = child.isVisibleLw();
             final boolean hasWallpaper = (child == mService.mWallpaperTarget)
                     && (type != WindowManager.LayoutParams.TYPE_KEYGUARD);
+            final boolean onDefaultDisplay = (child.getDisplayId() == Display.DEFAULT_DISPLAY);
 
             // If there's a drag in progress and 'child' is a potential drop target,
             // make sure it's been told about the drag
-            if (inDrag && isVisible) {
+            if (inDrag && isVisible && onDefaultDisplay) {
                 mService.mDragState.sendDragStartedIfNeededLw(child);
             }
 
             if (universeBackground != null && !addedUniverse
-                    && child.mBaseLayer < aboveUniverseLayer) {
+                    && child.mBaseLayer < aboveUniverseLayer && onDefaultDisplay) {
                 final WindowState u = universeBackground.mWin;
                 if (u.mInputChannel != null && u.mInputWindowHandle != null) {
                     addInputWindowHandleLw(u.mInputWindowHandle, u, u.mAttrs.flags,
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index cdca8bc..a7ea01d 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -10744,7 +10744,7 @@
         }
     }
 
-    boolean REVERSE_ITERATOR = true;
+    final static boolean REVERSE_ITERATOR = true;
     class AllWindowsIterator implements Iterator<WindowState> {
         private DisplayContent mDisplayContent;
         private DisplayContentsIterator mDisplayContentsIterator;
diff --git a/services/jni/com_android_server_input_InputManagerService.cpp b/services/jni/com_android_server_input_InputManagerService.cpp
index 5e36bf8..319cacd 100644
--- a/services/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/jni/com_android_server_input_InputManagerService.cpp
@@ -993,7 +993,8 @@
 static void nativeSetDisplayViewport(JNIEnv* env, jclass clazz, jint ptr, jboolean external,
         jint displayId, jint orientation,
         jint logicalLeft, jint logicalTop, jint logicalRight, jint logicalBottom,
-        jint physicalLeft, jint physicalTop, jint physicalRight, jint physicalBottom) {
+        jint physicalLeft, jint physicalTop, jint physicalRight, jint physicalBottom,
+        jint deviceWidth, jint deviceHeight) {
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
 
     DisplayViewport v;
@@ -1007,6 +1008,8 @@
     v.physicalTop = physicalTop;
     v.physicalRight = physicalRight;
     v.physicalBottom = physicalBottom;
+    v.deviceWidth = deviceWidth;
+    v.deviceHeight = deviceHeight;
     im->setDisplayViewport(external, v);
 }
 
@@ -1288,7 +1291,7 @@
             (void*) nativeInit },
     { "nativeStart", "(I)V",
             (void*) nativeStart },
-    { "nativeSetDisplayViewport", "(IZIIIIIIIIII)V",
+    { "nativeSetDisplayViewport", "(IZIIIIIIIIIIII)V",
             (void*) nativeSetDisplayViewport },
     { "nativeGetScanCodeState", "(IIII)I",
             (void*) nativeGetScanCodeState },
diff --git a/services/jni/com_android_server_input_InputWindowHandle.cpp b/services/jni/com_android_server_input_InputWindowHandle.cpp
index 01fb781..6692994 100644
--- a/services/jni/com_android_server_input_InputWindowHandle.cpp
+++ b/services/jni/com_android_server_input_InputWindowHandle.cpp
@@ -52,6 +52,7 @@
     jfieldID ownerPid;
     jfieldID ownerUid;
     jfieldID inputFeatures;
+    jfieldID displayId;
 } gInputWindowHandleClassInfo;
 
 static Mutex gHandleMutex;
@@ -151,6 +152,8 @@
             gInputWindowHandleClassInfo.ownerUid);
     mInfo->inputFeatures = env->GetIntField(obj,
             gInputWindowHandleClassInfo.inputFeatures);
+    mInfo->displayId = env->GetIntField(obj,
+            gInputWindowHandleClassInfo.displayId);
 
     env->DeleteLocalRef(obj);
     return true;
@@ -291,6 +294,9 @@
 
     GET_FIELD_ID(gInputWindowHandleClassInfo.inputFeatures, clazz,
             "inputFeatures", "I");
+
+    GET_FIELD_ID(gInputWindowHandleClassInfo.displayId, clazz,
+            "displayId", "I");
     return 0;
 }