Home | History | Annotate | Download | only in power
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.power;
     18 
     19 import com.android.server.LightsService;
     20 
     21 import android.os.AsyncTask;
     22 import android.os.Handler;
     23 import android.os.Looper;
     24 import android.os.PowerManager;
     25 import android.util.FloatProperty;
     26 import android.util.IntProperty;
     27 import android.util.Slog;
     28 import android.view.Choreographer;
     29 
     30 import java.io.PrintWriter;
     31 
     32 /**
     33  * Controls the display power state.
     34  * <p>
     35  * This component is similar in nature to a {@link View} except that it describes
     36  * the properties of a display.  When properties are changed, the component
     37  * invalidates itself and posts a callback to apply the changes in a consistent order.
     38  * This mechanism enables multiple properties of the display power state to be animated
     39  * together smoothly by the animation framework.  Some of the work to blank or unblank
     40  * the display is done on a separate thread to avoid blocking the looper.
     41  * </p><p>
     42  * This component must only be created or accessed by the {@link Looper} thread
     43  * that belongs to the {@link DisplayPowerController}.
     44  * </p><p>
     45  * We don't need to worry about holding a suspend blocker here because the
     46  * {@link PowerManagerService} does that for us whenever there is a change
     47  * in progress.
     48  * </p>
     49  */
     50 final class DisplayPowerState {
     51     private static final String TAG = "DisplayPowerState";
     52 
     53     private static boolean DEBUG = false;
     54 
     55     private final Handler mHandler;
     56     private final Choreographer mChoreographer;
     57     private final ElectronBeam mElectronBeam;
     58     private final DisplayBlanker mDisplayBlanker;
     59     private final LightsService.Light mBacklight;
     60     private final PhotonicModulator mPhotonicModulator;
     61 
     62     private boolean mScreenOn;
     63     private int mScreenBrightness;
     64     private boolean mScreenReady;
     65     private boolean mScreenUpdatePending;
     66 
     67     private boolean mElectronBeamPrepared;
     68     private float mElectronBeamLevel;
     69     private boolean mElectronBeamReady;
     70     private boolean mElectronBeamDrawPending;
     71 
     72     private Runnable mCleanListener;
     73 
     74     public DisplayPowerState(ElectronBeam electronBean,
     75             DisplayBlanker displayBlanker, LightsService.Light backlight) {
     76         mHandler = new Handler(true /*async*/);
     77         mChoreographer = Choreographer.getInstance();
     78         mElectronBeam = electronBean;
     79         mDisplayBlanker = displayBlanker;
     80         mBacklight = backlight;
     81         mPhotonicModulator = new PhotonicModulator();
     82 
     83         // At boot time, we know that the screen is on and the electron beam
     84         // animation is not playing.  We don't know the screen's brightness though,
     85         // so prepare to set it to a known state when the state is next applied.
     86         // Although we set the brightness to full on here, the display power controller
     87         // will reset the brightness to a new level immediately before the changes
     88         // actually have a chance to be applied.
     89         mScreenOn = true;
     90         mScreenBrightness = PowerManager.BRIGHTNESS_ON;
     91         scheduleScreenUpdate();
     92 
     93         mElectronBeamPrepared = false;
     94         mElectronBeamLevel = 1.0f;
     95         mElectronBeamReady = true;
     96     }
     97 
     98     public static final FloatProperty<DisplayPowerState> ELECTRON_BEAM_LEVEL =
     99             new FloatProperty<DisplayPowerState>("electronBeamLevel") {
    100         @Override
    101         public void setValue(DisplayPowerState object, float value) {
    102             object.setElectronBeamLevel(value);
    103         }
    104 
    105         @Override
    106         public Float get(DisplayPowerState object) {
    107             return object.getElectronBeamLevel();
    108         }
    109     };
    110 
    111     public static final IntProperty<DisplayPowerState> SCREEN_BRIGHTNESS =
    112             new IntProperty<DisplayPowerState>("screenBrightness") {
    113         @Override
    114         public void setValue(DisplayPowerState object, int value) {
    115             object.setScreenBrightness(value);
    116         }
    117 
    118         @Override
    119         public Integer get(DisplayPowerState object) {
    120             return object.getScreenBrightness();
    121         }
    122     };
    123 
    124     /**
    125      * Sets whether the screen is on or off.
    126      */
    127     public void setScreenOn(boolean on) {
    128         if (mScreenOn != on) {
    129             if (DEBUG) {
    130                 Slog.d(TAG, "setScreenOn: on=" + on);
    131             }
    132 
    133             mScreenOn = on;
    134             mScreenReady = false;
    135             scheduleScreenUpdate();
    136         }
    137     }
    138 
    139     /**
    140      * Returns true if the screen is on.
    141      */
    142     public boolean isScreenOn() {
    143         return mScreenOn;
    144     }
    145 
    146     /**
    147      * Sets the display brightness.
    148      *
    149      * @param brightness The brightness, ranges from 0 (minimum / off) to 255 (brightest).
    150      */
    151     public void setScreenBrightness(int brightness) {
    152         if (mScreenBrightness != brightness) {
    153             if (DEBUG) {
    154                 Slog.d(TAG, "setScreenBrightness: brightness=" + brightness);
    155             }
    156 
    157             mScreenBrightness = brightness;
    158             if (mScreenOn) {
    159                 mScreenReady = false;
    160                 scheduleScreenUpdate();
    161             }
    162         }
    163     }
    164 
    165     /**
    166      * Gets the screen brightness.
    167      */
    168     public int getScreenBrightness() {
    169         return mScreenBrightness;
    170     }
    171 
    172     /**
    173      * Prepares the electron beam to turn on or off.
    174      * This method should be called before starting an animation because it
    175      * can take a fair amount of time to prepare the electron beam surface.
    176      *
    177      * @param mode The electron beam animation mode to prepare.
    178      * @return True if the electron beam was prepared.
    179      */
    180     public boolean prepareElectronBeam(int mode) {
    181         if (!mElectronBeam.prepare(mode)) {
    182             mElectronBeamPrepared = false;
    183             mElectronBeamReady = true;
    184             return false;
    185         }
    186 
    187         mElectronBeamPrepared = true;
    188         mElectronBeamReady = false;
    189         scheduleElectronBeamDraw();
    190         return true;
    191     }
    192 
    193     /**
    194      * Dismisses the electron beam surface.
    195      */
    196     public void dismissElectronBeam() {
    197         mElectronBeam.dismiss();
    198         mElectronBeamPrepared = false;
    199         mElectronBeamReady = true;
    200     }
    201 
    202     /**
    203      * Sets the level of the electron beam steering current.
    204      *
    205      * The display is blanked when the level is 0.0.  In normal use, the electron
    206      * beam should have a value of 1.0.  The electron beam is unstable in between
    207      * these states and the picture quality may be compromised.  For best effect,
    208      * the electron beam should be warmed up or cooled off slowly.
    209      *
    210      * Warning: Electron beam emits harmful radiation.  Avoid direct exposure to
    211      * skin or eyes.
    212      *
    213      * @param level The level, ranges from 0.0 (full off) to 1.0 (full on).
    214      */
    215     public void setElectronBeamLevel(float level) {
    216         if (mElectronBeamLevel != level) {
    217             if (DEBUG) {
    218                 Slog.d(TAG, "setElectronBeamLevel: level=" + level);
    219             }
    220 
    221             mElectronBeamLevel = level;
    222             if (mScreenOn) {
    223                 mScreenReady = false;
    224                 scheduleScreenUpdate(); // update backlight brightness
    225             }
    226             if (mElectronBeamPrepared) {
    227                 mElectronBeamReady = false;
    228                 scheduleElectronBeamDraw();
    229             }
    230         }
    231     }
    232 
    233     /**
    234      * Gets the level of the electron beam steering current.
    235      */
    236     public float getElectronBeamLevel() {
    237         return mElectronBeamLevel;
    238     }
    239 
    240     /**
    241      * Returns true if no properties have been invalidated.
    242      * Otherwise, returns false and promises to invoke the specified listener
    243      * when the properties have all been applied.
    244      * The listener always overrides any previously set listener.
    245      */
    246     public boolean waitUntilClean(Runnable listener) {
    247         if (!mScreenReady || !mElectronBeamReady) {
    248             mCleanListener = listener;
    249             return false;
    250         } else {
    251             mCleanListener = null;
    252             return true;
    253         }
    254     }
    255 
    256     public void dump(PrintWriter pw) {
    257         pw.println();
    258         pw.println("Display Power State:");
    259         pw.println("  mScreenOn=" + mScreenOn);
    260         pw.println("  mScreenBrightness=" + mScreenBrightness);
    261         pw.println("  mScreenReady=" + mScreenReady);
    262         pw.println("  mScreenUpdatePending=" + mScreenUpdatePending);
    263         pw.println("  mElectronBeamPrepared=" + mElectronBeamPrepared);
    264         pw.println("  mElectronBeamLevel=" + mElectronBeamLevel);
    265         pw.println("  mElectronBeamReady=" + mElectronBeamReady);
    266         pw.println("  mElectronBeamDrawPending=" + mElectronBeamDrawPending);
    267 
    268         mPhotonicModulator.dump(pw);
    269         mElectronBeam.dump(pw);
    270     }
    271 
    272     private void scheduleScreenUpdate() {
    273         if (!mScreenUpdatePending) {
    274             mScreenUpdatePending = true;
    275             postScreenUpdateThreadSafe();
    276         }
    277     }
    278 
    279     private void postScreenUpdateThreadSafe() {
    280         mHandler.removeCallbacks(mScreenUpdateRunnable);
    281         mHandler.post(mScreenUpdateRunnable);
    282     }
    283 
    284     private void scheduleElectronBeamDraw() {
    285         if (!mElectronBeamDrawPending) {
    286             mElectronBeamDrawPending = true;
    287             mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL,
    288                     mElectronBeamDrawRunnable, null);
    289         }
    290     }
    291 
    292     private void invokeCleanListenerIfNeeded() {
    293         final Runnable listener = mCleanListener;
    294         if (listener != null && mScreenReady && mElectronBeamReady) {
    295             mCleanListener = null;
    296             listener.run();
    297         }
    298     }
    299 
    300     private final Runnable mScreenUpdateRunnable = new Runnable() {
    301         @Override
    302         public void run() {
    303             mScreenUpdatePending = false;
    304 
    305             int brightness = mScreenOn && mElectronBeamLevel > 0f ? mScreenBrightness : 0;
    306             if (mPhotonicModulator.setState(mScreenOn, brightness)) {
    307                 if (DEBUG) {
    308                     Slog.d(TAG, "Screen ready");
    309                 }
    310                 mScreenReady = true;
    311                 invokeCleanListenerIfNeeded();
    312             } else {
    313                 if (DEBUG) {
    314                     Slog.d(TAG, "Screen not ready");
    315                 }
    316             }
    317         }
    318     };
    319 
    320     private final Runnable mElectronBeamDrawRunnable = new Runnable() {
    321         @Override
    322         public void run() {
    323             mElectronBeamDrawPending = false;
    324 
    325             if (mElectronBeamPrepared) {
    326                 mElectronBeam.draw(mElectronBeamLevel);
    327             }
    328 
    329             mElectronBeamReady = true;
    330             invokeCleanListenerIfNeeded();
    331         }
    332     };
    333 
    334     /**
    335      * Updates the state of the screen and backlight asynchronously on a separate thread.
    336      */
    337     private final class PhotonicModulator {
    338         private static final boolean INITIAL_SCREEN_ON = false; // unknown, assume off
    339         private static final int INITIAL_BACKLIGHT = -1; // unknown
    340 
    341         private final Object mLock = new Object();
    342 
    343         private boolean mPendingOn = INITIAL_SCREEN_ON;
    344         private int mPendingBacklight = INITIAL_BACKLIGHT;
    345         private boolean mActualOn = INITIAL_SCREEN_ON;
    346         private int mActualBacklight = INITIAL_BACKLIGHT;
    347         private boolean mChangeInProgress;
    348 
    349         public boolean setState(boolean on, int backlight) {
    350             synchronized (mLock) {
    351                 if (on != mPendingOn || backlight != mPendingBacklight) {
    352                     if (DEBUG) {
    353                         Slog.d(TAG, "Requesting new screen state: on=" + on
    354                                 + ", backlight=" + backlight);
    355                     }
    356 
    357                     mPendingOn = on;
    358                     mPendingBacklight = backlight;
    359 
    360                     if (!mChangeInProgress) {
    361                         mChangeInProgress = true;
    362                         AsyncTask.THREAD_POOL_EXECUTOR.execute(mTask);
    363                     }
    364                 }
    365                 return !mChangeInProgress;
    366             }
    367         }
    368 
    369         public void dump(PrintWriter pw) {
    370             pw.println();
    371             pw.println("Photonic Modulator State:");
    372             pw.println("  mPendingOn=" + mPendingOn);
    373             pw.println("  mPendingBacklight=" + mPendingBacklight);
    374             pw.println("  mActualOn=" + mActualOn);
    375             pw.println("  mActualBacklight=" + mActualBacklight);
    376             pw.println("  mChangeInProgress=" + mChangeInProgress);
    377         }
    378 
    379         private final Runnable mTask = new Runnable() {
    380             @Override
    381             public void run() {
    382                 // Apply pending changes until done.
    383                 for (;;) {
    384                     final boolean on;
    385                     final boolean onChanged;
    386                     final int backlight;
    387                     final boolean backlightChanged;
    388                     synchronized (mLock) {
    389                         on = mPendingOn;
    390                         onChanged = (on != mActualOn);
    391                         backlight = mPendingBacklight;
    392                         backlightChanged = (backlight != mActualBacklight);
    393                         if (!onChanged && !backlightChanged) {
    394                             mChangeInProgress = false;
    395                             break;
    396                         }
    397                         mActualOn = on;
    398                         mActualBacklight = backlight;
    399                     }
    400 
    401                     if (DEBUG) {
    402                         Slog.d(TAG, "Updating screen state: on=" + on
    403                                 + ", backlight=" + backlight);
    404                     }
    405                     if (onChanged && on) {
    406                         mDisplayBlanker.unblankAllDisplays();
    407                     }
    408                     if (backlightChanged) {
    409                         mBacklight.setBrightness(backlight);
    410                     }
    411                     if (onChanged && !on) {
    412                         mDisplayBlanker.blankAllDisplays();
    413                     }
    414                 }
    415 
    416                 // Let the outer class know that all changes have been applied.
    417                 postScreenUpdateThreadSafe();
    418             }
    419         };
    420     }
    421 }
    422