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/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 3267172..731a72d 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -251,6 +251,15 @@
*/
public static final int VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 1 << 5;
+ /**
+ * Virtual display flag: Specifies that the virtual display can be associated with a
+ * touchpad device that matches its uniqueId.
+ *
+ * @see #createVirtualDisplay
+ * @hide
+ */
+ public static final int VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH = 1 << 6;
+
/** @hide */
public DisplayManager(Context context) {
mContext = context;
@@ -542,16 +551,17 @@
public VirtualDisplay createVirtualDisplay(@NonNull String name,
int width, int height, int densityDpi, @Nullable Surface surface, int flags,
@Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
- return createVirtualDisplay(null,
- name, width, height, densityDpi, surface, flags, callback, handler);
+ return createVirtualDisplay(null /* projection */, name, width, height, densityDpi, surface,
+ flags, callback, handler, null /* uniqueId */);
}
/** @hide */
public VirtualDisplay createVirtualDisplay(@Nullable MediaProjection projection,
@NonNull String name, int width, int height, int densityDpi, @Nullable Surface surface,
- int flags, @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
+ int flags, @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler,
+ @Nullable String uniqueId) {
return mGlobal.createVirtualDisplay(mContext, projection,
- name, width, height, densityDpi, surface, flags, callback, handler);
+ name, width, height, densityDpi, surface, flags, callback, handler, uniqueId);
}
/**
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 341754c..0b998e5 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -17,11 +17,10 @@
package android.hardware.display;
import android.content.Context;
-import android.content.res.Configuration;
import android.content.res.Resources;
import android.hardware.display.DisplayManager.DisplayListener;
-import android.media.projection.MediaProjection;
import android.media.projection.IMediaProjection;
+import android.media.projection.MediaProjection;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -31,8 +30,8 @@
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
-import android.view.DisplayAdjustments;
import android.view.Display;
+import android.view.DisplayAdjustments;
import android.view.DisplayInfo;
import android.view.Surface;
@@ -383,7 +382,7 @@
public VirtualDisplay createVirtualDisplay(Context context, MediaProjection projection,
String name, int width, int height, int densityDpi, Surface surface, int flags,
- VirtualDisplay.Callback callback, Handler handler) {
+ VirtualDisplay.Callback callback, Handler handler, String uniqueId) {
if (TextUtils.isEmpty(name)) {
throw new IllegalArgumentException("name must be non-null and non-empty");
}
@@ -397,7 +396,8 @@
int displayId;
try {
displayId = mDm.createVirtualDisplay(callbackWrapper, projectionToken,
- context.getPackageName(), name, width, height, densityDpi, surface, flags);
+ context.getPackageName(), name, width, height, densityDpi, surface, flags,
+ uniqueId);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/display/DisplayViewport.java b/core/java/android/hardware/display/DisplayViewport.java
index c2d498b..b4955ea 100644
--- a/core/java/android/hardware/display/DisplayViewport.java
+++ b/core/java/android/hardware/display/DisplayViewport.java
@@ -17,6 +17,7 @@
package android.hardware.display;
import android.graphics.Rect;
+import android.text.TextUtils;
/**
* Describes how the pixels of physical display device reflects the content of
@@ -52,6 +53,9 @@
public int deviceWidth;
public int deviceHeight;
+ // The ID used to uniquely identify this display.
+ public String uniqueId;
+
public void copyFrom(DisplayViewport viewport) {
valid = viewport.valid;
displayId = viewport.displayId;
@@ -60,6 +64,52 @@
physicalFrame.set(viewport.physicalFrame);
deviceWidth = viewport.deviceWidth;
deviceHeight = viewport.deviceHeight;
+ uniqueId = viewport.uniqueId;
+ }
+
+ /**
+ * Creates a copy of this DisplayViewport.
+ */
+ public DisplayViewport makeCopy() {
+ DisplayViewport dv = new DisplayViewport();
+ dv.copyFrom(this);
+ return dv;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+
+ if (!(o instanceof DisplayViewport)) {
+ return false;
+ }
+
+ DisplayViewport other = (DisplayViewport) o;
+ return valid == other.valid
+ && displayId == other.displayId
+ && orientation == other.orientation
+ && logicalFrame.equals(other.logicalFrame)
+ && physicalFrame.equals(other.physicalFrame)
+ && deviceWidth == other.deviceWidth
+ && deviceHeight == other.deviceHeight
+ && TextUtils.equals(uniqueId, other.uniqueId);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result += prime * result + (valid ? 1 : 0);
+ result += prime * result + displayId;
+ result += prime * result + orientation;
+ result += prime * result + logicalFrame.hashCode();
+ result += prime * result + physicalFrame.hashCode();
+ result += prime * result + deviceWidth;
+ result += prime * result + deviceHeight;
+ result += prime * result + uniqueId.hashCode();
+ return result;
}
// For debugging purposes.
@@ -67,6 +117,7 @@
public String toString() {
return "DisplayViewport{valid=" + valid
+ ", displayId=" + displayId
+ + ", uniqueId='" + uniqueId + "'"
+ ", orientation=" + orientation
+ ", logicalFrame=" + logicalFrame
+ ", physicalFrame=" + physicalFrame
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index f696c8d..7ca4dc1 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -66,7 +66,7 @@
// MediaProjection token for certain combinations of flags.
int createVirtualDisplay(in IVirtualDisplayCallback callback,
in IMediaProjection projectionToken, String packageName, String name,
- int width, int height, int densityDpi, in Surface surface, int flags);
+ int width, int height, int densityDpi, in Surface surface, int flags, String uniqueId);
// No permissions required, but must be same Uid as the creator.
void resizeVirtualDisplay(in IVirtualDisplayCallback token,
diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/core/java/android/hardware/input/InputManagerInternal.java
index 01a5404..4ea0f55 100644
--- a/core/java/android/hardware/input/InputManagerInternal.java
+++ b/core/java/android/hardware/input/InputManagerInternal.java
@@ -22,6 +22,8 @@
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
+import java.util.List;
+
/**
* Input manager local system service interface.
*
@@ -35,7 +37,7 @@
* by the input system. The input system must copy this information to retain it.
*/
public abstract void setDisplayViewports(DisplayViewport defaultViewport,
- DisplayViewport externalTouchViewport);
+ DisplayViewport externalTouchViewport, List<DisplayViewport> virtualTouchViewports);
/**
* Called by the power manager to tell the input manager whether it should start
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 0dc46ed..dceda9a 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -160,6 +160,7 @@
"android_hardware_camera2_legacy_LegacyCameraDevice.cpp",
"android_hardware_camera2_legacy_PerfMeasurement.cpp",
"android_hardware_camera2_DngCreator.cpp",
+ "android_hardware_display_DisplayViewport.cpp",
"android_hardware_HardwareBuffer.cpp",
"android_hardware_Radio.cpp",
"android_hardware_SensorManager.cpp",
diff --git a/core/jni/android_hardware_display_DisplayViewport.cpp b/core/jni/android_hardware_display_DisplayViewport.cpp
new file mode 100644
index 0000000..1823a2c
--- /dev/null
+++ b/core/jni/android_hardware_display_DisplayViewport.cpp
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "DisplayViewport-JNI"
+
+#include "JNIHelp.h"
+#include "core_jni_helpers.h"
+
+#include <android_hardware_display_DisplayViewport.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/Log.h>
+#include <utils/Log.h>
+
+#include <ScopedUtfChars.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+static struct {
+ jclass clazz;
+
+ jfieldID displayId;
+ jfieldID orientation;
+ jfieldID logicalFrame;
+ jfieldID physicalFrame;
+ jfieldID deviceWidth;
+ jfieldID deviceHeight;
+ jfieldID uniqueId;
+} gDisplayViewportClassInfo;
+
+static struct {
+ jfieldID left;
+ jfieldID top;
+ jfieldID right;
+ jfieldID bottom;
+} gRectClassInfo;
+
+// ----------------------------------------------------------------------------
+
+status_t android_hardware_display_DisplayViewport_toNative(JNIEnv* env, jobject viewportObj,
+ DisplayViewport* viewport) {
+ viewport->displayId = env->GetIntField(viewportObj, gDisplayViewportClassInfo.displayId);
+ viewport->orientation = env->GetIntField(viewportObj, gDisplayViewportClassInfo.orientation);
+ viewport->deviceWidth = env->GetIntField(viewportObj, gDisplayViewportClassInfo.deviceWidth);
+ viewport->deviceHeight = env->GetIntField(viewportObj, gDisplayViewportClassInfo.deviceHeight);
+
+ jstring uniqueId =
+ jstring(env->GetObjectField(viewportObj, gDisplayViewportClassInfo.uniqueId));
+ if (uniqueId != nullptr) {
+ viewport->uniqueId.setTo(ScopedUtfChars(env, uniqueId).c_str());
+ }
+
+ jobject logicalFrameObj =
+ env->GetObjectField(viewportObj, gDisplayViewportClassInfo.logicalFrame);
+ viewport->logicalLeft = env->GetIntField(logicalFrameObj, gRectClassInfo.left);
+ viewport->logicalTop = env->GetIntField(logicalFrameObj, gRectClassInfo.top);
+ viewport->logicalRight = env->GetIntField(logicalFrameObj, gRectClassInfo.right);
+ viewport->logicalBottom = env->GetIntField(logicalFrameObj, gRectClassInfo.bottom);
+
+ jobject physicalFrameObj =
+ env->GetObjectField(viewportObj, gDisplayViewportClassInfo.physicalFrame);
+ viewport->physicalLeft = env->GetIntField(physicalFrameObj, gRectClassInfo.left);
+ viewport->physicalTop = env->GetIntField(physicalFrameObj, gRectClassInfo.top);
+ viewport->physicalRight = env->GetIntField(physicalFrameObj, gRectClassInfo.right);
+ viewport->physicalBottom = env->GetIntField(physicalFrameObj, gRectClassInfo.bottom);
+
+ return OK;
+}
+
+// ----------------------------------------------------------------------------
+
+int register_android_hardware_display_DisplayViewport(JNIEnv* env) {
+ jclass clazz = FindClassOrDie(env, "android/hardware/display/DisplayViewport");
+ gDisplayViewportClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
+
+ gDisplayViewportClassInfo.displayId = GetFieldIDOrDie(env,
+ gDisplayViewportClassInfo.clazz, "displayId", "I");
+
+ gDisplayViewportClassInfo.orientation = GetFieldIDOrDie(env,
+ gDisplayViewportClassInfo.clazz, "orientation", "I");
+
+ gDisplayViewportClassInfo.deviceWidth = GetFieldIDOrDie(env,
+ gDisplayViewportClassInfo.clazz, "deviceWidth", "I");
+
+ gDisplayViewportClassInfo.deviceHeight = GetFieldIDOrDie(env,
+ gDisplayViewportClassInfo.clazz, "deviceHeight", "I");
+
+ gDisplayViewportClassInfo.logicalFrame = GetFieldIDOrDie(env,
+ gDisplayViewportClassInfo.clazz, "logicalFrame", "Landroid/graphics/Rect;");
+
+ gDisplayViewportClassInfo.physicalFrame = GetFieldIDOrDie(env,
+ gDisplayViewportClassInfo.clazz, "physicalFrame", "Landroid/graphics/Rect;");
+
+ gDisplayViewportClassInfo.uniqueId = GetFieldIDOrDie(env,
+ gDisplayViewportClassInfo.clazz, "uniqueId", "Ljava/lang/String;");
+
+ clazz = FindClassOrDie(env, "android/graphics/Rect");
+ gRectClassInfo.left = GetFieldIDOrDie(env, clazz, "left", "I");
+ gRectClassInfo.top = GetFieldIDOrDie(env, clazz, "top", "I");
+ gRectClassInfo.right = GetFieldIDOrDie(env, clazz, "right", "I");
+ gRectClassInfo.bottom = GetFieldIDOrDie(env, clazz, "bottom", "I");
+
+ return 0;
+}
+
+} // namespace android
diff --git a/core/jni/android_hardware_display_DisplayViewport.h b/core/jni/android_hardware_display_DisplayViewport.h
new file mode 100644
index 0000000..5853177
--- /dev/null
+++ b/core/jni/android_hardware_display_DisplayViewport.h
@@ -0,0 +1,34 @@
+/*
+ * 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 _ANDROID_HARDWARE_DISPLAY_DISPLAYVIEWPORT_H
+#define _ANDROID_HARDWARE_DISPLAY_DISPLAYVIEWPORT_H
+
+#include "jni.h"
+#include <input/DisplayViewport.h>
+#include <utils/Errors.h>
+#include <utils/threads.h>
+
+namespace android {
+
+/* Copies the contents of a DVM DisplayViewport object to a native DisplayViewport instance.
+ * Returns non-zero on error. */
+extern status_t android_hardware_display_DisplayViewport_toNative(JNIEnv* env, jobject eventObj,
+ DisplayViewport* viewport);
+
+} // namespace android
+
+#endif // _ANDROID_HARDWARE_DISPLAY_DISPLAYVIEWPORT_H
diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java
index e757f09..f9c5b8d 100644
--- a/media/java/android/media/projection/MediaProjection.java
+++ b/media/java/android/media/projection/MediaProjection.java
@@ -104,7 +104,8 @@
int flags = isSecure ? DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE : 0;
return dm.createVirtualDisplay(this, name, width, height, dpi, surface,
flags | DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR |
- DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION, callback, handler);
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION, callback, handler,
+ null /* uniqueId */);
}
/**
@@ -134,8 +135,8 @@
int width, int height, int dpi, int flags, @Nullable Surface surface,
@Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
DisplayManager dm = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
- return dm.createVirtualDisplay(
- this, name, width, height, dpi, surface, flags, callback, handler);
+ return dm.createVirtualDisplay(this, name, width, height, dpi, surface, flags, callback,
+ handler, null /* uniqueId */);
}
/**
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
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 14a2381..41f86c9 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -58,6 +58,7 @@
#include "com_android_server_power_PowerManagerService.h"
#include "com_android_server_input_InputApplicationHandle.h"
#include "com_android_server_input_InputWindowHandle.h"
+#include "android_hardware_display_DisplayViewport.h"
#define INDENT " "
@@ -196,7 +197,8 @@
void dump(String8& dump);
- void setDisplayViewport(bool external, const DisplayViewport& viewport);
+ void setVirtualDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray);
+ void setDisplayViewport(int32_t viewportType, const DisplayViewport& viewport);
status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
@@ -271,6 +273,7 @@
// Display size information.
DisplayViewport internalViewport;
DisplayViewport externalViewport;
+ Vector<DisplayViewport> virtualViewports;
// System UI visibility.
int32_t systemUiVisibility;
@@ -375,17 +378,53 @@
return false;
}
-void NativeInputManager::setDisplayViewport(bool external, const DisplayViewport& viewport) {
+void NativeInputManager::setVirtualDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray) {
+ Vector<DisplayViewport> viewports;
+
+ if (viewportObjArray) {
+ jsize length = env->GetArrayLength(viewportObjArray);
+ for (jsize i = 0; i < length; i++) {
+ jobject viewportObj = env->GetObjectArrayElement(viewportObjArray, i);
+ if (! viewportObj) {
+ break; // found null element indicating end of used portion of the array
+ }
+
+ DisplayViewport viewport;
+ android_hardware_display_DisplayViewport_toNative(env, viewportObj, &viewport);
+ ALOGI("Viewport [%d] to add: %s", (int) length, viewport.uniqueId.c_str());
+ viewports.push(viewport);
+
+ env->DeleteLocalRef(viewportObj);
+ }
+ }
+
+ {
+ AutoMutex _l(mLock);
+ mLocked.virtualViewports = viewports;
+ }
+
+ mInputManager->getReader()->requestRefreshConfiguration(
+ InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+}
+
+void NativeInputManager::setDisplayViewport(int32_t type, const DisplayViewport& viewport) {
bool changed = false;
{
AutoMutex _l(mLock);
- DisplayViewport& v = external ? mLocked.externalViewport : mLocked.internalViewport;
- if (v != viewport) {
- changed = true;
- v = viewport;
+ ViewportType viewportType = static_cast<ViewportType>(type);
+ DisplayViewport* v = NULL;
+ if (viewportType == ViewportType::VIEWPORT_EXTERNAL) {
+ v = &mLocked.externalViewport;
+ } else if (viewportType == ViewportType::VIEWPORT_INTERNAL) {
+ v = &mLocked.internalViewport;
+ }
- if (!external) {
+ if (v != NULL && *v != viewport) {
+ changed = true;
+ *v = viewport;
+
+ if (viewportType == ViewportType::VIEWPORT_INTERNAL) {
sp<PointerController> controller = mLocked.pointerController.promote();
if (controller != NULL) {
controller->setDisplayViewport(
@@ -478,8 +517,11 @@
outConfig->pointerCapture = mLocked.pointerCapture;
- outConfig->setDisplayInfo(false /*external*/, mLocked.internalViewport);
- outConfig->setDisplayInfo(true /*external*/, mLocked.externalViewport);
+ outConfig->setPhysicalDisplayViewport(ViewportType::VIEWPORT_INTERNAL,
+ mLocked.internalViewport);
+ outConfig->setPhysicalDisplayViewport(ViewportType::VIEWPORT_EXTERNAL,
+ mLocked.externalViewport);
+ outConfig->setVirtualDisplayViewports(mLocked.virtualViewports);
outConfig->disabledDevices = mLocked.disabledInputDevices;
} // release lock
@@ -1183,11 +1225,17 @@
}
}
-static void nativeSetDisplayViewport(JNIEnv* /* env */, jclass /* clazz */, jlong ptr,
- jboolean external, jint displayId, jint orientation,
+static void nativeSetVirtualDisplayViewports(JNIEnv* env, jclass /* clazz */, jlong ptr,
+ jobjectArray viewportObjArray) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+ im->setVirtualDisplayViewports(env, viewportObjArray);
+}
+
+static void nativeSetDisplayViewport(JNIEnv* env, jclass /* clazz */, jlong ptr,
+ jint viewportType, jint displayId, jint orientation,
jint logicalLeft, jint logicalTop, jint logicalRight, jint logicalBottom,
jint physicalLeft, jint physicalTop, jint physicalRight, jint physicalBottom,
- jint deviceWidth, jint deviceHeight) {
+ jint deviceWidth, jint deviceHeight, jstring uniqueId) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
DisplayViewport v;
@@ -1203,7 +1251,11 @@
v.physicalBottom = physicalBottom;
v.deviceWidth = deviceWidth;
v.deviceHeight = deviceHeight;
- im->setDisplayViewport(external, v);
+ if (uniqueId != nullptr) {
+ v.uniqueId.setTo(ScopedUtfChars(env, uniqueId).c_str());
+ }
+
+ im->setDisplayViewport(viewportType, v);
}
static jint nativeGetScanCodeState(JNIEnv* /* env */, jclass /* clazz */,
@@ -1574,7 +1626,9 @@
(void*) nativeInit },
{ "nativeStart", "(J)V",
(void*) nativeStart },
- { "nativeSetDisplayViewport", "(JZIIIIIIIIIIII)V",
+ { "nativeSetVirtualDisplayViewports", "(J[Landroid/hardware/display/DisplayViewport;)V",
+ (void*) nativeSetVirtualDisplayViewports },
+ { "nativeSetDisplayViewport", "(JIIIIIIIIIIIIILjava/lang/String;)V",
(void*) nativeSetDisplayViewport },
{ "nativeGetScanCodeState", "(JIII)I",
(void*) nativeGetScanCodeState },
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 2acefc3..79c9b417 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -50,6 +50,7 @@
int register_android_server_HardwarePropertiesManagerService(JNIEnv* env);
int register_android_server_SyntheticPasswordManager(JNIEnv* env);
int register_android_server_GraphicsStatsService(JNIEnv* env);
+int register_android_hardware_display_DisplayViewport(JNIEnv* env);
};
using namespace android;
@@ -94,6 +95,7 @@
register_android_server_storage_AppFuse(env);
register_android_server_SyntheticPasswordManager(env);
register_android_server_GraphicsStatsService(env);
+ register_android_hardware_display_DisplayViewport(env);
return JNI_VERSION_1_4;
}
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index fa72416..686dad4 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -191,7 +191,7 @@
</application>
<instrumentation
- android:name="android.support.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.frameworks.servicestests"
- android:label="Frameworks Services Tests" />
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.servicestests"
+ android:label="Frameworks Services Tests" />
</manifest>
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
new file mode 100644
index 0000000..c399a5d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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
+ */
+
+package com.android.server.display;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayViewport;
+import android.hardware.display.IVirtualDisplayCallback;
+import android.hardware.input.InputManagerInternal;
+import android.os.Handler;
+import android.os.IBinder;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.SurfaceControl;
+import android.view.WindowManagerInternal;
+
+import com.android.server.LocalServices;
+import com.android.server.display.DisplayManagerService.SyncRoot;
+import com.android.server.display.VirtualDisplayAdapter.SurfaceControlDisplayFactory;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@SmallTest
+public class DisplayManagerServiceTest extends AndroidTestCase {
+ private Handler mHandler;
+ private DisplayManagerService mDisplayManager;
+ @Mock InputManagerInternal mMockInputManagerInternal;
+ @Mock IVirtualDisplayCallback.Stub mMockAppToken;
+ @Mock WindowManagerInternal mMockWindowManagerInternal;
+ @Mock VirtualDisplayAdapter mMockVirtualDisplayAdapter;
+ @Mock IBinder mMockDisplayToken;
+
+ @Override
+ protected void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mDisplayManager = new DisplayManagerService(mContext,
+ new DisplayManagerService.Injector() {
+ @Override
+ VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, Context context,
+ Handler handler, DisplayAdapter.Listener displayAdapterListener) {
+ return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
+ (String name, boolean secure) -> mMockDisplayToken);
+ }
+ });
+ mHandler = mDisplayManager.getDisplayHandler();
+
+ LocalServices.removeServiceForTest(InputManagerInternal.class);
+ LocalServices.addService(InputManagerInternal.class, mMockInputManagerInternal);
+ LocalServices.removeServiceForTest(WindowManagerInternal.class);
+ LocalServices.addService(WindowManagerInternal.class, mMockWindowManagerInternal);
+
+ mDisplayManager.systemReady(false /* safeMode */, false /* onlyCore */);
+ mDisplayManager.windowManagerAndInputReady();
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testCreateVirtualDisplay_sentToInputManager() throws Exception {
+ // This is effectively the DisplayManager service published to ServiceManager.
+ DisplayManagerService.BinderService bs = mDisplayManager.new BinderService();
+
+ String uniqueId = "uniqueId --- Test";
+ String uniqueIdPrefix = "virtual:" + mContext.getPackageName() + ":";
+ int width = 600;
+ int height = 800;
+ int dpi = 320;
+ int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH;
+
+ when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+ int displayId = bs.createVirtualDisplay(mMockAppToken /* callback */,
+ null /* projection */, "com.android.frameworks.servicestests",
+ "Test Virtual Display", width, height, dpi, null /* surface */, flags /* flags */,
+ uniqueId);
+
+ mDisplayManager.performTraversalInTransactionFromWindowManagerInternal();
+
+ // flush the handler
+ mHandler.runWithScissors(() -> {}, 0 /* now */);
+
+ ArgumentCaptor<List<DisplayViewport>> virtualViewportCaptor =
+ ArgumentCaptor.forClass(List.class);
+ verify(mMockInputManagerInternal).setDisplayViewports(
+ any(), any(), virtualViewportCaptor.capture());
+
+ assertEquals(1, virtualViewportCaptor.getValue().size());
+ DisplayViewport dv = virtualViewportCaptor.getValue().get(0);
+ assertEquals(height, dv.deviceHeight);
+ assertEquals(width, dv.deviceWidth);
+ assertEquals(uniqueIdPrefix + uniqueId, dv.uniqueId);
+ assertEquals(displayId, dv.displayId);
+ }
+}