Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 975c00d8 authored by Santos Cordon's avatar Santos Cordon
Browse files

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
parent 63d98310
Loading
Loading
Loading
Loading
+117 −37
Original line number Diff line number Diff line

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,12 +207,22 @@ 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");
        }

        synchronized (vdLock) {
        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;
@@ -193,3 +230,46 @@ class CompatibilityDisplay {
                        }
                    }
               }
           };
        }

        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.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());
        }
    }
}