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

Commit 4fe4a600 authored by Jeff Brown's avatar Jeff Brown Committed by Android Git Automerger
Browse files

am e0ba56f2: am 8d48e401: am d6e3ad54: Merge "Reduce screen on/off latency." into jb-mr1-dev

* commit 'e0ba56f2':
  Reduce screen on/off latency.
parents 4c8c2aea e0ba56f2
Loading
Loading
Loading
Loading
+3 −13
Original line number Original line Diff line number Diff line
@@ -30,7 +30,6 @@ import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.SensorManager;
import android.hardware.SystemSensorManager;
import android.hardware.SystemSensorManager;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Handler;
import android.os.Looper;
import android.os.Looper;
import android.os.Message;
import android.os.Message;
@@ -44,7 +43,6 @@ import android.util.TimeUtils;
import android.view.Display;
import android.view.Display;


import java.io.PrintWriter;
import java.io.PrintWriter;
import java.util.concurrent.Executor;


/**
/**
 * Controls the power state of the display.
 * Controls the power state of the display.
@@ -161,9 +159,6 @@ final class DisplayPowerController {
    // Notifier for sending asynchronous notifications.
    // Notifier for sending asynchronous notifications.
    private final Notifier mNotifier;
    private final Notifier mNotifier;


    // A suspend blocker.
    private final SuspendBlocker mSuspendBlocker;

    // The display blanker.
    // The display blanker.
    private final DisplayBlanker mDisplayBlanker;
    private final DisplayBlanker mDisplayBlanker;


@@ -339,12 +334,11 @@ final class DisplayPowerController {
     * Creates the display power controller.
     * Creates the display power controller.
     */
     */
    public DisplayPowerController(Looper looper, Context context, Notifier notifier,
    public DisplayPowerController(Looper looper, Context context, Notifier notifier,
            LightsService lights, TwilightService twilight, SuspendBlocker suspendBlocker,
            LightsService lights, TwilightService twilight,
            DisplayBlanker displayBlanker,
            DisplayBlanker displayBlanker,
            Callbacks callbacks, Handler callbackHandler) {
            Callbacks callbacks, Handler callbackHandler) {
        mHandler = new DisplayControllerHandler(looper);
        mHandler = new DisplayControllerHandler(looper);
        mNotifier = notifier;
        mNotifier = notifier;
        mSuspendBlocker = suspendBlocker;
        mDisplayBlanker = displayBlanker;
        mDisplayBlanker = displayBlanker;
        mCallbacks = callbacks;
        mCallbacks = callbacks;
        mCallbackHandler = callbackHandler;
        mCallbackHandler = callbackHandler;
@@ -513,14 +507,10 @@ final class DisplayPowerController {
    }
    }


    private void initialize() {
    private void initialize() {
        final Executor executor = AsyncTask.THREAD_POOL_EXECUTOR;
        Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
        Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
        mPowerState = new DisplayPowerState(
        mPowerState = new DisplayPowerState(
                new ElectronBeam(display),
                new ElectronBeam(display), mDisplayBlanker,
                new PhotonicModulator(executor,
                mLights.getLight(LightsService.LIGHT_ID_BACKLIGHT));
                        mLights.getLight(LightsService.LIGHT_ID_BACKLIGHT),
                        mSuspendBlocker),
                mDisplayBlanker);


        mElectronBeamOnAnimator = ObjectAnimator.ofFloat(
        mElectronBeamOnAnimator = ObjectAnimator.ofFloat(
                mPowerState, DisplayPowerState.ELECTRON_BEAM_LEVEL, 0.0f, 1.0f);
                mPowerState, DisplayPowerState.ELECTRON_BEAM_LEVEL, 0.0f, 1.0f);
+215 −78
Original line number Original line Diff line number Diff line
@@ -16,6 +16,10 @@


package com.android.server.power;
package com.android.server.power;


import com.android.server.LightsService;

import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerManager;
import android.util.FloatProperty;
import android.util.FloatProperty;
@@ -26,47 +30,55 @@ import android.view.Choreographer;
import java.io.PrintWriter;
import java.io.PrintWriter;


/**
/**
 * Represents the current display power state and realizes it.
 * Controls the display power state.
 *
 * <p>
 * This component is similar in nature to a {@link View} except that it describes
 * This component is similar in nature to a {@link View} except that it describes
 * the properties of a display.  When properties are changed, the component
 * the properties of a display.  When properties are changed, the component
 * invalidates itself and posts a callback to the {@link Choreographer} to
 * invalidates itself and posts a callback to apply the changes in a consistent order.
 * apply the changes.  This mechanism enables the display power state to be
 * This mechanism enables multiple properties of the display power state to be animated
 * animated smoothly by the animation framework.
 * together smoothly by the animation framework.  Some of the work to blank or unblank
 *
 * the display is done on a separate thread to avoid blocking the looper.
 * </p><p>
 * This component must only be created or accessed by the {@link Looper} thread
 * This component must only be created or accessed by the {@link Looper} thread
 * that belongs to the {@link DisplayPowerController}.
 * that belongs to the {@link DisplayPowerController}.
 *
 * </p><p>
 * We don't need to worry about holding a suspend blocker here because the
 * We don't need to worry about holding a suspend blocker here because the
 * {@link DisplayPowerController} does that for us whenever there is a pending invalidate.
 * {@link PowerManagerService} does that for us whenever there is a change
 * in progress.
 * </p>
 */
 */
final class DisplayPowerState {
final class DisplayPowerState {
    private static final String TAG = "DisplayPowerState";
    private static final String TAG = "DisplayPowerState";


    private static boolean DEBUG = false;
    private static boolean DEBUG = false;


    private static final int DIRTY_SCREEN_ON = 1 << 0;
    private final Handler mHandler;
    private static final int DIRTY_ELECTRON_BEAM = 1 << 1;
    private static final int DIRTY_BRIGHTNESS = 1 << 2;

    private final Choreographer mChoreographer;
    private final Choreographer mChoreographer;
    private final ElectronBeam mElectronBeam;
    private final ElectronBeam mElectronBeam;
    private final PhotonicModulator mPhotonicModulator;
    private final DisplayBlanker mDisplayBlanker;
    private final DisplayBlanker mDisplayBlanker;
    private final LightsService.Light mBacklight;
    private final PhotonicModulator mPhotonicModulator;


    private int mDirty;
    private boolean mScreenOn;
    private boolean mScreenOn;
    private float mElectronBeamLevel;
    private int mScreenBrightness;
    private int mScreenBrightness;
    private boolean mScreenReady;
    private boolean mScreenUpdatePending;

    private boolean mElectronBeamPrepared;
    private float mElectronBeamLevel;
    private boolean mElectronBeamReady;
    private boolean mElectronBeamDrawPending;


    private Runnable mCleanListener;
    private Runnable mCleanListener;


    public DisplayPowerState(ElectronBeam electronBean,
    public DisplayPowerState(ElectronBeam electronBean,
            PhotonicModulator photonicModulator, DisplayBlanker displayBlanker) {
            DisplayBlanker displayBlanker, LightsService.Light backlight) {
        mHandler = new Handler(true /*async*/);
        mChoreographer = Choreographer.getInstance();
        mChoreographer = Choreographer.getInstance();
        mElectronBeam = electronBean;
        mElectronBeam = electronBean;
        mPhotonicModulator = photonicModulator;
        mDisplayBlanker = displayBlanker;
        mDisplayBlanker = displayBlanker;
        mBacklight = backlight;
        mPhotonicModulator = new PhotonicModulator();


        // At boot time, we know that the screen is on and the electron beam
        // At boot time, we know that the screen is on and the electron beam
        // animation is not playing.  We don't know the screen's brightness though,
        // animation is not playing.  We don't know the screen's brightness though,
@@ -75,9 +87,12 @@ final class DisplayPowerState {
        // will reset the brightness to a new level immediately before the changes
        // will reset the brightness to a new level immediately before the changes
        // actually have a chance to be applied.
        // actually have a chance to be applied.
        mScreenOn = true;
        mScreenOn = true;
        mElectronBeamLevel = 1.0f;
        mScreenBrightness = PowerManager.BRIGHTNESS_ON;
        mScreenBrightness = PowerManager.BRIGHTNESS_ON;
        invalidate(DIRTY_BRIGHTNESS);
        scheduleScreenUpdate();

        mElectronBeamPrepared = false;
        mElectronBeamLevel = 1.0f;
        mElectronBeamReady = true;
    }
    }


    public static final FloatProperty<DisplayPowerState> ELECTRON_BEAM_LEVEL =
    public static final FloatProperty<DisplayPowerState> ELECTRON_BEAM_LEVEL =
@@ -116,7 +131,8 @@ final class DisplayPowerState {
            }
            }


            mScreenOn = on;
            mScreenOn = on;
            invalidate(DIRTY_SCREEN_ON);
            mScreenReady = false;
            scheduleScreenUpdate();
        }
        }
    }
    }


@@ -127,6 +143,32 @@ final class DisplayPowerState {
        return mScreenOn;
        return mScreenOn;
    }
    }


    /**
     * Sets the display brightness.
     *
     * @param brightness The brightness, ranges from 0 (minimum / off) to 255 (brightest).
     */
    public void setScreenBrightness(int brightness) {
        if (mScreenBrightness != brightness) {
            if (DEBUG) {
                Slog.d(TAG, "setScreenBrightness: brightness=" + brightness);
            }

            mScreenBrightness = brightness;
            if (mScreenOn) {
                mScreenReady = false;
                scheduleScreenUpdate();
            }
        }
    }

    /**
     * Gets the screen brightness.
     */
    public int getScreenBrightness() {
        return mScreenBrightness;
    }

    /**
    /**
     * Prepares the electron beam to turn on or off.
     * Prepares the electron beam to turn on or off.
     * This method should be called before starting an animation because it
     * This method should be called before starting an animation because it
@@ -136,8 +178,16 @@ final class DisplayPowerState {
     * @return True if the electron beam was prepared.
     * @return True if the electron beam was prepared.
     */
     */
    public boolean prepareElectronBeam(int mode) {
    public boolean prepareElectronBeam(int mode) {
        invalidate(DIRTY_ELECTRON_BEAM);
        if (!mElectronBeam.prepare(mode)) {
        return mElectronBeam.prepare(mode);
            mElectronBeamPrepared = false;
            mElectronBeamReady = true;
            return false;
        }

        mElectronBeamPrepared = true;
        mElectronBeamReady = false;
        scheduleElectronBeamDraw();
        return true;
    }
    }


    /**
    /**
@@ -145,6 +195,8 @@ final class DisplayPowerState {
     */
     */
    public void dismissElectronBeam() {
    public void dismissElectronBeam() {
        mElectronBeam.dismiss();
        mElectronBeam.dismiss();
        mElectronBeamPrepared = false;
        mElectronBeamReady = true;
    }
    }


    /**
    /**
@@ -167,7 +219,14 @@ final class DisplayPowerState {
            }
            }


            mElectronBeamLevel = level;
            mElectronBeamLevel = level;
            invalidate(DIRTY_ELECTRON_BEAM);
            if (mScreenOn) {
                mScreenReady = false;
                scheduleScreenUpdate(); // update backlight brightness
            }
            if (mElectronBeamPrepared) {
                mElectronBeamReady = false;
                scheduleElectronBeamDraw();
            }
        }
        }
    }
    }


@@ -178,29 +237,6 @@ final class DisplayPowerState {
        return mElectronBeamLevel;
        return mElectronBeamLevel;
    }
    }


    /**
     * Sets the display brightness.
     *
     * @param brightness The brightness, ranges from 0 (minimum / off) to 255 (brightest).
     */
    public void setScreenBrightness(int brightness) {
        if (mScreenBrightness != brightness) {
            if (DEBUG) {
                Slog.d(TAG, "setScreenBrightness: brightness=" + brightness);
            }

            mScreenBrightness = brightness;
            invalidate(DIRTY_BRIGHTNESS);
        }
    }

    /**
     * Gets the screen brightness.
     */
    public int getScreenBrightness() {
        return mScreenBrightness;
    }

    /**
    /**
     * Returns true if no properties have been invalidated.
     * Returns true if no properties have been invalidated.
     * Otherwise, returns false and promises to invoke the specified listener
     * Otherwise, returns false and promises to invoke the specified listener
@@ -208,7 +244,7 @@ final class DisplayPowerState {
     * The listener always overrides any previously set listener.
     * The listener always overrides any previously set listener.
     */
     */
    public boolean waitUntilClean(Runnable listener) {
    public boolean waitUntilClean(Runnable listener) {
        if (mDirty != 0) {
        if (!mScreenReady || !mElectronBeamReady) {
            mCleanListener = listener;
            mCleanListener = listener;
            return false;
            return false;
        } else {
        } else {
@@ -220,58 +256,159 @@ final class DisplayPowerState {
    public void dump(PrintWriter pw) {
    public void dump(PrintWriter pw) {
        pw.println();
        pw.println();
        pw.println("Display Power State:");
        pw.println("Display Power State:");
        pw.println("  mDirty=" + Integer.toHexString(mDirty));
        pw.println("  mScreenOn=" + mScreenOn);
        pw.println("  mScreenOn=" + mScreenOn);
        pw.println("  mScreenBrightness=" + mScreenBrightness);
        pw.println("  mScreenBrightness=" + mScreenBrightness);
        pw.println("  mScreenReady=" + mScreenReady);
        pw.println("  mScreenUpdatePending=" + mScreenUpdatePending);
        pw.println("  mElectronBeamPrepared=" + mElectronBeamPrepared);
        pw.println("  mElectronBeamLevel=" + mElectronBeamLevel);
        pw.println("  mElectronBeamLevel=" + mElectronBeamLevel);
        pw.println("  mElectronBeamReady=" + mElectronBeamReady);
        pw.println("  mElectronBeamDrawPending=" + mElectronBeamDrawPending);


        mPhotonicModulator.dump(pw);
        mElectronBeam.dump(pw);
        mElectronBeam.dump(pw);
    }
    }


    private void invalidate(int dirty) {
    private void scheduleScreenUpdate() {
        if (mDirty == 0) {
        if (!mScreenUpdatePending) {
            mScreenUpdatePending = true;
            postScreenUpdateThreadSafe();
        }
    }

    private void postScreenUpdateThreadSafe() {
        mHandler.removeCallbacks(mScreenUpdateRunnable);
        mHandler.post(mScreenUpdateRunnable);
    }

    private void scheduleElectronBeamDraw() {
        if (!mElectronBeamDrawPending) {
            mElectronBeamDrawPending = true;
            mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL,
            mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL,
                    mTraversalRunnable, null);
                    mElectronBeamDrawRunnable, null);
        }
    }
    }


        mDirty |= dirty;
    private void invokeCleanListenerIfNeeded() {
        final Runnable listener = mCleanListener;
        if (listener != null && mScreenReady && mElectronBeamReady) {
            mCleanListener = null;
            listener.run();
        }
    }
    }


    private void apply() {
    private final Runnable mScreenUpdateRunnable = new Runnable() {
        if (mDirty != 0) {
        @Override
            if ((mDirty & DIRTY_SCREEN_ON) != 0 && !mScreenOn) {
        public void run() {
                mPhotonicModulator.setBrightness(0, true /*sync*/);
            mScreenUpdatePending = false;
                mDisplayBlanker.blankAllDisplays();

            if (mPhotonicModulator.setState(mScreenOn,
                    mScreenOn ? (int)(mScreenBrightness * mElectronBeamLevel) : 0)) {
                mScreenReady = true;
                invokeCleanListenerIfNeeded();
            }
            }
        }
    };

    private final Runnable mElectronBeamDrawRunnable = new Runnable() {
        @Override
        public void run() {
            mElectronBeamDrawPending = false;


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


            if ((mDirty & DIRTY_SCREEN_ON) != 0 && mScreenOn) {
            mElectronBeamReady = true;
                mDisplayBlanker.unblankAllDisplays();
            invokeCleanListenerIfNeeded();
        }
        }
    };

    /**
     * Updates the state of the screen and backlight asynchronously on a separate thread.
     */
    private final class PhotonicModulator {
        private static final boolean INITIAL_SCREEN_ON = false; // unknown, assume off
        private static final int INITIAL_BACKLIGHT = -1; // unknown


            if ((mDirty & (DIRTY_BRIGHTNESS | DIRTY_SCREEN_ON | DIRTY_ELECTRON_BEAM)) != 0
        private final Object mLock = new Object();
                    && mScreenOn) {

                mPhotonicModulator.setBrightness(
        private boolean mPendingOn = INITIAL_SCREEN_ON;
                        (int)(mScreenBrightness * mElectronBeamLevel), false /*sync*/);
        private int mPendingBacklight = INITIAL_BACKLIGHT;
        private boolean mActualOn = INITIAL_SCREEN_ON;
        private int mActualBacklight = INITIAL_BACKLIGHT;
        private boolean mChangeInProgress;

        public boolean setState(boolean on, int backlight) {
            synchronized (mLock) {
                if (on != mPendingOn || backlight != mPendingBacklight) {
                    if (DEBUG) {
                        Slog.d(TAG, "Requesting new screen state: on=" + on
                                + ", backlight=" + backlight);
                    }
                    }


            mDirty = 0;
                    mPendingOn = on;
                    mPendingBacklight = backlight;


            if (mCleanListener != null) {
                    if (!mChangeInProgress) {
                mCleanListener.run();
                        mChangeInProgress = true;
                        AsyncTask.THREAD_POOL_EXECUTOR.execute(mTask);
                    }
                    }
                }
                }
                return mChangeInProgress;
            }
        }

        public void dump(PrintWriter pw) {
            pw.println();
            pw.println("Photonic Modulator State:");
            pw.println("  mPendingOn=" + mPendingOn);
            pw.println("  mPendingBacklight=" + mPendingBacklight);
            pw.println("  mActualOn=" + mActualOn);
            pw.println("  mActualBacklight=" + mActualBacklight);
            pw.println("  mChangeInProgress=" + mChangeInProgress);
        }
        }


    private final Runnable mTraversalRunnable = new Runnable() {
        private final Runnable mTask = new Runnable() {
            @Override
            @Override
            public void run() {
            public void run() {
            if (mDirty != 0) {
                // Apply pending changes until done.
                apply();
                for (;;) {
                    final boolean on;
                    final boolean onChanged;
                    final int backlight;
                    final boolean backlightChanged;
                    synchronized (mLock) {
                        on = mPendingOn;
                        onChanged = (on != mActualOn);
                        backlight = mPendingBacklight;
                        backlightChanged = (backlight != mActualBacklight);
                        if (!onChanged && !backlightChanged) {
                            mChangeInProgress = false;
                            break;
                        }
                        mActualOn = on;
                        mActualBacklight = backlight;
                    }

                    if (DEBUG) {
                        Slog.d(TAG, "Updating screen state: on=" + on
                                + ", backlight=" + backlight);
                    }
                    if (onChanged && on) {
                        mDisplayBlanker.unblankAllDisplays();
                    }
                    }
                    if (backlightChanged) {
                        mBacklight.setBrightness(backlight);
                    }
                    if (onChanged && !on) {
                        mDisplayBlanker.blankAllDisplays();
                    }
                }

                // Let the outer class know that all changes have been applied.
                postScreenUpdateThreadSafe();
            }
            }
        };
        };
    }
    }
}
+0 −109
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2012 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.power;

import android.util.Slog;

import com.android.server.LightsService;

import java.util.concurrent.Executor;

/**
 * Sets the value of a light asynchronously.
 *
 * This is done to avoid blocking the looper on devices for which
 * setting the backlight brightness is especially slow.
 */
final class PhotonicModulator {
    private static final String TAG = "PhotonicModulator";
    private static final boolean DEBUG = false;

    private static final int UNKNOWN_LIGHT_VALUE = -1;

    private final Object mLock = new Object();

    private final LightsService.Light mLight;
    private final Executor mExecutor;
    private final SuspendBlocker mSuspendBlocker;

    private boolean mPendingChange;
    private int mPendingLightValue;
    private int mActualLightValue;

    public PhotonicModulator(Executor executor, LightsService.Light light,
            SuspendBlocker suspendBlocker) {
        mExecutor = executor;
        mLight = light;
        mSuspendBlocker = suspendBlocker;
        mPendingLightValue = UNKNOWN_LIGHT_VALUE;
        mActualLightValue = UNKNOWN_LIGHT_VALUE;
    }

    /**
     * Sets the backlight brightness, synchronously or asynchronously.
     *
     * @param lightValue The new light value, from 0 to 255.
     * @param sync If true, waits for the brightness change to complete before returning.
     */
    public void setBrightness(int lightValue, boolean sync) {
        synchronized (mLock) {
            if (lightValue != mPendingLightValue) {
                mPendingLightValue = lightValue;
                if (DEBUG) {
                    Slog.d(TAG, "Enqueuing request to change brightness to " + lightValue);
                }
                if (!mPendingChange) {
                    mPendingChange = true;
                    mSuspendBlocker.acquire();
                    mExecutor.execute(mTask);
                }
            }
            if (sync) {
                while (mPendingChange) {
                    try {
                        mLock.wait();
                    } catch (InterruptedException ex) {
                        // ignore it
                    }
                }
            }
        }
    }

    private final Runnable mTask = new Runnable() {
        @Override
        public void run() {
            for (;;) {
                final int newLightValue;
                synchronized (mLock) {
                    newLightValue = mPendingLightValue;
                    if (newLightValue == mActualLightValue) {
                        mSuspendBlocker.release();
                        mPendingChange = false;
                        mLock.notifyAll();
                        return;
                    }
                    mActualLightValue = newLightValue;
                }
                if (DEBUG) {
                    Slog.d(TAG, "Setting brightness to " + newLightValue);
                }
                mLight.setBrightness(newLightValue);
            }
        }
    };
}
+0 −1
Original line number Original line Diff line number Diff line
@@ -433,7 +433,6 @@ public final class PowerManagerService extends IPowerManager.Stub
            // own handler thread.
            // own handler thread.
            mDisplayPowerController = new DisplayPowerController(mHandler.getLooper(),
            mDisplayPowerController = new DisplayPowerController(mHandler.getLooper(),
                    mContext, mNotifier, mLightsService, twilight,
                    mContext, mNotifier, mLightsService, twilight,
                    createSuspendBlockerLocked("PowerManagerService.Display"),
                    mDisplayBlanker, mDisplayPowerControllerCallbacks, mHandler);
                    mDisplayBlanker, mDisplayPowerControllerCallbacks, mHandler);


            mSettingsObserver = new SettingsObserver(mHandler);
            mSettingsObserver = new SettingsObserver(mHandler);