summaryrefslogtreecommitdiffstats
path: root/core/tests
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2013-08-07 14:17:04 -0700
committerJeff Brown <jeffbrown@google.com>2013-08-07 17:58:47 -0700
commitf724c277d3362dbc8099fcbf8674609a424cd2ee (patch)
treec079ab8e3c1b04130613348680ab9cc78be2625f /core/tests
parent6876f3210764559a2fcccdd16079f7a66a871570 (diff)
downloadframeworks_base-f724c277d3362dbc8099fcbf8674609a424cd2ee.zip
frameworks_base-f724c277d3362dbc8099fcbf8674609a424cd2ee.tar.gz
frameworks_base-f724c277d3362dbc8099fcbf8674609a424cd2ee.tar.bz2
Add more virtual display tests.
We can't test everything in CTS because some features require system permissions. So this is another copy of the CTS test with more stuff that we can build with the system cert. Change-Id: Ied5a456a0810d38d307b6dfbad0f770cb480b4ee
Diffstat (limited to 'core/tests')
-rw-r--r--core/tests/coretests/AndroidManifest.xml6
-rw-r--r--core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java508
2 files changed, 513 insertions, 1 deletions
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index f8b26bc..a2cc40c 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -94,7 +94,7 @@
<uses-permission android:name="android.permission.MOVE_PACKAGE" />
<uses-permission android:name="android.permission.PACKAGE_VERIFICATION_AGENT" />
- <!--os storage test permissions -->
+ <!-- os storage test permissions -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.ASEC_ACCESS" />
<uses-permission android:name="android.permission.ASEC_CREATE" />
@@ -103,6 +103,10 @@
<uses-permission android:name="android.permission.ASEC_RENAME" />
<uses-permission android:name="android.permission.SHUTDOWN" />
+ <!-- virtual display test permissions -->
+ <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" />
+ <uses-permission android:name="android.permission.CAPTURE_SECURE_VIDEO_OUTPUT" />
+
<!-- accessibility test permissions -->
<uses-permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT" />
diff --git a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java
new file mode 100644
index 0000000..ebecf2e
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2013 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 android.hardware.display;
+
+import android.app.Presentation;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.drawable.ColorDrawable;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.Image;
+import android.media.ImageReader;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.test.AndroidTestCase;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Display;
+import android.view.Surface;
+import android.view.ViewGroup.LayoutParams;
+import android.view.WindowManager;
+import android.widget.ImageView;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Tests that applications can create virtual displays and present content on them.
+ *
+ * Contains additional tests that cannot be included in CTS because they require
+ * system permissions. See also the CTS version of VirtualDisplayTest.
+ */
+public class VirtualDisplayTest extends AndroidTestCase {
+ private static final String TAG = "VirtualDisplayTest";
+
+ private static final String NAME = TAG;
+ private static final int WIDTH = 720;
+ private static final int HEIGHT = 480;
+ private static final int DENSITY = DisplayMetrics.DENSITY_MEDIUM;
+ private static final int TIMEOUT = 10000;
+
+ // Colors that we use as a signal to determine whether some desired content was
+ // drawn. The colors themselves doesn't matter but we choose them to have with distinct
+ // values for each color channel so as to detect possible RGBA vs. BGRA buffer format issues.
+ // We should only observe RGBA buffers but some graphics drivers might incorrectly
+ // deliver BGRA buffers to virtual displays instead.
+ private static final int BLUEISH = 0xff1122ee;
+ private static final int GREENISH = 0xff33dd44;
+
+ private DisplayManager mDisplayManager;
+ private Handler mHandler;
+ private final Lock mImageReaderLock = new ReentrantLock(true /*fair*/);
+ private ImageReader mImageReader;
+ private Surface mSurface;
+ private ImageListener mImageListener;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mDisplayManager = (DisplayManager)mContext.getSystemService(Context.DISPLAY_SERVICE);
+ mHandler = new Handler(Looper.getMainLooper());
+ mImageListener = new ImageListener();
+
+ mImageReaderLock.lock();
+ try {
+ mImageReader = new ImageReader(WIDTH, HEIGHT, PixelFormat.RGBA_8888, 2);
+ mImageReader.setImageAvailableListener(mImageListener, mHandler);
+ mSurface = mImageReader.getSurface();
+ } finally {
+ mImageReaderLock.unlock();
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ mImageReaderLock.lock();
+ try {
+ mImageReader.close();
+ mImageReader = null;
+ mSurface = null;
+ } finally {
+ mImageReaderLock.unlock();
+ }
+ }
+
+ /**
+ * Ensures that an application can create a private virtual display and show
+ * its own windows on it.
+ */
+ public void testPrivateVirtualDisplay() throws Exception {
+ VirtualDisplay virtualDisplay = mDisplayManager.createVirtualDisplay(NAME,
+ WIDTH, HEIGHT, DENSITY, mSurface, 0);
+ assertNotNull("virtual display must not be null", virtualDisplay);
+
+ Display display = virtualDisplay.getDisplay();
+ try {
+ assertDisplayRegistered(display, Display.FLAG_PRIVATE);
+
+ // Show a private presentation on the display.
+ assertDisplayCanShowPresentation("private presentation window",
+ display, BLUEISH,
+ WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION, 0);
+ } finally {
+ virtualDisplay.release();
+ }
+ assertDisplayUnregistered(display);
+ }
+
+ /**
+ * Ensures that an application can create a private presentation virtual display and show
+ * its own windows on it.
+ */
+ public void testPrivatePresentationVirtualDisplay() throws Exception {
+ VirtualDisplay virtualDisplay = mDisplayManager.createVirtualDisplay(NAME,
+ WIDTH, HEIGHT, DENSITY, mSurface,
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION);
+ assertNotNull("virtual display must not be null", virtualDisplay);
+
+ Display display = virtualDisplay.getDisplay();
+ try {
+ assertDisplayRegistered(display, Display.FLAG_PRIVATE | Display.FLAG_PRESENTATION);
+
+ // Show a private presentation on the display.
+ assertDisplayCanShowPresentation("private presentation window",
+ display, BLUEISH,
+ WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION, 0);
+ } finally {
+ virtualDisplay.release();
+ }
+ assertDisplayUnregistered(display);
+ }
+
+ /**
+ * Ensures that an application can create a public virtual display and show
+ * its own windows on it. This test requires the CAPTURE_VIDEO_OUTPUT permission.
+ *
+ * Because this test does not have an activity token, we use the TOAST window
+ * type to create the window. Another choice might be SYSTEM_ALERT_WINDOW but
+ * that requires a permission.
+ */
+ public void testPublicPresentationVirtualDisplay() throws Exception {
+ VirtualDisplay virtualDisplay = mDisplayManager.createVirtualDisplay(NAME,
+ WIDTH, HEIGHT, DENSITY, mSurface,
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
+ | DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION);
+ assertNotNull("virtual display must not be null", virtualDisplay);
+
+ Display display = virtualDisplay.getDisplay();
+ try {
+ assertDisplayRegistered(display, Display.FLAG_PRESENTATION);
+
+ // Mirroring case.
+ // Show a window on the default display. It should be mirrored to the
+ // virtual display automatically.
+ Display defaultDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ assertDisplayCanShowPresentation("mirrored window",
+ defaultDisplay, GREENISH,
+ WindowManager.LayoutParams.TYPE_TOAST, 0);
+
+ // Mirroring case with secure window (but display is not secure).
+ // Show a window on the default display. It should be replaced with black on
+ // the virtual display.
+ assertDisplayCanShowPresentation("mirrored secure window on non-secure display",
+ defaultDisplay, Color.BLACK,
+ WindowManager.LayoutParams.TYPE_TOAST,
+ WindowManager.LayoutParams.FLAG_SECURE);
+
+ // Presentation case.
+ // Show a normal presentation on the display.
+ assertDisplayCanShowPresentation("presentation window",
+ display, BLUEISH,
+ WindowManager.LayoutParams.TYPE_TOAST, 0);
+
+ // Presentation case with secure window (but display is not secure).
+ // Show a normal presentation on the display. It should be replaced with black.
+ assertDisplayCanShowPresentation("secure presentation window on non-secure display",
+ display, Color.BLACK,
+ WindowManager.LayoutParams.TYPE_TOAST,
+ WindowManager.LayoutParams.FLAG_SECURE);
+ } finally {
+ virtualDisplay.release();
+ }
+ assertDisplayUnregistered(display);
+ }
+
+ /**
+ * Ensures that an application can create a secure public virtual display and show
+ * its own windows on it. This test requires the CAPTURE_SECURE_VIDEO_OUTPUT permission.
+ *
+ * Because this test does not have an activity token, we use the TOAST window
+ * type to create the window. Another choice might be SYSTEM_ALERT_WINDOW but
+ * that requires a permission.
+ */
+ public void testSecurePublicPresentationVirtualDisplay() throws Exception {
+ VirtualDisplay virtualDisplay = mDisplayManager.createVirtualDisplay(NAME,
+ WIDTH, HEIGHT, DENSITY, mSurface,
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE
+ | DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
+ | DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION);
+ assertNotNull("virtual display must not be null", virtualDisplay);
+
+ Display display = virtualDisplay.getDisplay();
+ try {
+ assertDisplayRegistered(display, Display.FLAG_PRESENTATION | Display.FLAG_SECURE);
+
+ // Mirroring case with secure window (and display is secure).
+ // Show a window on the default display. It should be mirrored to the
+ // virtual display automatically.
+ Display defaultDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ assertDisplayCanShowPresentation("mirrored secure window on secure display",
+ defaultDisplay, GREENISH,
+ WindowManager.LayoutParams.TYPE_TOAST,
+ WindowManager.LayoutParams.FLAG_SECURE);
+
+ // Presentation case with secure window (and display is secure).
+ // Show a normal presentation on the display.
+ assertDisplayCanShowPresentation("secure presentation window on secure display",
+ display, BLUEISH,
+ WindowManager.LayoutParams.TYPE_TOAST,
+ WindowManager.LayoutParams.FLAG_SECURE);
+ } finally {
+ virtualDisplay.release();
+ }
+ assertDisplayUnregistered(display);
+ }
+
+ private void assertDisplayRegistered(Display display, int flags) {
+ assertNotNull("display object must not be null", display);
+ assertTrue("display must be valid", display.isValid());
+ assertTrue("display id must be unique",
+ display.getDisplayId() != Display.DEFAULT_DISPLAY);
+ assertEquals("display must have correct flags", flags, display.getFlags());
+ assertEquals("display name must match supplied name", NAME, display.getName());
+ Point size = new Point();
+ display.getSize(size);
+ assertEquals("display width must match supplied width", WIDTH, size.x);
+ assertEquals("display height must match supplied height", HEIGHT, size.y);
+ assertEquals("display rotation must be 0",
+ Surface.ROTATION_0, display.getRotation());
+ assertNotNull("display must be registered",
+ findDisplay(mDisplayManager.getDisplays(), NAME));
+
+ if ((flags & Display.FLAG_PRESENTATION) != 0) {
+ assertNotNull("display must be registered as a presentation display",
+ findDisplay(mDisplayManager.getDisplays(
+ DisplayManager.DISPLAY_CATEGORY_PRESENTATION), NAME));
+ } else {
+ assertNull("display must not be registered as a presentation display",
+ findDisplay(mDisplayManager.getDisplays(
+ DisplayManager.DISPLAY_CATEGORY_PRESENTATION), NAME));
+ }
+ }
+
+ private void assertDisplayUnregistered(Display display) {
+ assertNull("display must no longer be registered after being removed",
+ findDisplay(mDisplayManager.getDisplays(), NAME));
+ assertFalse("display must no longer be valid", display.isValid());
+ }
+
+ private void assertDisplayCanShowPresentation(String message, final Display display,
+ final int color, final int windowType, final int windowFlags) {
+ // At this point, we should not have seen any blue.
+ assertTrue(message + ": display should not show content before window is shown",
+ mImageListener.getColor() != color);
+
+ final TestPresentation[] presentation = new TestPresentation[1];
+ try {
+ // Show the presentation.
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ presentation[0] = new TestPresentation(getContext(), display,
+ color, windowType, windowFlags);
+ presentation[0].show();
+ }
+ });
+
+ // Wait for the blue to be seen.
+ assertTrue(message + ": display should show content after window is shown",
+ mImageListener.waitForColor(color, TIMEOUT));
+ } finally {
+ if (presentation[0] != null) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ presentation[0].dismiss();
+ }
+ });
+ }
+ }
+ }
+
+ private void runOnUiThread(Runnable runnable) {
+ Runnable waiter = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (this) {
+ notifyAll();
+ }
+ }
+ };
+ synchronized (waiter) {
+ mHandler.post(runnable);
+ mHandler.post(waiter);
+ try {
+ waiter.wait(TIMEOUT);
+ } catch (InterruptedException ex) {
+ }
+ }
+ }
+
+ private Display findDisplay(Display[] displays, String name) {
+ for (int i = 0; i < displays.length; i++) {
+ if (displays[i].getName().equals(name)) {
+ return displays[i];
+ }
+ }
+ return null;
+ }
+
+ private final class TestPresentation extends Presentation {
+ private final int mColor;
+ private final int mWindowType;
+ private final int mWindowFlags;
+
+ public TestPresentation(Context context, Display display,
+ int color, int windowType, int windowFlags) {
+ super(context, display);
+ mColor = color;
+ mWindowType = windowType;
+ mWindowFlags = windowFlags;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setTitle(TAG);
+ getWindow().setType(mWindowType);
+ getWindow().addFlags(mWindowFlags);
+
+ // Create a solid color image to use as the content of the presentation.
+ ImageView view = new ImageView(getContext());
+ view.setImageDrawable(new ColorDrawable(mColor));
+ view.setLayoutParams(new LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+ setContentView(view);
+ }
+ }
+
+ /**
+ * Watches for an image with a large amount of some particular solid color to be shown.
+ */
+ private final class ImageListener
+ implements ImageReader.OnImageAvailableListener {
+ private int mColor = -1;
+
+ public int getColor() {
+ synchronized (this) {
+ return mColor;
+ }
+ }
+
+ public boolean waitForColor(int color, long timeoutMillis) {
+ long timeoutTime = SystemClock.uptimeMillis() + timeoutMillis;
+ synchronized (this) {
+ while (mColor != color) {
+ long now = SystemClock.uptimeMillis();
+ if (now >= timeoutTime) {
+ return false;
+ }
+ try {
+ wait(timeoutTime - now);
+ } catch (InterruptedException ex) {
+ }
+ }
+ return true;
+ }
+ }
+
+ @Override
+ public void onImageAvailable(ImageReader reader) {
+ mImageReaderLock.lock();
+ try {
+ if (reader != mImageReader) {
+ return;
+ }
+
+ Log.d(TAG, "New image available from virtual display.");
+ Image image = reader.getNextImage();
+ if (image != null) {
+ try {
+ // Get the latest buffer.
+ for (;;) {
+ Image nextImage = reader.getNextImage();
+ if (nextImage == null) {
+ break;
+ }
+ reader.releaseImage(image);
+ image = nextImage;
+ }
+
+ // Scan for colors.
+ int color = scanImage(image);
+ synchronized (this) {
+ if (mColor != color) {
+ mColor = color;
+ notifyAll();
+ }
+ }
+ } finally {
+ reader.releaseImage(image);
+ }
+ }
+ } finally {
+ mImageReaderLock.unlock();
+ }
+ }
+
+ private int scanImage(Image image) {
+ final Image.Plane plane = image.getPlanes()[0];
+ final ByteBuffer buffer = plane.getBuffer();
+ final int width = image.getWidth();
+ final int height = image.getHeight();
+ final int pixelStride = plane.getPixelStride();
+ final int rowStride = plane.getRowStride();
+ final int rowPadding = rowStride - pixelStride * width;
+
+ Log.d(TAG, "- Scanning image: width=" + width + ", height=" + height
+ + ", pixelStride=" + pixelStride + ", rowStride=" + rowStride);
+
+ int offset = 0;
+ int blackPixels = 0;
+ int bluePixels = 0;
+ int greenPixels = 0;
+ int otherPixels = 0;
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ int pixel = 0;
+ pixel |= (buffer.get(offset) & 0xff) << 16; // R
+ pixel |= (buffer.get(offset + 1) & 0xff) << 8; // G
+ pixel |= (buffer.get(offset + 2) & 0xff); // B
+ pixel |= (buffer.get(offset + 3) & 0xff) << 24; // A
+ if (pixel == Color.BLACK || pixel == 0) {
+ blackPixels += 1;
+ } else if (pixel == BLUEISH) {
+ bluePixels += 1;
+ } else if (pixel == GREENISH) {
+ greenPixels += 1;
+ } else {
+ otherPixels += 1;
+ if (otherPixels < 10) {
+ Log.d(TAG, "- Found unexpected color: " + Integer.toHexString(pixel));
+ }
+ }
+ offset += pixelStride;
+ }
+ offset += rowPadding;
+ }
+
+ // Return a color if it represents more than one quarter of the pixels.
+ // We use this threshold in case the display is being letterboxed when
+ // mirroring so there might be large black bars on the sides, which is normal.
+ Log.d(TAG, "- Pixels: " + blackPixels + " black, "
+ + bluePixels + " blue, "
+ + greenPixels + " green, "
+ + otherPixels + " other");
+ final int threshold = width * height / 4;
+ if (bluePixels > threshold) {
+ Log.d(TAG, "- Reporting blue.");
+ return BLUEISH;
+ }
+ if (greenPixels > threshold) {
+ Log.d(TAG, "- Reporting green.");
+ return GREENISH;
+ }
+ if (blackPixels > threshold) {
+ Log.d(TAG, "- Reporting black.");
+ return Color.BLACK;
+ }
+ Log.d(TAG, "- Reporting other.");
+ return -1;
+ }
+ }
+}
+