Add uniqueId to Virtual Display and pass through to inputflinger (2/2)

This CL adds:
1) Adds uniqueId (protected via system/sig permission) to virtual
displays.
2) Add support for N virtual display viewports into inputflinger.
3) Set the virtual display's viewports in inputflinger if it has the
uniqueId value set to non-null. (a) Moving the new viewport from java to
native inputflinger and (b) adding "uniqueId" value to viewports makes
up the great majority of this change.
4) From the inputflinger side, we also read in a new value from the
input device configuration files called 'touch.displayId'.
5) When touch.displayId and the virtual display's uniqueId match,
inputflinger links the two.

Test: Start VR and ensure that the virtual viewport shows up when running
'adb shell dump input".  Run a VR app, and ensure that the virtual input
device is associated with the new virtual viewport.

Bug: 36051620
Change-Id: I662f09f863a3dc94c570e7e7f30b83888b3f514c
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
new file mode 100644
index 0000000..86da4d3
--- /dev/null
+++ b/include/input/DisplayViewport.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef _LIBINPUT_DISPLAY_VIEWPORT_H
+#define _LIBINPUT_DISPLAY_VIEWPORT_H
+
+#include <ui/DisplayInfo.h>
+#include <input/Input.h>
+
+namespace android {
+
+/*
+ * Describes how coordinates are mapped on a physical display.
+ * See com.android.server.display.DisplayViewport.
+ */
+struct DisplayViewport {
+    int32_t displayId; // -1 if invalid
+    int32_t orientation;
+    int32_t logicalLeft;
+    int32_t logicalTop;
+    int32_t logicalRight;
+    int32_t logicalBottom;
+    int32_t physicalLeft;
+    int32_t physicalTop;
+    int32_t physicalRight;
+    int32_t physicalBottom;
+    int32_t deviceWidth;
+    int32_t deviceHeight;
+    String8 uniqueId;
+
+    DisplayViewport() :
+            displayId(ADISPLAY_ID_NONE), orientation(DISPLAY_ORIENTATION_0),
+            logicalLeft(0), logicalTop(0), logicalRight(0), logicalBottom(0),
+            physicalLeft(0), physicalTop(0), physicalRight(0), physicalBottom(0),
+            deviceWidth(0), deviceHeight(0) {
+    }
+
+    bool operator==(const DisplayViewport& other) const {
+        return displayId == other.displayId
+                && orientation == other.orientation
+                && logicalLeft == other.logicalLeft
+                && logicalTop == other.logicalTop
+                && logicalRight == other.logicalRight
+                && logicalBottom == other.logicalBottom
+                && physicalLeft == other.physicalLeft
+                && physicalTop == other.physicalTop
+                && physicalRight == other.physicalRight
+                && physicalBottom == other.physicalBottom
+                && deviceWidth == other.deviceWidth
+                && deviceHeight == other.deviceHeight
+                && uniqueId == other.uniqueId;
+    }
+
+    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;
+        uniqueId.clear();
+    }
+};
+
+/**
+ * Describes the different type of viewports supported by input flinger.
+ * Keep in sync with values in InputManagerService.java.
+ */
+enum class ViewportType : int32_t {
+    VIEWPORT_INTERNAL = 1,
+    VIEWPORT_EXTERNAL = 2,
+    VIEWPORT_VIRTUAL = 3,
+};
+
+} // namespace android
+
+#endif // _LIBINPUT_DISPLAY_VIEWPORT_H
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index 2705e13..73255dd 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -227,18 +227,65 @@
 
 // --- InputReaderConfiguration ---
 
-bool InputReaderConfiguration::getDisplayInfo(bool external, DisplayViewport* outViewport) const {
-    const DisplayViewport& viewport = external ? mExternalDisplay : mInternalDisplay;
-    if (viewport.displayId >= 0) {
-        *outViewport = viewport;
+bool InputReaderConfiguration::getDisplayViewport(ViewportType viewportType,
+        const String8* uniqueDisplayId, DisplayViewport* outViewport) const {
+    const DisplayViewport* viewport = NULL;
+    if (viewportType == ViewportType::VIEWPORT_VIRTUAL && uniqueDisplayId != NULL) {
+        for (DisplayViewport currentViewport : mVirtualDisplays) {
+            if (currentViewport.uniqueId == *uniqueDisplayId) {
+                viewport = &currentViewport;
+                break;
+            }
+        }
+    } else if (viewportType == ViewportType::VIEWPORT_EXTERNAL) {
+        viewport = &mExternalDisplay;
+    } else if (viewportType == ViewportType::VIEWPORT_INTERNAL) {
+        viewport = &mInternalDisplay;
+    }
+
+    if (viewport != NULL && viewport->displayId >= 0) {
+        *outViewport = *viewport;
         return true;
     }
     return false;
 }
 
-void InputReaderConfiguration::setDisplayInfo(bool external, const DisplayViewport& viewport) {
-    DisplayViewport& v = external ? mExternalDisplay : mInternalDisplay;
-    v = viewport;
+void InputReaderConfiguration::setPhysicalDisplayViewport(ViewportType viewportType,
+        const DisplayViewport& viewport) {
+    if (viewportType == ViewportType::VIEWPORT_EXTERNAL) {
+        mExternalDisplay = viewport;
+    } else if (viewportType == ViewportType::VIEWPORT_INTERNAL) {
+        mInternalDisplay = viewport;
+    }
+}
+
+void InputReaderConfiguration::setVirtualDisplayViewports(
+        const Vector<DisplayViewport>& viewports) {
+    mVirtualDisplays = viewports;
+}
+
+void InputReaderConfiguration::dump(String8& dump) const {
+    dump.append(INDENT4 "ViewportInternal:\n");
+    dumpViewport(dump, mInternalDisplay);
+    dump.append(INDENT4 "ViewportExternal:\n");
+    dumpViewport(dump, mExternalDisplay);
+    dump.append(INDENT4 "ViewportVirtual:\n");
+    for (const DisplayViewport& viewport : mVirtualDisplays) {
+        dumpViewport(dump, viewport);
+    }
+}
+
+void InputReaderConfiguration::dumpViewport(String8& dump, const DisplayViewport& viewport) const {
+    dump.appendFormat(INDENT5 "Viewport: displayId=%d, orientation=%d, uniqueId='%s', "
+            "logicalFrame=[%d, %d, %d, %d], "
+            "physicalFrame=[%d, %d, %d, %d], "
+            "deviceSize=[%d, %d]\n",
+            viewport.displayId, viewport.orientation, viewport.uniqueId.c_str(),
+            viewport.logicalLeft, viewport.logicalTop,
+            viewport.logicalRight, viewport.logicalBottom,
+            viewport.physicalLeft, viewport.physicalTop,
+            viewport.physicalRight, viewport.physicalBottom,
+            viewport.deviceWidth, viewport.deviceHeight);
 }
 
 
@@ -851,6 +898,9 @@
             mConfig.pointerGestureMovementSpeedRatio);
     dump.appendFormat(INDENT3 "ZoomSpeedRatio: %0.1f\n",
             mConfig.pointerGestureZoomSpeedRatio);
+
+    dump.append(INDENT3 "Viewports:\n");
+    mConfig.dump(dump);
 }
 
 void InputReader::monitor() {
@@ -2161,7 +2211,7 @@
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
         if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
             DisplayViewport v;
-            if (config->getDisplayInfo(false /*external*/, &v)) {
+            if (config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, NULL, &v)) {
                 mOrientation = v.orientation;
             } else {
                 mOrientation = DISPLAY_ORIENTATION_0;
@@ -2534,7 +2584,7 @@
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
         if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
             DisplayViewport v;
-            if (config->getDisplayInfo(false /*external*/, &v)) {
+            if (config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, NULL, &v)) {
                 mOrientation = v.orientation;
             } else {
                 mOrientation = DISPLAY_ORIENTATION_0;
@@ -2979,7 +3029,7 @@
 }
 
 void TouchInputMapper::dump(String8& dump) {
-    dump.append(INDENT2 "Touch Input Mapper:\n");
+    dump.appendFormat(INDENT2 "Touch Input Mapper (mode - %s):\n", modeToString(mDeviceMode));
     dumpParameters(dump);
     dumpVirtualKeys(dump);
     dumpRawPointerAxes(dump);
@@ -3071,6 +3121,22 @@
     }
 }
 
+const char* TouchInputMapper::modeToString(DeviceMode deviceMode) {
+    switch (deviceMode) {
+    case DEVICE_MODE_DISABLED:
+        return "disabled";
+    case DEVICE_MODE_DIRECT:
+        return "direct";
+    case DEVICE_MODE_UNSCALED:
+        return "unscaled";
+    case DEVICE_MODE_NAVIGATION:
+        return "navigation";
+    case DEVICE_MODE_POINTER:
+        return "pointer";
+    }
+    return "unknown";
+}
+
 void TouchInputMapper::configure(nsecs_t when,
         const InputReaderConfiguration* config, uint32_t changes) {
     InputMapper::configure(when, config, changes);
@@ -3196,9 +3262,11 @@
             || mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN
             || mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) {
         mParameters.hasAssociatedDisplay = true;
-        mParameters.associatedDisplayIsExternal =
-                mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN
-                        && getDevice()->isExternal();
+        if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) {
+            mParameters.associatedDisplayIsExternal = getDevice()->isExternal();
+            getDevice()->getConfiguration().tryGetProperty(String8("touch.displayId"),
+                    mParameters.uniqueDisplayId);
+        }
     }
 
     // Initial downs on external touch devices should wake the device.
@@ -3240,9 +3308,11 @@
         ALOG_ASSERT(false);
     }
 
-    dump.appendFormat(INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s\n",
+    dump.appendFormat(
+            INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s, displayId='%s'\n",
             toString(mParameters.hasAssociatedDisplay),
-            toString(mParameters.associatedDisplayIsExternal));
+            toString(mParameters.associatedDisplayIsExternal),
+            mParameters.uniqueDisplayId.c_str());
     dump.appendFormat(INDENT4 "OrientationAware: %s\n",
             toString(mParameters.orientationAware));
 }
@@ -3318,7 +3388,21 @@
     // Get associated display dimensions.
     DisplayViewport newViewport;
     if (mParameters.hasAssociatedDisplay) {
-        if (!mConfig.getDisplayInfo(mParameters.associatedDisplayIsExternal, &newViewport)) {
+        const String8* uniqueDisplayId = NULL;
+        ViewportType viewportTypeToUse;
+
+        if (mParameters.associatedDisplayIsExternal) {
+            viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL;
+        } else if (!mParameters.uniqueDisplayId.isEmpty()) {
+            // If the IDC file specified a unique display Id, then it expects to be linked to a
+            // virtual display with the same unique ID.
+            uniqueDisplayId = &mParameters.uniqueDisplayId;
+            viewportTypeToUse = ViewportType::VIEWPORT_VIRTUAL;
+        } else {
+            viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL;
+        }
+
+        if (!mConfig.getDisplayViewport(viewportTypeToUse, uniqueDisplayId, &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.",
diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h
index 3171526..edb6fcc 100644
--- a/services/inputflinger/InputReader.h
+++ b/services/inputflinger/InputReader.h
@@ -21,6 +21,7 @@
 #include "PointerControllerInterface.h"
 #include "InputListener.h"
 
+#include <input/DisplayViewport.h>
 #include <input/Input.h>
 #include <input/VelocityControl.h>
 #include <input/VelocityTracker.h>
@@ -49,70 +50,6 @@
 class InputMapper;
 
 /*
- * Describes how coordinates are mapped on a physical display.
- * See com.android.server.display.DisplayViewport.
- */
-struct DisplayViewport {
-    int32_t displayId; // -1 if invalid
-    int32_t orientation;
-    int32_t logicalLeft;
-    int32_t logicalTop;
-    int32_t logicalRight;
-    int32_t logicalBottom;
-    int32_t physicalLeft;
-    int32_t physicalTop;
-    int32_t physicalRight;
-    int32_t physicalBottom;
-    int32_t deviceWidth;
-    int32_t deviceHeight;
-
-    DisplayViewport() :
-            displayId(ADISPLAY_ID_NONE), orientation(DISPLAY_ORIENTATION_0),
-            logicalLeft(0), logicalTop(0), logicalRight(0), logicalBottom(0),
-            physicalLeft(0), physicalTop(0), physicalRight(0), physicalBottom(0),
-            deviceWidth(0), deviceHeight(0) {
-    }
-
-    bool operator==(const DisplayViewport& other) const {
-        return displayId == other.displayId
-                && orientation == other.orientation
-                && logicalLeft == other.logicalLeft
-                && logicalTop == other.logicalTop
-                && logicalRight == other.logicalRight
-                && logicalBottom == other.logicalBottom
-                && physicalLeft == other.physicalLeft
-                && physicalTop == other.physicalTop
-                && physicalRight == other.physicalRight
-                && 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;
-    }
-};
-
-/*
  * Input reader configuration.
  *
  * Specifies various options that modify the behavior of the input reader.
@@ -255,12 +192,19 @@
             pointerGestureZoomSpeedRatio(0.3f),
             showTouches(false) { }
 
-    bool getDisplayInfo(bool external, DisplayViewport* outViewport) const;
-    void setDisplayInfo(bool external, const DisplayViewport& viewport);
+    bool getDisplayViewport(ViewportType viewportType, const String8* displayId,
+            DisplayViewport* outViewport) const;
+    void setPhysicalDisplayViewport(ViewportType viewportType, const DisplayViewport& viewport);
+    void setVirtualDisplayViewports(const Vector<DisplayViewport>& viewports);
+
+
+    void dump(String8& dump) const;
+    void dumpViewport(String8& dump, const DisplayViewport& viewport) const;
 
 private:
     DisplayViewport mInternalDisplay;
     DisplayViewport mExternalDisplay;
+    Vector<DisplayViewport> mVirtualDisplays;
 };
 
 
@@ -1341,6 +1285,7 @@
         bool associatedDisplayIsExternal;
         bool orientationAware;
         bool hasButtonUnderPad;
+        String8 uniqueDisplayId;
 
         enum GestureMode {
             GESTURE_MODE_SINGLE_TOUCH,
@@ -1889,6 +1834,8 @@
     const VirtualKey* findVirtualKeyHit(int32_t x, int32_t y);
 
     static void assignPointerIds(const RawState* last, RawState* current);
+
+    const char* modeToString(DeviceMode deviceMode);
 };
 
 
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 2e0bcd1..dcfe114 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -29,6 +29,10 @@
 static const int32_t DISPLAY_ID = 0;
 static const int32_t DISPLAY_WIDTH = 480;
 static const int32_t DISPLAY_HEIGHT = 800;
+static const int32_t VIRTUAL_DISPLAY_ID = 1;
+static const int32_t VIRTUAL_DISPLAY_WIDTH = 400;
+static const int32_t VIRTUAL_DISPLAY_HEIGHT = 500;
+static const char* VIRTUAL_DISPLAY_UNIQUE_ID = "Vr-display-unique-ID";
 
 // Error tolerance for floating point assertions.
 static const float EPSILON = 0.001f;
@@ -136,25 +140,19 @@
     FakeInputReaderPolicy() {
     }
 
-    void setDisplayInfo(int32_t displayId, int32_t width, int32_t height, int32_t orientation) {
+    void setDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
+            const String8& uniqueId) {
+        DisplayViewport v = createDisplayViewport(displayId, width, height, orientation, uniqueId);
         // Set the size of both the internal and external display at the same time.
-        bool isRotated = (orientation == DISPLAY_ORIENTATION_90
-                || orientation == DISPLAY_ORIENTATION_270);
-        DisplayViewport v;
-        v.displayId = displayId;
-        v.orientation = orientation;
-        v.logicalLeft = 0;
-        v.logicalTop = 0;
-        v.logicalRight = isRotated ? height : width;
-        v.logicalBottom = isRotated ? width : height;
-        v.physicalLeft = 0;
-        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);
+        mConfig.setPhysicalDisplayViewport(ViewportType::VIEWPORT_INTERNAL, v);
+        mConfig.setPhysicalDisplayViewport(ViewportType::VIEWPORT_EXTERNAL, v);
+    }
+
+    void setVirtualDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
+            const String8& uniqueId) {
+        Vector<DisplayViewport> viewports;
+        viewports.push_back(createDisplayViewport(displayId, width, height, orientation, uniqueId));
+        mConfig.setVirtualDisplayViewports(viewports);
     }
 
     void addExcludedDeviceName(const String8& deviceName) {
@@ -187,6 +185,27 @@
     }
 
 private:
+    DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height,
+            int32_t orientation, const String8& uniqueId) {
+        bool isRotated = (orientation == DISPLAY_ORIENTATION_90
+                || orientation == DISPLAY_ORIENTATION_270);
+        DisplayViewport v;
+        v.displayId = displayId;
+        v.orientation = orientation;
+        v.logicalLeft = 0;
+        v.logicalTop = 0;
+        v.logicalRight = isRotated ? height : width;
+        v.logicalBottom = isRotated ? width : height;
+        v.physicalLeft = 0;
+        v.physicalTop = 0;
+        v.physicalRight = isRotated ? height : width;
+        v.physicalBottom = isRotated ? width : height;
+        v.deviceWidth = isRotated ? height : width;
+        v.deviceHeight = isRotated ? width : height;
+        v.uniqueId = uniqueId;
+        return v;
+    }
+
     virtual void getReaderConfiguration(InputReaderConfiguration* outConfig) {
         *outConfig = mConfig;
     }
@@ -1445,7 +1464,13 @@
 
     void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
             int32_t orientation) {
-        mFakePolicy->setDisplayInfo(displayId, width, height, orientation);
+        mFakePolicy->setDisplayViewport(displayId, width, height, orientation, String8::empty());
+        configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    }
+
+    void setVirtualDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
+            int32_t orientation, const String8& uniqueId) {
+        mFakePolicy->setVirtualDisplayViewport(displayId, width, height, orientation, uniqueId);
         configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     }
 
@@ -2751,6 +2776,8 @@
     static const int32_t RAW_SLOT_MAX;
     static const float X_PRECISION;
     static const float Y_PRECISION;
+    static const float X_PRECISION_VIRTUAL;
+    static const float Y_PRECISION_VIRTUAL;
 
     static const float GEOMETRIC_SCALE;
     static const TouchAffineTransformation AFFINE_TRANSFORM;
@@ -2772,6 +2799,7 @@
     };
 
     void prepareDisplay(int32_t orientation);
+    void prepareVirtualDisplay(int32_t orientation);
     void prepareVirtualKeys();
     void prepareLocationCalibration();
     int32_t toRawX(float displayX);
@@ -2779,7 +2807,10 @@
     float toCookedX(float rawX, float rawY);
     float toCookedY(float rawX, float rawY);
     float toDisplayX(int32_t rawX);
+    float toDisplayX(int32_t rawX, int32_t displayWidth);
     float toDisplayY(int32_t rawY);
+    float toDisplayY(int32_t rawY, int32_t displayHeight);
+
 };
 
 const int32_t TouchInputMapperTest::RAW_X_MIN = 25;
@@ -2804,6 +2835,10 @@
 const int32_t TouchInputMapperTest::RAW_SLOT_MAX = 9;
 const float TouchInputMapperTest::X_PRECISION = float(RAW_X_MAX - RAW_X_MIN + 1) / DISPLAY_WIDTH;
 const float TouchInputMapperTest::Y_PRECISION = float(RAW_Y_MAX - RAW_Y_MIN + 1) / DISPLAY_HEIGHT;
+const float TouchInputMapperTest::X_PRECISION_VIRTUAL =
+        float(RAW_X_MAX - RAW_X_MIN + 1) / VIRTUAL_DISPLAY_WIDTH;
+const float TouchInputMapperTest::Y_PRECISION_VIRTUAL =
+        float(RAW_Y_MAX - RAW_Y_MIN + 1) / VIRTUAL_DISPLAY_HEIGHT;
 const TouchAffineTransformation TouchInputMapperTest::AFFINE_TRANSFORM =
         TouchAffineTransformation(1, -2, 3, -4, 5, -6);
 
@@ -2820,6 +2855,11 @@
     setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation);
 }
 
+void TouchInputMapperTest::prepareVirtualDisplay(int32_t orientation) {
+    setVirtualDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH,
+        VIRTUAL_DISPLAY_HEIGHT, orientation, String8(VIRTUAL_DISPLAY_UNIQUE_ID));
+}
+
 void TouchInputMapperTest::prepareVirtualKeys() {
     mFakeEventHub->addVirtualKeyDefinition(DEVICE_ID, VIRTUAL_KEYS[0]);
     mFakeEventHub->addVirtualKeyDefinition(DEVICE_ID, VIRTUAL_KEYS[1]);
@@ -2850,11 +2890,19 @@
 }
 
 float TouchInputMapperTest::toDisplayX(int32_t rawX) {
-    return float(rawX - RAW_X_MIN) * DISPLAY_WIDTH / (RAW_X_MAX - RAW_X_MIN + 1);
+    return toDisplayX(rawX, DISPLAY_WIDTH);
+}
+
+float TouchInputMapperTest::toDisplayX(int32_t rawX, int32_t displayWidth) {
+    return float(rawX - RAW_X_MIN) * displayWidth / (RAW_X_MAX - RAW_X_MIN + 1);
 }
 
 float TouchInputMapperTest::toDisplayY(int32_t rawY) {
-    return float(rawY - RAW_Y_MIN) * DISPLAY_HEIGHT / (RAW_Y_MAX - RAW_Y_MIN + 1);
+    return toDisplayY(rawY, DISPLAY_HEIGHT);
+}
+
+float TouchInputMapperTest::toDisplayY(int32_t rawY, int32_t displayHeight) {
+    return float(rawY - RAW_Y_MIN) * displayHeight / (RAW_Y_MAX - RAW_Y_MIN + 1);
 }
 
 
@@ -3314,6 +3362,105 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
 }
 
+TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture_VirtualDisplay) {
+    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    addConfigurationProperty("touch.displayId", VIRTUAL_DISPLAY_UNIQUE_ID);
+
+    prepareVirtualDisplay(DISPLAY_ORIENTATION_0);
+    prepareButtons();
+    prepareAxes(POSITION);
+    prepareVirtualKeys();
+    addMapperAndConfigure(mapper);
+
+    mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+
+    NotifyMotionArgs motionArgs;
+
+    // Down.
+    int32_t x = 100;
+    int32_t y = 125;
+    processDown(mapper, x, y);
+    processSync(mapper);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
+    ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
+    ASSERT_EQ(VIRTUAL_DISPLAY_ID, motionArgs.displayId);
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
+    ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.flags);
+    ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(0, motionArgs.edgeFlags);
+    ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+    ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(x, VIRTUAL_DISPLAY_WIDTH), toDisplayY(y, VIRTUAL_DISPLAY_HEIGHT),
+            1, 0, 0, 0, 0, 0, 0, 0));
+    ASSERT_NEAR(X_PRECISION_VIRTUAL, motionArgs.xPrecision, EPSILON);
+    ASSERT_NEAR(Y_PRECISION_VIRTUAL, motionArgs.yPrecision, EPSILON);
+    ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
+
+    // Move.
+    x += 50;
+    y += 75;
+    processMove(mapper, x, y);
+    processSync(mapper);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
+    ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
+    ASSERT_EQ(VIRTUAL_DISPLAY_ID, motionArgs.displayId);
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
+    ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.flags);
+    ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(0, motionArgs.edgeFlags);
+    ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+    ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(x, VIRTUAL_DISPLAY_WIDTH), toDisplayY(y, VIRTUAL_DISPLAY_HEIGHT),
+            1, 0, 0, 0, 0, 0, 0, 0));
+    ASSERT_NEAR(X_PRECISION_VIRTUAL, motionArgs.xPrecision, EPSILON);
+    ASSERT_NEAR(Y_PRECISION_VIRTUAL, motionArgs.yPrecision, EPSILON);
+    ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
+
+    // Up.
+    processUp(mapper);
+    processSync(mapper);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime);
+    ASSERT_EQ(DEVICE_ID, motionArgs.deviceId);
+    ASSERT_EQ(VIRTUAL_DISPLAY_ID, motionArgs.displayId);
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source);
+    ASSERT_EQ(uint32_t(0), motionArgs.policyFlags);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+    ASSERT_EQ(0, motionArgs.flags);
+    ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState);
+    ASSERT_EQ(0, motionArgs.buttonState);
+    ASSERT_EQ(0, motionArgs.edgeFlags);
+    ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+    ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
+            toDisplayX(x, VIRTUAL_DISPLAY_WIDTH), toDisplayY(y, VIRTUAL_DISPLAY_HEIGHT),
+            1, 0, 0, 0, 0, 0, 0, 0));
+    ASSERT_NEAR(X_PRECISION_VIRTUAL, motionArgs.xPrecision, EPSILON);
+    ASSERT_NEAR(Y_PRECISION_VIRTUAL, motionArgs.yPrecision, EPSILON);
+    ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime);
+
+    // Should not have sent any more keys or motions.
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
 TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) {
     SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp
index ee09d48..f0bdcd9 100644
--- a/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp
+++ b/services/vr/virtual_touchpad/VirtualTouchpadEvdev.cpp
@@ -18,7 +18,7 @@
 // use it to look up device configuration, so it must be unique. Vendor and
 // product values must be 0 to indicate an internal device and prevent a
 // similar lookup that could conflict with a physical device.
-static const char* const kDeviceNameFormat = "vr virtual touchpad %d";
+static const char* const kDeviceNameFormat = "vr-virtual-touchpad-%d";
 static constexpr int16_t kDeviceBusType = BUS_VIRTUAL;
 static constexpr int16_t kDeviceVendor = 0;
 static constexpr int16_t kDeviceProduct = 0;
diff --git a/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp b/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
index 24cfdf8..564bcd7 100644
--- a/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
+++ b/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
@@ -139,7 +139,7 @@
     SCOPED_TRACE(t);
     uidev = touchpad->injector[t].GetUiDev();
     String8 name;
-    name.appendFormat("vr virtual touchpad %d", t);
+    name.appendFormat("vr-virtual-touchpad-%d", t);
     EXPECT_EQ(name, uidev->name);
     for (int i = 0; i < ABS_CNT; ++i) {
       EXPECT_EQ(0, uidev->absmin[i]);