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

Commit 9302251e authored by Jeff Brown's avatar Jeff Brown Committed by Android (Google) Code Review
Browse files

Merge "Reduce screen on latency, eliminate flashes." into jb-mr1-dev

parents ec799e88 8b9cf1c8
Loading
Loading
Loading
Loading
+45 −27
Original line number Diff line number Diff line
@@ -271,6 +271,12 @@ final class DisplayPowerController {
    // When the screen turns on again, we report user activity to the power manager.
    private boolean mScreenOffBecauseOfProximity;

    // True if the screen on is being blocked.
    private boolean mScreenOnWasBlocked;

    // The elapsed real time when the screen on was blocked.
    private long mScreenOnBlockStartRealTime;

    // Set to true if the light sensor is enabled.
    private boolean mLightSensorEnabled;

@@ -513,7 +519,7 @@ final class DisplayPowerController {
        final Executor executor = AsyncTask.THREAD_POOL_EXECUTOR;
        Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
        mPowerState = new DisplayPowerState(
                mElectronBeamAnimatesBacklightConfig ? null : new ElectronBeam(display),
                new ElectronBeam(display),
                new PhotonicModulator(executor,
                        mLights.getLight(LightsService.LIGHT_ID_BACKLIGHT),
                        mSuspendBlocker));
@@ -553,7 +559,6 @@ final class DisplayPowerController {
        final boolean mustNotify;
        boolean mustInitialize = false;
        boolean updateAutoBrightness = mTwilightChanged;
        boolean screenOnWasBlocked = false;
        mTwilightChanged = false;

        synchronized (mLock) {
@@ -662,18 +667,24 @@ final class DisplayPowerController {
                // It is relatively short but if we cancel it and switch to the
                // on animation immediately then the results are pretty ugly.
                if (!mElectronBeamOffAnimator.isStarted()) {
                    if (mPowerRequest.blockScreenOn && !mPowerState.isScreenOn()) {
                        if (DEBUG) {
                            Slog.d(TAG, "Blocked screen on while screen currently off.");
                        }
                        screenOnWasBlocked = true;
                    } else {
                    // Turn the screen on.  The contents of the screen may not yet
                    // be visible if the electron beam has not been dismissed because
                    // its last frame of animation is solid black.
                    setScreenOn(true);

                    if (mPowerRequest.blockScreenOn
                            && mPowerState.getElectronBeamLevel() == 0.0f) {
                        blockScreenOn();
                    } else {
                        unblockScreenOn();
                        if (USE_ELECTRON_BEAM_ON_ANIMATION) {
                            if (!mElectronBeamOnAnimator.isStarted()) {
                                if (mPowerState.getElectronBeamLevel() == 1.0f) {
                                    mPowerState.dismissElectronBeam();
                                } else if (mPowerState.prepareElectronBeam(true)) {
                                } else if (mPowerState.prepareElectronBeam(
                                        mElectronBeamAnimatesBacklightConfig ?
                                                ElectronBeam.MODE_BLANK :
                                                        ElectronBeam.MODE_WARM_UP)) {
                                    mElectronBeamOnAnimator.start();
                                } else {
                                    mElectronBeamOnAnimator.end();
@@ -684,22 +695,6 @@ final class DisplayPowerController {
                            mPowerState.dismissElectronBeam();
                        }
                    }
                } else {
                    // FIXME: If the electron beam off animation is playing then we have a bit
                    // of a problem.  The window manager policy would only have requested
                    // to block screen on if it was about to start preparing the keyguard.
                    // It's already too late to do anything about that.  Ideally we would
                    // let the animation play out first but that would require making
                    // some pretty deep changes to the power manager and we don't have
                    // time just now.  For now, short-circuit the animation and get ready.
                    if (mPowerRequest.blockScreenOn) {
                        if (DEBUG) {
                            Slog.d(TAG, "Blocked screen on while screen off animation running.");
                        }
                        screenOnWasBlocked = true;
                        setScreenOn(false);
                        mElectronBeamOffAnimator.end();
                    }
                }
            } else {
                // Want screen off.
@@ -708,7 +703,10 @@ final class DisplayPowerController {
                    if (!mElectronBeamOffAnimator.isStarted()) {
                        if (mPowerState.getElectronBeamLevel() == 0.0f) {
                            setScreenOn(false);
                        } else if (mPowerState.prepareElectronBeam(false)
                        } else if (mPowerState.prepareElectronBeam(
                                mElectronBeamAnimatesBacklightConfig ?
                                        ElectronBeam.MODE_BLANK :
                                                ElectronBeam.MODE_COOL_DOWN)
                                && mPowerState.isScreenOn()) {
                            mElectronBeamOffAnimator.start();
                        } else {
@@ -723,7 +721,7 @@ final class DisplayPowerController {
        // We mostly care about the screen state here, ignoring brightness changes
        // which will be handled asynchronously.
        if (mustNotify
                && !screenOnWasBlocked
                && !mScreenOnWasBlocked
                && !mElectronBeamOnAnimator.isStarted()
                && !mElectronBeamOffAnimator.isStarted()
                && mPowerState.waitUntilClean(mCleanListener)) {
@@ -740,6 +738,26 @@ final class DisplayPowerController {
        }
    }

    private void blockScreenOn() {
        if (!mScreenOnWasBlocked) {
            mScreenOnWasBlocked = true;
            if (DEBUG) {
                Slog.d(TAG, "Blocked screen on.");
                mScreenOnBlockStartRealTime = SystemClock.elapsedRealtime();
            }
        }
    }

    private void unblockScreenOn() {
        if (mScreenOnWasBlocked) {
            mScreenOnWasBlocked = false;
            if (DEBUG) {
                Slog.d(TAG, "Unblocked screen on after " +
                        (SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime) + " ms");
            }
        }
    }

    private void setScreenOn(boolean on) {
        if (!mPowerState.isScreenOn() == on) {
            mPowerState.setScreenOn(on);
+8 −5
Original line number Diff line number Diff line
@@ -52,11 +52,14 @@ final class DisplayPowerRequest {
    // If true, enables automatic brightness control.
    public boolean useAutoBrightness;

    // If true, prevents the screen from turning on if it is currently off.
    // The display does not enter a "ready" state if this flag is true and the screen
    // is off and is being prevented from turning on.  The window manager policy blocks
    // screen on while it prepares the keyguard to prevent the user from seeing
    // intermediate updates.
    // If true, prevents the screen from completely turning on if it is currently off.
    // The display does not enter a "ready" state if this flag is true and screen on is
    // blocked.  The window manager policy blocks screen on while it prepares the keyguard to
    // prevent the user from seeing intermediate updates.
    //
    // Technically, we may not block the screen itself from turning on (because that introduces
    // extra unnecessary latency) but we do prevent content on screen from becoming
    // visible to the user.
    public boolean blockScreenOn;

    public DisplayPowerRequest() {
+8 −17
Original line number Diff line number Diff line
@@ -50,7 +50,7 @@ final class DisplayPowerState {
    private static final int DIRTY_BRIGHTNESS = 1 << 2;

    private final Choreographer mChoreographer;
    private final ElectronBeam mElectronBeam; // may be null if only animating backlights
    private final ElectronBeam mElectronBeam;
    private final PhotonicModulator mScreenBrightnessModulator;

    private int mDirty;
@@ -130,27 +130,20 @@ final class DisplayPowerState {
     * This method should be called before starting an animation because it
     * can take a fair amount of time to prepare the electron beam surface.
     *
     * @param warmUp True if the electron beam should start warming up.
     * @param mode The electron beam animation mode to prepare.
     * @return True if the electron beam was prepared.
     */
    public boolean prepareElectronBeam(boolean warmUp) {
        if (mElectronBeam != null) {
            boolean success = mElectronBeam.prepare(warmUp);
    public boolean prepareElectronBeam(int mode) {
        invalidate(DIRTY_ELECTRON_BEAM);
            return success;
        } else {
            return true;
        }
        return mElectronBeam.prepare(mode);
    }

    /**
     * Dismisses the electron beam surface.
     */
    public void dismissElectronBeam() {
        if (mElectronBeam != null) {
        mElectronBeam.dismiss();
    }
    }

    /**
     * Sets the level of the electron beam steering current.
@@ -230,10 +223,8 @@ final class DisplayPowerState {
        pw.println("  mScreenBrightness=" + mScreenBrightness);
        pw.println("  mElectronBeamLevel=" + mElectronBeamLevel);

        if (mElectronBeam != null) {
        mElectronBeam.dump(pw);
    }
    }

    private void invalidate(int dirty) {
        if (mDirty == 0) {
@@ -251,7 +242,7 @@ final class DisplayPowerState {
                PowerManagerService.nativeSetScreenState(false);
            }

            if ((mDirty & DIRTY_ELECTRON_BEAM) != 0 && mElectronBeam != null) {
            if ((mDirty & DIRTY_ELECTRON_BEAM) != 0) {
                mElectronBeam.draw(mElectronBeamLevel);
            }

+54 −20
Original line number Diff line number Diff line
@@ -26,7 +26,6 @@ import android.opengl.EGLSurface;
import android.opengl.GLES10;
import android.opengl.GLUtils;
import android.os.Looper;
import android.os.Process;
import android.util.FloatMath;
import android.util.Slog;
import android.view.Display;
@@ -41,12 +40,13 @@ import java.nio.FloatBuffer;

/**
 * Bzzzoooop!  *crackle*
 *
 * <p>
 * Animates a screen transition from on to off or off to on by applying
 * some GL transformations to a screenshot.
 *
 * </p><p>
 * This component must only be created or accessed by the {@link Looper} thread
 * that belongs to the {@link DisplayPowerController}.
 * </p>
 */
final class ElectronBeam {
    private static final String TAG = "ElectronBeam";
@@ -65,7 +65,7 @@ final class ElectronBeam {

    // Set to true when the animation context has been fully prepared.
    private boolean mPrepared;
    private boolean mWarmUp;
    private int mMode;

    private final Display mDisplay;
    private final DisplayInfo mDisplayInfo = new DisplayInfo();
@@ -90,6 +90,10 @@ final class ElectronBeam {
    private final FloatBuffer mVertexBuffer = createNativeFloatBuffer(8);
    private final FloatBuffer mTexCoordBuffer = createNativeFloatBuffer(8);

    public static final int MODE_WARM_UP = 0;
    public static final int MODE_COOL_DOWN = 1;
    public static final int MODE_BLANK = 2;

    public ElectronBeam(Display display) {
        mDisplay = display;
    }
@@ -98,16 +102,15 @@ final class ElectronBeam {
     * Warms up the electron beam in preparation for turning on or off.
     * This method prepares a GL context, and captures a screen shot.
     *
     * @param warmUp True if the electron beam is about to be turned on, false if
     * it is about to be turned off.
     * @param mode The desired mode for the upcoming animation.
     * @return True if the electron beam is ready, false if it is uncontrollable.
     */
    public boolean prepare(boolean warmUp) {
    public boolean prepare(int mode) {
        if (DEBUG) {
            Slog.d(TAG, "prepare: warmUp=" + warmUp);
            Slog.d(TAG, "prepare: mode=" + mode);
        }

        mWarmUp = warmUp;
        mMode = mode;

        // Get the display size and adjust it for rotation.
        mDisplay.getDisplayInfo(mDisplayInfo);
@@ -123,17 +126,28 @@ final class ElectronBeam {
        }

        // Prepare the surface for drawing.
        if (!createEglContext()
                || !createEglSurface()
                || !captureScreenshotTextureAndSetViewport()) {
        if (!tryPrepare()) {
            dismiss();
            return false;
        }

        // Done.
        mPrepared = true;
        return true;
    }

    private boolean tryPrepare() {
        if (createSurface()) {
            if (mMode == MODE_BLANK) {
                return true;
            }
            return createEglContext()
                    && createEglSurface()
                    && captureScreenshotTextureAndSetViewport();
        }
        return false;
    }

    /**
     * Dismisses the electron beam animation surface and cleans up.
     *
@@ -148,6 +162,7 @@ final class ElectronBeam {

        destroyScreenshotTexture();
        destroyEglSurface();
        destroySurface();
        mPrepared = false;
    }

@@ -163,6 +178,14 @@ final class ElectronBeam {
            Slog.d(TAG, "drawFrame: level=" + level);
        }

        if (!mPrepared) {
            return false;
        }

        if (mMode == MODE_BLANK) {
            return showSurface(1.0f - level);
        }

        if (!attachEglContext()) {
            return false;
        }
@@ -185,8 +208,7 @@ final class ElectronBeam {
        } finally {
            detachEglContext();
        }

        return showEglSurface();
        return showSurface(1.0f);
    }

    /**
@@ -217,7 +239,7 @@ final class ElectronBeam {
        // bind texture and set blending for drawing planes
        GLES10.glBindTexture(GLES10.GL_TEXTURE_2D, mTexNames[0]);
        GLES10.glTexEnvx(GLES10.GL_TEXTURE_ENV, GLES10.GL_TEXTURE_ENV_MODE,
                mWarmUp ? GLES10.GL_MODULATE : GLES10.GL_REPLACE);
                mMode == MODE_WARM_UP ? GLES10.GL_MODULATE : GLES10.GL_REPLACE);
        GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
                GLES10.GL_TEXTURE_MAG_FILTER, GLES10.GL_LINEAR);
        GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D,
@@ -251,7 +273,7 @@ final class ElectronBeam {
        GLES10.glColorMask(true, true, true, true);

        // draw the white highlight (we use the last vertices)
        if (!mWarmUp) {
        if (mMode == MODE_COOL_DOWN) {
            GLES10.glColor4f(ag, ag, ag, 1.0f);
            GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4);
        }
@@ -472,7 +494,7 @@ final class ElectronBeam {
        }
    }*/

    private boolean createEglSurface() {
    private boolean createSurface() {
        if (mSurfaceSession == null) {
            mSurfaceSession = new SurfaceSession();
        }
@@ -481,9 +503,15 @@ final class ElectronBeam {
        try {
            if (mSurface == null) {
                try {
                    int flags;
                    if (mMode == MODE_BLANK) {
                        flags = Surface.FX_SURFACE_DIM | Surface.HIDDEN;
                    } else {
                        flags = Surface.OPAQUE | Surface.HIDDEN;
                    }
                    mSurface = new Surface(mSurfaceSession,
                            "ElectronBeam", mDisplayWidth, mDisplayHeight,
                            PixelFormat.OPAQUE, Surface.OPAQUE | Surface.HIDDEN);
                            PixelFormat.OPAQUE, flags);
                } catch (Surface.OutOfResourcesException ex) {
                    Slog.e(TAG, "Unable to create surface.", ex);
                    return false;
@@ -514,7 +542,10 @@ final class ElectronBeam {
        } finally {
            Surface.closeTransaction();
        }
        return true;
    }

    private boolean createEglSurface() {
        if (mEglSurface == null) {
            int[] eglSurfaceAttribList = new int[] {
                    EGL14.EGL_NONE
@@ -536,7 +567,9 @@ final class ElectronBeam {
            }
            mEglSurface = null;
        }
    }

    private void destroySurface() {
        if (mSurface != null) {
            Surface.openTransaction();
            try {
@@ -549,11 +582,12 @@ final class ElectronBeam {
        }
    }

    private boolean showEglSurface() {
    private boolean showSurface(float alpha) {
        if (!mSurfaceVisible) {
            Surface.openTransaction();
            try {
                mSurface.setLayer(ELECTRON_BEAM_LAYER);
                mSurface.setAlpha(alpha);
                mSurface.show();
            } finally {
                Surface.closeTransaction();
@@ -643,7 +677,7 @@ final class ElectronBeam {
        pw.println();
        pw.println("Electron Beam State:");
        pw.println("  mPrepared=" + mPrepared);
        pw.println("  mWarmUp=" + mWarmUp);
        pw.println("  mMode=" + mMode);
        pw.println("  mDisplayLayerStack=" + mDisplayLayerStack);
        pw.println("  mDisplayRotation=" + mDisplayRotation);
        pw.println("  mDisplayWidth=" + mDisplayWidth);
+1 −1
Original line number Diff line number Diff line
@@ -18,7 +18,7 @@ package com.android.server.power;

/**
 * Low-level screen on blocker mechanism which is used to keep the screen off
 * until the window manager is ready to show new content.
 * or the contents of the screen hidden until the window manager is ready to show new content.
 */
interface ScreenOnBlocker {
    /**