Make display manager tell input system about viewports.

The window manager is no longer responsible for telling the
input system about the display viewport.

Change-Id: I932882bae55decef55f25093bb2a7ebac1620bb1
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 1aad9b3..c28afb2 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -304,6 +304,7 @@
 
             ActivityManagerService.self().setWindowManager(wm);
             display.setWindowManager(wm);
+            display.setInputManager(inputManager);
 
             // Skip Bluetooth if we have an emulator kernel
             // TODO: Use a more reliable check to see if this product should
diff --git a/services/java/com/android/server/display/DisplayDevice.java b/services/java/com/android/server/display/DisplayDevice.java
index 995c553..8eeefb4 100644
--- a/services/java/com/android/server/display/DisplayDevice.java
+++ b/services/java/com/android/server/display/DisplayDevice.java
@@ -132,14 +132,17 @@
                 || mCurrentDisplayRect == null
                 || !mCurrentDisplayRect.equals(displayRect)) {
             mCurrentOrientation = orientation;
+
             if (mCurrentLayerStackRect == null) {
                 mCurrentLayerStackRect = new Rect();
             }
             mCurrentLayerStackRect.set(layerStackRect);
+
             if (mCurrentDisplayRect == null) {
                 mCurrentDisplayRect = new Rect();
             }
             mCurrentDisplayRect.set(displayRect);
+
             Surface.setDisplayProjection(mDisplayToken,
                     orientation, layerStackRect, displayRect);
         }
@@ -156,6 +159,26 @@
     }
 
     /**
+     * Populates the specified viewport object with orientation,
+     * physical and logical rects based on the display's current projection.
+     */
+    public final void populateViewportLocked(DisplayViewport viewport) {
+        viewport.orientation = mCurrentOrientation;
+
+        if (mCurrentLayerStackRect != null) {
+            viewport.logicalFrame.set(mCurrentLayerStackRect);
+        } else {
+            viewport.logicalFrame.setEmpty();
+        }
+
+        if (mCurrentDisplayRect != null) {
+            viewport.physicalFrame.set(mCurrentDisplayRect);
+        } else {
+            viewport.physicalFrame.setEmpty();
+        }
+    }
+
+    /**
      * Dumps the local state of the display device.
      * Does not need to dump the display device info because that is already dumped elsewhere.
      */
diff --git a/services/java/com/android/server/display/DisplayDeviceInfo.java b/services/java/com/android/server/display/DisplayDeviceInfo.java
index c90a1c6..7c57694 100644
--- a/services/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/java/com/android/server/display/DisplayDeviceInfo.java
@@ -43,6 +43,21 @@
     public static final int FLAG_SUPPORTS_ROTATION = 1 << 2;
 
     /**
+     * Touch attachment: Display does not receive touch.
+     */
+    public static final int TOUCH_NONE = 0;
+
+    /**
+     * Touch attachment: Touch input is via the internal interface.
+     */
+    public static final int TOUCH_INTERNAL = 1;
+
+    /**
+     * Touch attachment: Touch input is via an external interface, such as USB.
+     */
+    public static final int TOUCH_EXTERNAL = 2;
+
+    /**
      * Gets the name of the display device, which may be derived from
      * EDID or other sources.  The name may be displayed to the user.
      */
@@ -90,6 +105,11 @@
      */
     public int flags;
 
+    /**
+     * The touch attachment, per {@link DisplayViewport#touch}.
+     */
+    public int touch;
+
     public void setAssumedDensityForExternalDisplay(int width, int height) {
         densityDpi = Math.min(width, height) * DisplayMetrics.DENSITY_XHIGH / 1080;
         // Technically, these values should be smaller than the apparent density
@@ -112,7 +132,8 @@
                 && densityDpi == other.densityDpi
                 && xDpi == other.xDpi
                 && yDpi == other.yDpi
-                && flags == other.flags;
+                && flags == other.flags
+                && touch == other.touch;
     }
 
     @Override
@@ -129,6 +150,7 @@
         xDpi = other.xDpi;
         yDpi = other.yDpi;
         flags = other.flags;
+        touch = other.touch;
     }
 
     // For debugging purposes
@@ -136,7 +158,20 @@
     public String toString() {
         return "DisplayDeviceInfo{\"" + name + "\": " + width + " x " + height + ", " + refreshRate + " fps, "
                 + "density " + densityDpi + ", " + xDpi + " x " + yDpi + " dpi"
-                + flagsToString(flags) + "}";
+                + ", touch " + touchToString(touch) + flagsToString(flags) + "}";
+    }
+
+    private static String touchToString(int touch) {
+        switch (touch) {
+            case TOUCH_NONE:
+                return "NONE";
+            case TOUCH_INTERNAL:
+                return "INTERNAL";
+            case TOUCH_EXTERNAL:
+                return "EXTERNAL";
+            default:
+                return Integer.toString(touch);
+        }
     }
 
     private static String flagsToString(int flags) {
diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java
index 41a0c09..e11d454 100644
--- a/services/java/com/android/server/display/DisplayManagerService.java
+++ b/services/java/com/android/server/display/DisplayManagerService.java
@@ -96,6 +96,7 @@
     private static final int MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS = 2;
     private static final int MSG_DELIVER_DISPLAY_EVENT = 3;
     private static final int MSG_REQUEST_TRAVERSAL = 4;
+    private static final int MSG_UPDATE_VIEWPORT = 5;
 
     private final Context mContext;
     private final boolean mHeadless;
@@ -103,6 +104,7 @@
     private final Handler mUiHandler;
     private final DisplayAdapterListener mDisplayAdapterListener;
     private WindowManagerFuncs mWindowManagerFuncs;
+    private InputManagerFuncs mInputManagerFuncs;
 
     // The synchronization root for the display manager.
     // This lock guards most of the display manager's state.
@@ -141,6 +143,11 @@
     // The Wifi display adapter, or null if not registered.
     private WifiDisplayAdapter mWifiDisplayAdapter;
 
+    // Viewports of the default display and the display that should receive touch
+    // input from an external source.  Used by the input system.
+    private final DisplayViewport mDefaultViewport = new DisplayViewport();
+    private final DisplayViewport mExternalTouchViewport = new DisplayViewport();
+
     // Temporary callback list, used when sending display events to applications.
     // May be used outside of the lock but only on the handler thread.
     private final ArrayList<CallbackRecord> mTempCallbacks = new ArrayList<CallbackRecord>();
@@ -148,6 +155,11 @@
     // Temporary display info, used for comparing display configurations.
     private final DisplayInfo mTempDisplayInfo = new DisplayInfo();
 
+    // Temporary viewports, used when sending new viewport information to the
+    // input system.  May be used outside of the lock but only on the handler thread.
+    private final DisplayViewport mTempDefaultViewport = new DisplayViewport();
+    private final DisplayViewport mTempExternalTouchViewport = new DisplayViewport();
+
     public DisplayManagerService(Context context, Handler mainHandler, Handler uiHandler) {
         mContext = context;
         mHeadless = SystemProperties.get(SYSTEM_HEADLESS).equals("1");
@@ -183,7 +195,7 @@
     }
 
     /**
-     * Called during initialization to associated the display manager with the
+     * Called during initialization to associate the display manager with the
      * window manager.
      */
     public void setWindowManager(WindowManagerFuncs windowManagerFuncs) {
@@ -194,6 +206,17 @@
     }
 
     /**
+     * Called during initialization to associate the display manager with the
+     * input manager.
+     */
+    public void setInputManager(InputManagerFuncs inputManagerFuncs) {
+        synchronized (mSyncRoot) {
+            mInputManagerFuncs = inputManagerFuncs;
+            scheduleTraversalLocked();
+        }
+    }
+
+    /**
      * Called when the system is ready to go.
      */
     public void systemReady(boolean safeMode, boolean onlyCore) {
@@ -487,7 +510,7 @@
         final int displayId = assignDisplayIdLocked(isDefault);
         final int layerStack = assignLayerStackLocked(displayId);
 
-        LogicalDisplay display = new LogicalDisplay(layerStack, device);
+        LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
         display.updateLocked(mDisplayDevices);
         if (!display.isValidLocked()) {
             // This should never happen currently.
@@ -548,6 +571,10 @@
         }
         mRemovedDisplayDevices.clear();
 
+        // Clear all viewports before configuring displays so that we can keep
+        // track of which ones we have configured.
+        clearViewportsLocked();
+
         // Configure each display device.
         final int count = mDisplayDevices.size();
         for (int i = 0; i < count; i++) {
@@ -555,6 +582,11 @@
             configureDisplayInTransactionLocked(device);
             device.performTraversalInTransactionLocked();
         }
+
+        // Tell the input system about these new viewports.
+        if (mInputManagerFuncs != null) {
+            mHandler.sendEmptyMessage(MSG_UPDATE_VIEWPORT);
+        }
     }
 
     /**
@@ -577,7 +609,11 @@
                 scheduleTraversalLocked();
             }
         }
+    }
 
+    private void clearViewportsLocked() {
+        mDefaultViewport.valid = false;
+        mExternalTouchViewport.valid = false;
     }
 
     private void configureDisplayInTransactionLocked(DisplayDevice device) {
@@ -593,11 +629,30 @@
         // Apply the logical display configuration to the display device.
         if (display == null) {
             // TODO: no logical display for the device, blank it
-            Slog.d(TAG, "Missing logical display to use for physical display device: "
+            Slog.w(TAG, "Missing logical display to use for physical display device: "
                     + device.getDisplayDeviceInfoLocked());
+            return;
         } else {
             display.configureDisplayInTransactionLocked(device);
         }
+
+        // Update the viewports if needed.
+        DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+        if (!mDefaultViewport.valid
+                && (info.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) {
+            setViewportLocked(mDefaultViewport, display, device);
+        }
+        if (!mExternalTouchViewport.valid
+                && info.touch == DisplayDeviceInfo.TOUCH_EXTERNAL) {
+            setViewportLocked(mExternalTouchViewport, display, device);
+        }
+    }
+
+    private static void setViewportLocked(DisplayViewport viewport,
+            LogicalDisplay display, DisplayDevice device) {
+        viewport.valid = true;
+        viewport.displayId = display.getDisplayIdLocked();
+        device.populateViewportLocked(viewport);
     }
 
     private LogicalDisplay findLogicalDisplayForDeviceLocked(DisplayDevice device) {
@@ -690,6 +745,10 @@
                 pw.println("  Display " + displayId + ":");
                 display.dumpLocked(ipw);
             }
+
+            pw.println();
+            pw.println("Default viewport: " + mDefaultViewport);
+            pw.println("External touch viewport: " + mExternalTouchViewport);
         }
     }
 
@@ -714,6 +773,18 @@
         void requestTraversal();
     }
 
+    /**
+     * Private interface to the input manager.
+     */
+    public interface InputManagerFuncs {
+        /**
+         * Sets information about the displays as needed by the input system.
+         * The input system should copy this information if required.
+         */
+        void setDisplayViewports(DisplayViewport defaultViewport,
+                DisplayViewport externalTouchViewport);
+    }
+
     private final class DisplayManagerHandler extends Handler {
         public DisplayManagerHandler(Looper looper) {
             super(looper, null, true /*async*/);
@@ -737,6 +808,16 @@
                 case MSG_REQUEST_TRAVERSAL:
                     mWindowManagerFuncs.requestTraversal();
                     break;
+
+                case MSG_UPDATE_VIEWPORT: {
+                    synchronized (mSyncRoot) {
+                        mTempDefaultViewport.copyFrom(mDefaultViewport);
+                        mTempExternalTouchViewport.copyFrom(mExternalTouchViewport);
+                    }
+                    mInputManagerFuncs.setDisplayViewports(
+                            mTempDefaultViewport, mTempExternalTouchViewport);
+                    break;
+                }
             }
         }
     }
diff --git a/services/java/com/android/server/display/DisplayViewport.java b/services/java/com/android/server/display/DisplayViewport.java
new file mode 100644
index 0000000..ed4016d
--- /dev/null
+++ b/services/java/com/android/server/display/DisplayViewport.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2012 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 com.android.server.display;
+
+import android.graphics.Rect;
+
+/**
+ * Describes how the pixels of physical display device reflects the content of
+ * a logical display.
+ * <p>
+ * This information is used by the input system to translate touch input from
+ * physical display coordinates into logical display coordinates.
+ * </p>
+ */
+public final class DisplayViewport {
+    // True if this viewport is valid.
+    public boolean valid;
+
+    // The logical display id.
+    public int displayId;
+
+    // The rotation applied to the physical coordinate system.
+    public int orientation;
+
+    // The portion of the logical display that are presented on this physical display.
+    public final Rect logicalFrame = new Rect();
+
+    // The portion of the (rotated) physical display that shows the logical display contents.
+    // The relation between logical and physical frame defines how the coordinate system
+    // should be scaled or translated after rotation.
+    public final Rect physicalFrame = new Rect();
+
+    public void copyFrom(DisplayViewport viewport) {
+        valid = viewport.valid;
+        displayId = viewport.displayId;
+        orientation = viewport.orientation;
+        logicalFrame.set(viewport.logicalFrame);
+        physicalFrame.set(viewport.physicalFrame);
+    }
+
+    // For debugging purposes.
+    @Override
+    public String toString() {
+        return "DisplayViewport{valid=" + valid
+                + ", displayId=" + displayId
+                + ", orientation=" + orientation
+                + ", logicalFrame=" + logicalFrame
+                + ", physicalFrame=" + physicalFrame
+                + "}";
+    }
+}
diff --git a/services/java/com/android/server/display/HeadlessDisplayAdapter.java b/services/java/com/android/server/display/HeadlessDisplayAdapter.java
index 3aaacf1..7629db6 100644
--- a/services/java/com/android/server/display/HeadlessDisplayAdapter.java
+++ b/services/java/com/android/server/display/HeadlessDisplayAdapter.java
@@ -61,6 +61,7 @@
                 mInfo.yDpi = 160;
                 mInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
                         | DisplayDeviceInfo.FLAG_SECURE;
+                mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
             }
             return mInfo;
         }
diff --git a/services/java/com/android/server/display/LocalDisplayAdapter.java b/services/java/com/android/server/display/LocalDisplayAdapter.java
index 4962753..80c860b 100644
--- a/services/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/java/com/android/server/display/LocalDisplayAdapter.java
@@ -129,10 +129,12 @@
                     mInfo.densityDpi = (int)(mPhys.density * 160 + 0.5f);
                     mInfo.xDpi = mPhys.xDpi;
                     mInfo.yDpi = mPhys.yDpi;
+                    mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
                 } else {
                     mInfo.name = getContext().getResources().getString(
                             com.android.internal.R.string.display_manager_hdmi_display_name);
                     mInfo.flags = DisplayDeviceInfo.FLAG_SECURE;
+                    mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
                     mInfo.setAssumedDensityForExternalDisplay(mPhys.width, mPhys.height);
                 }
             }
diff --git a/services/java/com/android/server/display/LogicalDisplay.java b/services/java/com/android/server/display/LogicalDisplay.java
index e0f63dd..2e75260 100644
--- a/services/java/com/android/server/display/LogicalDisplay.java
+++ b/services/java/com/android/server/display/LogicalDisplay.java
@@ -54,6 +54,7 @@
 final class LogicalDisplay {
     private final DisplayInfo mBaseDisplayInfo = new DisplayInfo();
 
+    private final int mDisplayId;
     private final int mLayerStack;
     private DisplayInfo mOverrideDisplayInfo; // set by the window manager
     private DisplayInfo mInfo;
@@ -70,12 +71,22 @@
     private final Rect mTempLayerStackRect = new Rect();
     private final Rect mTempDisplayRect = new Rect();
 
-    public LogicalDisplay(int layerStack, DisplayDevice primaryDisplayDevice) {
+    public LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice) {
+        mDisplayId = displayId;
         mLayerStack = layerStack;
         mPrimaryDisplayDevice = primaryDisplayDevice;
     }
 
     /**
+     * Gets the logical display id of this logical display.
+     *
+     * @return The logical display id.
+     */
+    public int getDisplayIdLocked() {
+        return mDisplayId;
+    }
+
+    /**
      * Gets the primary display device associated with this logical display.
      *
      * @return The primary display device.
diff --git a/services/java/com/android/server/display/OverlayDisplayAdapter.java b/services/java/com/android/server/display/OverlayDisplayAdapter.java
index e2d3059..9b0e534 100644
--- a/services/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -228,6 +228,7 @@
                 mInfo.xDpi = mDensityDpi;
                 mInfo.yDpi = mDensityDpi;
                 mInfo.flags = DisplayDeviceInfo.FLAG_SECURE;
+                mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
             }
             return mInfo;
         }
diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java
index abf0d27..ca500c0 100644
--- a/services/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/java/com/android/server/display/WifiDisplayAdapter.java
@@ -324,6 +324,7 @@
                 mInfo.height = mHeight;
                 mInfo.refreshRate = mRefreshRate;
                 mInfo.flags = mFlags;
+                mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
                 mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight);
             }
             return mInfo;
diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java
index fd4f5fc..95655a5 100644
--- a/services/java/com/android/server/input/InputManagerService.java
+++ b/services/java/com/android/server/input/InputManagerService.java
@@ -19,6 +19,8 @@
 import com.android.internal.R;
 import com.android.internal.util.XmlUtils;
 import com.android.server.Watchdog;
+import com.android.server.display.DisplayManagerService;
+import com.android.server.display.DisplayViewport;
 
 import org.xmlpull.v1.XmlPullParser;
 
@@ -90,7 +92,8 @@
 /*
  * Wraps the C++ InputManager and provides its callbacks.
  */
-public class InputManagerService extends IInputManager.Stub implements Watchdog.Monitor {
+public class InputManagerService extends IInputManager.Stub
+        implements Watchdog.Monitor, DisplayManagerService.InputManagerFuncs {
     static final String TAG = "InputManager";
     static final boolean DEBUG = false;
 
@@ -143,11 +146,11 @@
     private static native int nativeInit(InputManagerService service,
             Context context, MessageQueue messageQueue);
     private static native void nativeStart(int ptr);
-    private static native void nativeSetDisplaySize(int ptr, int displayId,
-            int width, int height, int externalWidth, int externalHeight);
-    private static native void nativeSetDisplayOrientation(int ptr, int displayId,
-            int rotation, int externalRotation);
-    
+    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);
+
     private static native int nativeGetScanCodeState(int ptr,
             int deviceId, int sourceMask, int scanCode);
     private static native int nativeGetKeyCodeState(int ptr,
@@ -282,28 +285,27 @@
         nativeReloadDeviceAliases(mPtr);
     }
 
-    public void setDisplaySize(int displayId, int width, int height) {
-        if (width <= 0 || height <= 0) {
-            throw new IllegalArgumentException("Invalid display id or dimensions.");
+    @Override
+    public void setDisplayViewports(DisplayViewport defaultViewport,
+            DisplayViewport externalTouchViewport) {
+        if (defaultViewport.valid) {
+            setDisplayViewport(false, defaultViewport);
         }
-        
-        if (DEBUG) {
-            Slog.d(TAG, "Setting display #" + displayId + " size to " + width + "x" + height);
+
+        if (externalTouchViewport.valid) {
+            setDisplayViewport(true, externalTouchViewport);
+        } else if (defaultViewport.valid) {
+            setDisplayViewport(true, defaultViewport);
         }
-        // FIXME: external size is deprecated
-        nativeSetDisplaySize(mPtr, displayId, width, height, 1280, 720);
     }
 
-    public void setDisplayOrientation(int displayId, int rotation) {
-        if (rotation < Surface.ROTATION_0 || rotation > Surface.ROTATION_270) {
-            throw new IllegalArgumentException("Invalid rotation.");
-        }
-        
-        if (DEBUG) {
-            Slog.d(TAG, "Setting display #" + displayId + " orientation to rotation " + rotation);
-        }
-        // FIXME: external rotation is deprecated
-        nativeSetDisplayOrientation(mPtr, displayId, rotation, Surface.ROTATION_0);
+    private void setDisplayViewport(boolean external, DisplayViewport viewport) {
+        nativeSetDisplayViewport(mPtr, external,
+                viewport.displayId, viewport.orientation,
+                viewport.logicalFrame.left, viewport.logicalFrame.top,
+                viewport.logicalFrame.right, viewport.logicalFrame.bottom,
+                viewport.physicalFrame.left, viewport.physicalFrame.top,
+                viewport.physicalFrame.right, viewport.physicalFrame.bottom);
     }
 
     /**
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 18e793d..cdca8bc 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -5947,7 +5947,6 @@
         mWaitingForConfig = true;
         getDefaultDisplayContent().layoutNeeded = true;
         startFreezingDisplayLocked(inTransaction);
-        mInputManager.setDisplayOrientation(0, rotation);
 
         // We need to update our screen size information to match the new
         // rotation.  Note that this is redundant with the later call to
@@ -7121,10 +7120,6 @@
                 displayInfo.appWidth, displayInfo.appHeight);
 
             final DisplayContent displayContent = getDefaultDisplayContent();
-            mInputManager.setDisplaySize(displayContent.getDisplayId(),
-                    displayContent.mInitialDisplayWidth, displayContent.mInitialDisplayHeight);
-            mInputManager.setDisplayOrientation(displayContent.getDisplayId(),
-                    mDefaultDisplay.getRotation());
             mPolicy.setInitialDisplaySize(mDefaultDisplay, displayContent.mInitialDisplayWidth,
                     displayContent.mInitialDisplayHeight, displayContent.mInitialDisplayDensity);
         }