From 975c00d8ffeeb72a05bc229e0e1234e771c4a824 Mon Sep 17 00:00:00 2001 From: Santos Cordon Date: Tue, 21 Mar 2017 17:05:36 -0700 Subject: [PATCH] Add a default Surface to the VR Virtual Display. Uses ImageReader as a do-nothing Surface to ensure the Virtual Display gets passed through to SurfaceFlinger. Without a Surface, the Virtual Display never makes it through the android system to SurfaceFlinger. This change is needed to get the Virtual Display recognized in the Flinger layers. Test: 'setprop vr_virtualdisplay true' to enable virtual displays. 'setprop vr_debug_vd_receiver true' and shell restart for the test app. Bug: 36491910 Change-Id: I2bf2cb4035dcf04484b7fb0bec61671864069335 --- .../android/server/vr/CompatibilityDisplay.java | 154 ++++++++++++++++----- 1 file changed, 117 insertions(+), 37 deletions(-) diff --git a/services/core/java/com/android/server/vr/CompatibilityDisplay.java b/services/core/java/com/android/server/vr/CompatibilityDisplay.java index 8f95cc74c914..a8d622353636 100644 --- a/services/core/java/com/android/server/vr/CompatibilityDisplay.java +++ b/services/core/java/com/android/server/vr/CompatibilityDisplay.java @@ -1,4 +1,3 @@ - package com.android.server.vr; import static android.view.Display.INVALID_DISPLAY; @@ -8,14 +7,18 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.graphics.ImageFormat; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; +import android.media.ImageReader; import android.os.Build; import android.os.Handler; import android.os.IBinder; +import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; -import android.service.vr.IVrStateCallbacks; +import android.os.SystemProperties; +import android.service.vr.IPersistentVrStateCallbacks; import android.service.vr.IVrManager; import android.util.Log; import android.view.Surface; @@ -34,11 +37,12 @@ class CompatibilityDisplay { private final static int HEIGHT = 1800; private final static int WIDTH = 1400; private final static int DPI = 320; + private final static int STOP_VIRTUAL_DISPLAY_DELAY_MILLIS = 2000; private final static String DEBUG_ACTION_SET_MODE = "com.android.server.vr.CompatibilityDisplay.SET_MODE"; private final static String DEBUG_EXTRA_MODE_ON = - "com.android.servier.vr.CompatibilityDisplay.EXTRA_MODE_ON"; + "com.android.server.vr.CompatibilityDisplay.EXTRA_MODE_ON"; private final static String DEBUG_ACTION_SET_SURFACE = "com.android.server.vr.CompatibilityDisplay.SET_SURFACE"; private final static String DEBUG_EXTRA_SURFACE = @@ -46,14 +50,16 @@ class CompatibilityDisplay { private final DisplayManager mDisplayManager; private final IVrManager mVrManager; + private final Object mVdLock = new Object(); + private final Handler mHandler = new Handler(); - // TODO: Lock initially created when VrStateCallback was connected through Binder. This may not - // be necessary with the direct access to VrManager. - private final Object vdLock = new Object(); - - private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() { + /** + * Callback implementation to receive changes to VrMode. + **/ + private final IPersistentVrStateCallbacks mVrStateCallbacks = + new IPersistentVrStateCallbacks.Stub() { @Override - public void onVrStateChanged(boolean enabled) { + public void onPersistentVrStateChanged(boolean enabled) { if (enabled != mIsVrModeEnabled) { mIsVrModeEnabled = enabled; updateVirtualDisplay(); @@ -63,7 +69,9 @@ class CompatibilityDisplay { private VirtualDisplay mVirtualDisplay; private Surface mSurface; - private boolean mIsDebugOverrideEnabled; + private ImageReader mImageReader; + private Runnable mStopVDRunnable; + private boolean mIsVrModeOverrideEnabled; private boolean mIsVrModeEnabled; public CompatibilityDisplay(DisplayManager displayManager, IVrManager vrManager) { @@ -79,13 +87,23 @@ class CompatibilityDisplay { startDebugOnlyBroadcastReceiver(context); } + /** + * Creates and Destroys the virtual display depending on the current state of VrMode. + */ private void updateVirtualDisplay() { - if (mIsVrModeEnabled || (DEBUG && mIsDebugOverrideEnabled)) { + boolean createVirtualDisplay = "true".equals(SystemProperties.get("vr_virtualdisplay")); + if (DEBUG) { + Log.i(TAG, "isVrMode: " + mIsVrModeEnabled + ", createVD: " + createVirtualDisplay + + ", override: " + mIsVrModeOverrideEnabled); + } + + if (mIsVrModeEnabled || (createVirtualDisplay && mIsVrModeOverrideEnabled)) { // TODO: Consider not creating the display until ActivityManager needs one on // which to display a 2D application. - // TODO: STOPSHIP Remove DEBUG conditional before launching. - if (DEBUG) { + // TODO: STOPSHIP Remove createVirtualDisplay conditional before launching. + if (createVirtualDisplay) { startVirtualDisplay(); + startImageReader(); } } else { // Stop virtual display to test exit condition @@ -93,8 +111,17 @@ class CompatibilityDisplay { } } + /** + * Creates a DEBUG-only BroadcastReceiver through which a test app can simulate VrMode and + * set a custom Surface for the virtual display. This allows testing of the virtual display + * without going into full 3D. + * + * @param context The context. + */ private void startDebugOnlyBroadcastReceiver(Context context) { - if (DEBUG) { + // STOPSHIP: remove vr_debug_vd_receiver test. + boolean debugBroadcast = "true".equals(SystemProperties.get("vr_debug_vd_receiver")); + if (DEBUG || debugBroadcast) { IntentFilter intentFilter = new IntentFilter(DEBUG_ACTION_SET_MODE); intentFilter.addAction(DEBUG_ACTION_SET_SURFACE); @@ -103,21 +130,13 @@ class CompatibilityDisplay { public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (DEBUG_ACTION_SET_MODE.equals(action)) { - mIsDebugOverrideEnabled = + mIsVrModeOverrideEnabled = intent.getBooleanExtra(DEBUG_EXTRA_MODE_ON, false); updateVirtualDisplay(); } else if (DEBUG_ACTION_SET_SURFACE.equals(action)) { if (mVirtualDisplay != null) { - final Surface newSurface = - intent.getParcelableExtra(DEBUG_EXTRA_SURFACE); - - Log.i(TAG, "Setting the new surface from " + mSurface + " to " + newSurface); - if (newSurface != mSurface) { - mVirtualDisplay.setSurface(newSurface); - if (mSurface != null) { - mSurface.release(); - } - mSurface = newSurface; + if (intent.hasExtra(DEBUG_EXTRA_SURFACE)) { + setSurfaceLocked(intent.getParcelableExtra(DEBUG_EXTRA_SURFACE)); } } else { Log.w(TAG, "Cannot set the surface because the VD is null."); @@ -128,18 +147,27 @@ class CompatibilityDisplay { } } + /** + * Starts listening to VrMode changes. + */ private void startVrModeListener() { if (mVrManager != null) { try { - mVrManager.registerListener(mVrStateCallbacks); + mVrManager.registerPersistentVrStateListener(mVrStateCallbacks); } catch (RemoteException e) { Log.e(TAG, "Could not register VR State listener.", e); } } } + /** + * Returns the virtual display ID if one currently exists, otherwise returns + * {@link INVALID_DISPLAY_ID}. + * + * @return The virtual display ID. + */ public int getVirtualDisplayId() { - synchronized(vdLock) { + synchronized(mVdLock) { if (mVirtualDisplay != null) { int virtualDisplayId = mVirtualDisplay.getDisplay().getDisplayId(); if (DEBUG) { @@ -151,6 +179,9 @@ class CompatibilityDisplay { return INVALID_DISPLAY; } + /** + * Starts the virtual display if one does not already exist. + */ private void startVirtualDisplay() { if (DEBUG) { Log.d(TAG, "Request to start VD, DM:" + mDisplayManager); @@ -161,7 +192,7 @@ class CompatibilityDisplay { return; } - synchronized (vdLock) { + synchronized (mVdLock) { if (mVirtualDisplay != null) { Log.i(TAG, "VD already exists, ignoring request"); return; @@ -169,10 +200,6 @@ class CompatibilityDisplay { mVirtualDisplay = mDisplayManager.createVirtualDisplay("VR 2D Display", WIDTH, HEIGHT, DPI, null /* Surface */, 0 /* flags */); - if (mVirtualDisplay != null && mSurface != null && mSurface.isValid()) { - // TODO: Need to protect all setSurface calls with a lock. - mVirtualDisplay.setSurface(mSurface); - } } if (DEBUG) { @@ -180,16 +207,69 @@ class CompatibilityDisplay { } } + /** + * Stops the virtual display with a {@link #STOP_VIRTUAL_DISPLAY_DELAY_MILLIS} timeout. + * The timeout prevents the virtual display from bouncing in cases where VrMode goes in and out + * of being enabled. This can happen sometimes with our 2D test app. + */ private void stopVirtualDisplay() { - if (DEBUG) { - Log.i(TAG, "Santos, stopping VD"); + if (mStopVDRunnable == null) { + mStopVDRunnable = new Runnable() { + @Override + public void run() { + if (mIsVrModeEnabled) { + Log.i(TAG, "Virtual Display destruction stopped: VrMode is back on."); + } else { + Log.i(TAG, "Stopping Virtual Display"); + synchronized (mVdLock) { + setSurfaceLocked(null); // clean up and release the surface first. + if (mVirtualDisplay != null) { + mVirtualDisplay.release(); + mVirtualDisplay = null; + } + } + } + } + }; } - synchronized (vdLock) { + mHandler.removeCallbacks(mStopVDRunnable); + mHandler.postDelayed(mStopVDRunnable, STOP_VIRTUAL_DISPLAY_DELAY_MILLIS); + } + + /** + * Set the surface to use with the virtual display. + * + * Code should be locked by {@link #mVdLock} before invoked. + * + * @param surface The Surface to set. + */ + private void setSurfaceLocked(Surface surface) { + // Change the surface to either a valid surface or a null value. + if (mSurface != surface && (surface == null || surface.isValid())) { + Log.i(TAG, "Setting the new surface from " + mSurface + " to " + surface); if (mVirtualDisplay != null) { - mVirtualDisplay.release(); - mVirtualDisplay = null; + mVirtualDisplay.setSurface(surface); + } + if (mSurface != null) { + mSurface.release(); } + mSurface = surface; + } + } + + /** + * Starts an ImageReader as a do-nothing Surface. The virtual display will not get fully + * initialized within surface flinger unless it has a valid Surface associated with it. We use + * the ImageReader as the default valid Surface. + */ + private void startImageReader() { + if (mImageReader == null) { + mImageReader = ImageReader.newInstance(WIDTH, HEIGHT, ImageFormat.RAW_PRIVATE, + 2 /* maxImages */); + } + synchronized (mVdLock) { + setSurfaceLocked(mImageReader.getSurface()); } } } -- 2.11.0