Add uniqueId to Virtual Display and pass through to inputflinger (1/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.
Test: com.android.server.display.DisplayManagerServiceTest

Bug: 36051620
Change-Id: Ic2117eb8e19f7f3c59687160591f8bc6692c1f12
diff --git a/services/core/java/com/android/server/Vr2dDisplay.java b/services/core/java/com/android/server/Vr2dDisplay.java
index 1116e4e..f9bcb01 100644
--- a/services/core/java/com/android/server/Vr2dDisplay.java
+++ b/services/core/java/com/android/server/Vr2dDisplay.java
@@ -40,6 +40,8 @@
     private int mVirtualDisplayWidth;
     private int mVirtualDisplayDpi;
     private final static int STOP_VIRTUAL_DISPLAY_DELAY_MILLIS = 2000;
+    private final static String UNIQUE_DISPLAY_ID = "277f1a09-b88d-4d1e-8716-796f114d080b";
+    private final static String DISPLAY_NAME = "VR 2D Display";
 
     private final static String DEBUG_ACTION_SET_MODE =
             "com.android.server.vr.Vr2dDisplay.SET_MODE";
@@ -268,9 +270,11 @@
                 return;
             }
 
-            mVirtualDisplay = mDisplayManager.createVirtualDisplay("VR 2D Display",
-                    mVirtualDisplayWidth, mVirtualDisplayHeight, mVirtualDisplayDpi,
-                    null /* Surface */, 0 /* flags */);
+            int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH;
+            mVirtualDisplay = mDisplayManager.createVirtualDisplay(null /* projection */,
+                    DISPLAY_NAME, mVirtualDisplayWidth, mVirtualDisplayHeight, mVirtualDisplayDpi,
+                    null /* surface */, flags, null /* callback */, null /* handler */,
+                    UNIQUE_DISPLAY_ID);
 
             if (mVirtualDisplay != null) {
                 mActivityManagerInternal.setVr2dDisplayId(
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index f49d482..7266876 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -4966,10 +4966,11 @@
 
         VirtualActivityDisplay(int width, int height, int density) {
             DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
-            mVirtualDisplay = dm.createVirtualDisplay(mService.mContext, null,
-                    VIRTUAL_DISPLAY_BASE_NAME, width, height, density, null,
+            mVirtualDisplay = dm.createVirtualDisplay(mService.mContext, null /* projection */,
+                    VIRTUAL_DISPLAY_BASE_NAME, width, height, density, null /* surface */,
                     DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC |
-                    DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY, null, null);
+                    DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY, null /* callback */,
+                    null /* handler */, null /* uniqueId */);
 
             init(mVirtualDisplay.getDisplay());
 
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 4a52b3c..ef6de4c 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -113,6 +113,13 @@
     public static final int TOUCH_EXTERNAL = 2;
 
     /**
+     * Touch attachment: Touch input is via an input device matching {@link VirtualDisplay}'s
+     * uniqueId.
+     * @hide
+     */
+    public static final int TOUCH_VIRTUAL = 3;
+
+    /**
      * Diff result: The {@link #state} fields differ.
      */
     public static final int DIFF_STATE = 1 << 0;
@@ -391,6 +398,8 @@
                 return "INTERNAL";
             case TOUCH_EXTERNAL:
                 return "EXTERNAL";
+            case TOUCH_VIRTUAL:
+                return "VIRTUAL";
             default:
                 return Integer.toString(touch);
         }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 2082958..8129f45 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -23,10 +23,12 @@
 import static android.hardware.display.DisplayManager
         .VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.SensorManager;
@@ -213,6 +215,7 @@
     // input from an external source.  Used by the input system.
     private final DisplayViewport mDefaultViewport = new DisplayViewport();
     private final DisplayViewport mExternalTouchViewport = new DisplayViewport();
+    private final ArrayList<DisplayViewport> mVirtualTouchViewports = new ArrayList<>();
 
     // Persistent data store for all internal settings maintained by the display manager service.
     private final PersistentDataStore mPersistentDataStore = new PersistentDataStore();
@@ -228,6 +231,7 @@
     // 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();
+    private final ArrayList<DisplayViewport> mTempVirtualTouchViewports = new ArrayList<>();
 
     // The default color mode for default displays. Overrides the usual
     // Display.Display.COLOR_MODE_DEFAULT for displays with the
@@ -242,8 +246,16 @@
     // Lists of UIDs that are present on the displays. Maps displayId -> array of UIDs.
     private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>();
 
+    private final Injector mInjector;
+
     public DisplayManagerService(Context context) {
+        this(context, new Injector());
+    }
+
+    @VisibleForTesting
+    DisplayManagerService(Context context, Injector injector) {
         super(context);
+        mInjector = injector;
         mContext = context;
         mHandler = new DisplayManagerHandler(DisplayThread.get().getLooper());
         mUiHandler = UiThread.getHandler();
@@ -326,6 +338,11 @@
         mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
     }
 
+    @VisibleForTesting
+    Handler getDisplayHandler() {
+        return mHandler;
+    }
+
     private void registerDisplayTransactionListenerInternal(
             DisplayTransactionListener listener) {
         // List is self-synchronized copy-on-write.
@@ -363,7 +380,8 @@
         }
     }
 
-    private void performTraversalInTransactionFromWindowManagerInternal() {
+    @VisibleForTesting
+    void performTraversalInTransactionFromWindowManagerInternal() {
         synchronized (mSyncRoot) {
             if (!mPendingTraversal) {
                 return;
@@ -601,8 +619,8 @@
     }
 
     private int createVirtualDisplayInternal(IVirtualDisplayCallback callback,
-            IMediaProjection projection, int callingUid, String packageName,
-            String name, int width, int height, int densityDpi, Surface surface, int flags) {
+            IMediaProjection projection, int callingUid, String packageName, String name, int width,
+            int height, int densityDpi, Surface surface, int flags, String uniqueId) {
         synchronized (mSyncRoot) {
             if (mVirtualDisplayAdapter == null) {
                 Slog.w(TAG, "Rejecting request to create private virtual display "
@@ -611,8 +629,8 @@
             }
 
             DisplayDevice device = mVirtualDisplayAdapter.createVirtualDisplayLocked(
-                    callback, projection, callingUid, packageName,
-                    name, width, height, densityDpi, surface, flags);
+                    callback, projection, callingUid, packageName, name, width, height, densityDpi,
+                    surface, flags, uniqueId);
             if (device == null) {
                 return -1;
             }
@@ -702,8 +720,8 @@
     }
 
     private void registerVirtualDisplayAdapterLocked() {
-        mVirtualDisplayAdapter = new VirtualDisplayAdapter(
-                mSyncRoot, mContext, mHandler, mDisplayAdapterListener);
+        mVirtualDisplayAdapter = mInjector.getVirtualDisplayAdapter(mSyncRoot, mContext, mHandler,
+                mDisplayAdapterListener);
         registerDisplayAdapterLocked(mVirtualDisplayAdapter);
     }
 
@@ -995,6 +1013,7 @@
     private void clearViewportsLocked() {
         mDefaultViewport.valid = false;
         mExternalTouchViewport.valid = false;
+        mVirtualTouchViewports.clear();
     }
 
     private void configureDisplayInTransactionLocked(DisplayDevice device) {
@@ -1033,6 +1052,28 @@
                 && info.touch == DisplayDeviceInfo.TOUCH_EXTERNAL) {
             setViewportLocked(mExternalTouchViewport, display, device);
         }
+
+        if (info.touch == DisplayDeviceInfo.TOUCH_VIRTUAL && !TextUtils.isEmpty(info.uniqueId)) {
+            final DisplayViewport viewport = getVirtualTouchViewportLocked(info.uniqueId);
+            setViewportLocked(viewport, display, device);
+        }
+    }
+
+    /** Gets the virtual device viewport or creates it if not yet created. */
+    private DisplayViewport getVirtualTouchViewportLocked(@NonNull String uniqueId) {
+        DisplayViewport viewport;
+        final int count = mVirtualTouchViewports.size();
+        for (int i = 0; i < count; i++) {
+            viewport = mVirtualTouchViewports.get(i);
+            if (uniqueId.equals(viewport.uniqueId)) {
+                return viewport;
+            }
+        }
+
+        viewport = new DisplayViewport();
+        viewport.uniqueId = uniqueId;
+        mVirtualTouchViewports.add(viewport);
+        return viewport;
     }
 
     private static void setViewportLocked(DisplayViewport viewport,
@@ -1113,6 +1154,7 @@
             pw.println("  mNextNonDefaultDisplayId=" + mNextNonDefaultDisplayId);
             pw.println("  mDefaultViewport=" + mDefaultViewport);
             pw.println("  mExternalTouchViewport=" + mExternalTouchViewport);
+            pw.println("  mVirtualTouchViewports=" + mVirtualTouchViewports);
             pw.println("  mDefaultDisplayDefaultColorMode=" + mDefaultDisplayDefaultColorMode);
             pw.println("  mSingleDisplayDemoMode=" + mSingleDisplayDemoMode);
             pw.println("  mWifiDisplayScanRequestCount=" + mWifiDisplayScanRequestCount);
@@ -1171,6 +1213,14 @@
     public static final class SyncRoot {
     }
 
+    @VisibleForTesting
+    static class Injector {
+        VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, Context context,
+                Handler handler, DisplayAdapter.Listener displayAdapterListener) {
+            return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener);
+        }
+    }
+
     private final class DisplayManagerHandler extends Handler {
         public DisplayManagerHandler(Looper looper) {
             super(looper, null, true /*async*/);
@@ -1199,9 +1249,15 @@
                     synchronized (mSyncRoot) {
                         mTempDefaultViewport.copyFrom(mDefaultViewport);
                         mTempExternalTouchViewport.copyFrom(mExternalTouchViewport);
+                        if (!mTempVirtualTouchViewports.equals(mVirtualTouchViewports)) {
+                          mTempVirtualTouchViewports.clear();
+                          for (DisplayViewport d : mVirtualTouchViewports) {
+                              mTempVirtualTouchViewports.add(d.makeCopy());
+                          }
+                        }
                     }
-                    mInputManagerInternal.setDisplayViewports(
-                            mTempDefaultViewport, mTempExternalTouchViewport);
+                    mInputManagerInternal.setDisplayViewports(mTempDefaultViewport,
+                            mTempExternalTouchViewport, mTempVirtualTouchViewports);
                     break;
                 }
             }
@@ -1264,7 +1320,8 @@
         }
     }
 
-    private final class BinderService extends IDisplayManager.Stub {
+    @VisibleForTesting
+    final class BinderService extends IDisplayManager.Stub {
         /**
          * Returns information about the specified logical display.
          *
@@ -1458,7 +1515,8 @@
         @Override // Binder call
         public int createVirtualDisplay(IVirtualDisplayCallback callback,
                 IMediaProjection projection, String packageName, String name,
-                int width, int height, int densityDpi, Surface surface, int flags) {
+                int width, int height, int densityDpi, Surface surface, int flags,
+                String uniqueId) {
             final int callingUid = Binder.getCallingUid();
             if (!validatePackageName(callingUid, packageName)) {
                 throw new SecurityException("packageName must match the calling uid");
@@ -1520,8 +1578,8 @@
 
             final long token = Binder.clearCallingIdentity();
             try {
-                return createVirtualDisplayInternal(callback, projection, callingUid,
-                        packageName, name, width, height, densityDpi, surface, flags);
+                return createVirtualDisplayInternal(callback, projection, callingUid, packageName,
+                        name, width, height, densityDpi, surface, flags, uniqueId);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 5149933..9d3021a 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -22,6 +22,7 @@
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH;
 
 import android.content.Context;
 import android.hardware.display.IVirtualDisplayCallback;
@@ -39,6 +40,8 @@
 import android.view.Surface;
 import android.view.SurfaceControl;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.io.PrintWriter;
 import java.util.Iterator;
 
@@ -48,7 +51,8 @@
  * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
  * </p>
  */
-final class VirtualDisplayAdapter extends DisplayAdapter {
+@VisibleForTesting
+public class VirtualDisplayAdapter extends DisplayAdapter {
     static final String TAG = "VirtualDisplayAdapter";
     static final boolean DEBUG = false;
 
@@ -57,27 +61,42 @@
 
     private final ArrayMap<IBinder, VirtualDisplayDevice> mVirtualDisplayDevices =
             new ArrayMap<IBinder, VirtualDisplayDevice>();
-    private Handler mHandler;
+    private final Handler mHandler;
+    private final SurfaceControlDisplayFactory mSurfaceControlDisplayFactory;
 
     // Called with SyncRoot lock held.
     public VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
             Context context, Handler handler, Listener listener) {
+        this(syncRoot, context, handler, listener,
+                (String name, boolean secure) -> SurfaceControl.createDisplay(name, secure));
+    }
+
+    @VisibleForTesting
+    VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
+            Context context, Handler handler, Listener listener,
+            SurfaceControlDisplayFactory surfaceControlDisplayFactory) {
         super(syncRoot, context, handler, listener, TAG);
         mHandler = handler;
+        mSurfaceControlDisplayFactory = surfaceControlDisplayFactory;
     }
 
     public DisplayDevice createVirtualDisplayLocked(IVirtualDisplayCallback callback,
-            IMediaProjection projection, int ownerUid, String ownerPackageName,
-            String name, int width, int height, int densityDpi, Surface surface, int flags) {
+            IMediaProjection projection, int ownerUid, String ownerPackageName, String name,
+            int width, int height, int densityDpi, Surface surface, int flags, String uniqueId) {
         boolean secure = (flags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0;
         IBinder appToken = callback.asBinder();
-        IBinder displayToken = SurfaceControl.createDisplay(name, secure);
+        IBinder displayToken = mSurfaceControlDisplayFactory.createDisplay(name, secure);
         final String baseUniqueId =
                 UNIQUE_ID_PREFIX + ownerPackageName + "," + ownerUid + "," + name + ",";
         final int uniqueIndex = getNextUniqueIndex(baseUniqueId);
+        if (uniqueId == null) {
+            uniqueId = baseUniqueId + uniqueIndex;
+        } else {
+            uniqueId = UNIQUE_ID_PREFIX + ownerPackageName + ":" + uniqueId;
+        }
         VirtualDisplayDevice device = new VirtualDisplayDevice(displayToken, appToken,
                 ownerUid, ownerPackageName, name, width, height, densityDpi, surface, flags,
-                new Callback(callback, mHandler), baseUniqueId + uniqueIndex, uniqueIndex);
+                new Callback(callback, mHandler), uniqueId, uniqueIndex);
 
         mVirtualDisplayDevices.put(appToken, device);
 
@@ -341,7 +360,8 @@
                     mInfo.flags |= DisplayDeviceInfo.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
                 }
                 mInfo.type = Display.TYPE_VIRTUAL;
-                mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
+                mInfo.touch = ((mFlags & VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH) == 0) ?
+                        DisplayDeviceInfo.TOUCH_NONE : DisplayDeviceInfo.TOUCH_VIRTUAL;
                 mInfo.state = mSurface != null ? Display.STATE_ON : Display.STATE_OFF;
                 mInfo.ownerUid = mOwnerUid;
                 mInfo.ownerPackageName = mOwnerPackageName;
@@ -407,4 +427,9 @@
             }
         }
     }
+
+    @VisibleForTesting
+    public interface SurfaceControlDisplayFactory {
+        public IBinder createDisplay(String name, boolean secure);
+    }
 }
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 0e52871..717efbf 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -190,11 +190,13 @@
     private static native long nativeInit(InputManagerService service,
             Context context, MessageQueue messageQueue);
     private static native void nativeStart(long ptr);
-    private static native void nativeSetDisplayViewport(long ptr, boolean external,
+    private static native void nativeSetVirtualDisplayViewports(long ptr,
+            DisplayViewport[] viewports);
+    private static native void nativeSetDisplayViewport(long ptr, int viewportType,
             int displayId, int rotation,
             int logicalLeft, int logicalTop, int logicalRight, int logicalBottom,
             int physicalLeft, int physicalTop, int physicalRight, int physicalBottom,
-            int deviceWidth, int deviceHeight);
+            int deviceWidth, int deviceHeight, String uniqueId);
 
     private static native int nativeGetScanCodeState(long ptr,
             int deviceId, int sourceMask, int scanCode);
@@ -292,6 +294,11 @@
     /** Switch code: Camera lens cover. When set the lens is covered. */
     public static final int SW_CAMERA_LENS_COVER = 0x09;
 
+    // Viewport constants defined in InputReader.h.
+    public static final int VIEWPORT_DEFAULT = 1;
+    public static final int VIEWPORT_EXTERNAL = 2;
+    public static final int VIEWPORT_VIRTUAL = 3;
+
     public static final int SW_LID_BIT = 1 << SW_LID;
     public static final int SW_TABLET_MODE_BIT = 1 << SW_TABLET_MODE;
     public static final int SW_KEYPAD_SLIDE_BIT = 1 << SW_KEYPAD_SLIDE;
@@ -409,26 +416,30 @@
     }
 
     private void setDisplayViewportsInternal(DisplayViewport defaultViewport,
-            DisplayViewport externalTouchViewport) {
+            DisplayViewport externalTouchViewport,
+            List<DisplayViewport> virtualTouchViewports) {
         if (defaultViewport.valid) {
-            setDisplayViewport(false, defaultViewport);
+            setDisplayViewport(VIEWPORT_DEFAULT, defaultViewport);
         }
 
         if (externalTouchViewport.valid) {
-            setDisplayViewport(true, externalTouchViewport);
+            setDisplayViewport(VIEWPORT_EXTERNAL, externalTouchViewport);
         } else if (defaultViewport.valid) {
-            setDisplayViewport(true, defaultViewport);
+            setDisplayViewport(VIEWPORT_EXTERNAL, defaultViewport);
         }
+
+        nativeSetVirtualDisplayViewports(mPtr,
+                virtualTouchViewports.toArray(new DisplayViewport[0]));
     }
 
-    private void setDisplayViewport(boolean external, DisplayViewport viewport) {
-        nativeSetDisplayViewport(mPtr, external,
+    private void setDisplayViewport(int viewportType, DisplayViewport viewport) {
+        nativeSetDisplayViewport(mPtr, viewportType,
                 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,
-                viewport.deviceWidth, viewport.deviceHeight);
+                viewport.deviceWidth, viewport.deviceHeight, viewport.uniqueId);
     }
 
     /**
@@ -2325,9 +2336,11 @@
 
     private final class LocalService extends InputManagerInternal {
         @Override
-        public void setDisplayViewports(
-                DisplayViewport defaultViewport, DisplayViewport externalTouchViewport) {
-            setDisplayViewportsInternal(defaultViewport, externalTouchViewport);
+        public void setDisplayViewports(DisplayViewport defaultViewport,
+                DisplayViewport externalTouchViewport,
+                List<DisplayViewport> virtualTouchViewports) {
+            setDisplayViewportsInternal(defaultViewport, externalTouchViewport,
+                    virtualTouchViewports);
         }
 
         @Override