1/*
2 * Copyright (C) 2007 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
17package android.os;
18
19import android.util.Log;
20
21import com.android.internal.os.RuntimeInit;
22
23/**
24 * This class gives you control of the power state of the device.
25 *
26 * <p><b>Device battery life will be significantly affected by the use of this API.</b>  Do not
27 * acquire WakeLocks unless you really need them, use the minimum levels possible, and be sure
28 * to release it as soon as you can.
29 *
30 * <p>You can obtain an instance of this class by calling
31 * {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
32 *
33 * <p>The primary API you'll use is {@link #newWakeLock(int, String) newWakeLock()}.  This will
34 * create a {@link PowerManager.WakeLock} object.  You can then use methods on this object to
35 * control the power state of the device.  In practice it's quite simple:
36 *
37 * {@samplecode
38 * PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
39 * PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
40 * wl.acquire();
41 *   ..screen will stay on during this section..
42 * wl.release();
43 * }
44 *
45 * <p>The following flags are defined, with varying effects on system power.  <i>These flags are
46 * mutually exclusive - you may only specify one of them.</i>
47 * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
48 *
49 *     <thead>
50 *     <tr><th>Flag Value</th>
51 *     <th>CPU</th> <th>Screen</th> <th>Keyboard</th></tr>
52 *     </thead>
53 *
54 *     <tbody>
55 *     <tr><th>{@link #PARTIAL_WAKE_LOCK}</th>
56 *         <td>On*</td> <td>Off</td> <td>Off</td>
57 *     </tr>
58 *
59 *     <tr><th>{@link #SCREEN_DIM_WAKE_LOCK}</th>
60 *         <td>On</td> <td>Dim</td> <td>Off</td>
61 *     </tr>
62 *
63 *     <tr><th>{@link #SCREEN_BRIGHT_WAKE_LOCK}</th>
64 *         <td>On</td> <td>Bright</td> <td>Off</td>
65 *     </tr>
66 *
67 *     <tr><th>{@link #FULL_WAKE_LOCK}</th>
68 *         <td>On</td> <td>Bright</td> <td>Bright</td>
69 *     </tr>
70 *     </tbody>
71 * </table>
72 *
73 * <p>*<i>If you hold a partial wakelock, the CPU will continue to run, irrespective of any timers
74 * and even after the user presses the power button.  In all other wakelocks, the CPU will run, but
75 * the user can still put the device to sleep using the power button.</i>
76 *
77 * <p>In addition, you can add two more flags, which affect behavior of the screen only.  <i>These
78 * flags have no effect when combined with a {@link #PARTIAL_WAKE_LOCK}.</i>
79 * <table border="2" width="85%" align="center" frame="hsides" rules="rows">
80 *
81 *     <thead>
82 *     <tr><th>Flag Value</th> <th>Description</th></tr>
83 *     </thead>
84 *
85 *     <tbody>
86 *     <tr><th>{@link #ACQUIRE_CAUSES_WAKEUP}</th>
87 *         <td>Normal wake locks don't actually turn on the illumination.  Instead, they cause
88 *         the illumination to remain on once it turns on (e.g. from user activity).  This flag
89 *         will force the screen and/or keyboard to turn on immediately, when the WakeLock is
90 *         acquired.  A typical use would be for notifications which are important for the user to
91 *         see immediately.</td>
92 *     </tr>
93 *
94 *     <tr><th>{@link #ON_AFTER_RELEASE}</th>
95 *         <td>If this flag is set, the user activity timer will be reset when the WakeLock is
96 *         released, causing the illumination to remain on a bit longer.  This can be used to
97 *         reduce flicker if you are cycling between wake lock conditions.</td>
98 *     </tr>
99 *     </tbody>
100 * </table>
101 *
102 *
103 */
104public class PowerManager
105{
106    private static final String TAG = "PowerManager";
107
108    /**
109     * These internal values define the underlying power elements that we might
110     * want to control individually.  Eventually we'd like to expose them.
111     */
112    private static final int WAKE_BIT_CPU_STRONG = 1;
113    private static final int WAKE_BIT_CPU_WEAK = 2;
114    private static final int WAKE_BIT_SCREEN_DIM = 4;
115    private static final int WAKE_BIT_SCREEN_BRIGHT = 8;
116    private static final int WAKE_BIT_KEYBOARD_BRIGHT = 16;
117    private static final int WAKE_BIT_PROXIMITY_SCREEN_OFF = 32;
118
119    private static final int LOCK_MASK = WAKE_BIT_CPU_STRONG
120                                        | WAKE_BIT_CPU_WEAK
121                                        | WAKE_BIT_SCREEN_DIM
122                                        | WAKE_BIT_SCREEN_BRIGHT
123                                        | WAKE_BIT_KEYBOARD_BRIGHT
124                                        | WAKE_BIT_PROXIMITY_SCREEN_OFF;
125
126    /**
127     * Wake lock that ensures that the CPU is running.  The screen might
128     * not be on.
129     */
130    public static final int PARTIAL_WAKE_LOCK = WAKE_BIT_CPU_STRONG;
131
132    /**
133     * Wake lock that ensures that the screen and keyboard are on at
134     * full brightness.
135     */
136    public static final int FULL_WAKE_LOCK = WAKE_BIT_CPU_WEAK | WAKE_BIT_SCREEN_BRIGHT
137                                            | WAKE_BIT_KEYBOARD_BRIGHT;
138
139    /**
140     * Wake lock that ensures that the screen is on at full brightness;
141     * the keyboard backlight will be allowed to go off.
142     */
143    public static final int SCREEN_BRIGHT_WAKE_LOCK = WAKE_BIT_CPU_WEAK | WAKE_BIT_SCREEN_BRIGHT;
144
145    /**
146     * Wake lock that ensures that the screen is on (but may be dimmed);
147     * the keyboard backlight will be allowed to go off.
148     */
149    public static final int SCREEN_DIM_WAKE_LOCK = WAKE_BIT_CPU_WEAK | WAKE_BIT_SCREEN_DIM;
150
151    /**
152     * Wake lock that turns the screen off when the proximity sensor activates.
153     * Since not all devices have proximity sensors, use
154     * {@link #getSupportedWakeLockFlags() getSupportedWakeLockFlags()} to determine if
155     * this wake lock mode is supported.
156     *
157     * {@hide}
158     */
159    public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = WAKE_BIT_PROXIMITY_SCREEN_OFF;
160
161    /**
162     * Flag for {@link WakeLock#release release(int)} to defer releasing a
163     * {@link #WAKE_BIT_PROXIMITY_SCREEN_OFF} wakelock until the proximity sensor returns
164     * a negative value.
165     *
166     * {@hide}
167     */
168    public static final int WAIT_FOR_PROXIMITY_NEGATIVE = 1;
169
170    /**
171     * Normally wake locks don't actually wake the device, they just cause
172     * it to remain on once it's already on.  Think of the video player
173     * app as the normal behavior.  Notifications that pop up and want
174     * the device to be on are the exception; use this flag to be like them.
175     * <p>
176     * Does not work with PARTIAL_WAKE_LOCKs.
177     */
178    public static final int ACQUIRE_CAUSES_WAKEUP = 0x10000000;
179
180    /**
181     * When this wake lock is released, poke the user activity timer
182     * so the screen stays on for a little longer.
183     * <p>
184     * Will not turn the screen on if it is not already on.  See {@link #ACQUIRE_CAUSES_WAKEUP}
185     * if you want that.
186     * <p>
187     * Does not work with PARTIAL_WAKE_LOCKs.
188     */
189    public static final int ON_AFTER_RELEASE = 0x20000000;
190
191    /**
192     * Class lets you say that you need to have the device on.
193     *
194     * <p>Call release when you are done and don't need the lock anymore.
195     */
196    public class WakeLock
197    {
198        static final int RELEASE_WAKE_LOCK = 1;
199
200        Runnable mReleaser = new Runnable() {
201            public void run() {
202                release();
203            }
204        };
205
206        int mFlags;
207        String mTag;
208        IBinder mToken;
209        int mCount = 0;
210        boolean mRefCounted = true;
211        boolean mHeld = false;
212        WorkSource mWorkSource;
213
214        WakeLock(int flags, String tag)
215        {
216            switch (flags & LOCK_MASK) {
217            case PARTIAL_WAKE_LOCK:
218            case SCREEN_DIM_WAKE_LOCK:
219            case SCREEN_BRIGHT_WAKE_LOCK:
220            case FULL_WAKE_LOCK:
221            case PROXIMITY_SCREEN_OFF_WAKE_LOCK:
222                break;
223            default:
224                throw new IllegalArgumentException();
225            }
226
227            mFlags = flags;
228            mTag = tag;
229            mToken = new Binder();
230        }
231
232        /**
233         * Sets whether this WakeLock is ref counted.
234         *
235         * <p>Wake locks are reference counted by default.
236         *
237         * @param value true for ref counted, false for not ref counted.
238         */
239        public void setReferenceCounted(boolean value)
240        {
241            mRefCounted = value;
242        }
243
244        /**
245         * Makes sure the device is on at the level you asked when you created
246         * the wake lock.
247         */
248        public void acquire()
249        {
250            synchronized (mToken) {
251                if (!mRefCounted || mCount++ == 0) {
252                    try {
253                        mService.acquireWakeLock(mFlags, mToken, mTag, mWorkSource);
254                    } catch (RemoteException e) {
255                    }
256                    mHeld = true;
257                }
258            }
259        }
260
261        /**
262         * Makes sure the device is on at the level you asked when you created
263         * the wake lock. The lock will be released after the given timeout.
264         *
265         * @param timeout Release the lock after the give timeout in milliseconds.
266         */
267        public void acquire(long timeout) {
268            acquire();
269            mHandler.postDelayed(mReleaser, timeout);
270        }
271
272
273        /**
274         * Release your claim to the CPU or screen being on.
275         *
276         * <p>
277         * It may turn off shortly after you release it, or it may not if there
278         * are other wake locks held.
279         */
280        public void release()
281        {
282            release(0);
283        }
284
285        /**
286         * Release your claim to the CPU or screen being on.
287         * @param flags Combination of flag values to modify the release behavior.
288         *              Currently only {@link #WAIT_FOR_PROXIMITY_NEGATIVE} is supported.
289         *
290         * <p>
291         * It may turn off shortly after you release it, or it may not if there
292         * are other wake locks held.
293         *
294         * {@hide}
295         */
296        public void release(int flags)
297        {
298            synchronized (mToken) {
299                if (!mRefCounted || --mCount == 0) {
300                    try {
301                        mService.releaseWakeLock(mToken, flags);
302                    } catch (RemoteException e) {
303                    }
304                    mHeld = false;
305                }
306                if (mCount < 0) {
307                    throw new RuntimeException("WakeLock under-locked " + mTag);
308                }
309            }
310        }
311
312        public boolean isHeld()
313        {
314            synchronized (mToken) {
315                return mHeld;
316            }
317        }
318
319        public void setWorkSource(WorkSource ws) {
320            synchronized (mToken) {
321                if (ws != null && ws.size() == 0) {
322                    ws = null;
323                }
324                boolean changed = true;
325                if (ws == null) {
326                    mWorkSource = null;
327                } else if (mWorkSource == null) {
328                    changed = mWorkSource != null;
329                    mWorkSource = new WorkSource(ws);
330                } else {
331                    changed = mWorkSource.diff(ws);
332                    if (changed) {
333                        mWorkSource.set(ws);
334                    }
335                }
336                if (changed && mHeld) {
337                    try {
338                        mService.updateWakeLockWorkSource(mToken, mWorkSource);
339                    } catch (RemoteException e) {
340                    }
341                }
342            }
343        }
344
345        public String toString() {
346            synchronized (mToken) {
347                return "WakeLock{"
348                    + Integer.toHexString(System.identityHashCode(this))
349                    + " held=" + mHeld + ", refCount=" + mCount + "}";
350            }
351        }
352
353        @Override
354        protected void finalize() throws Throwable
355        {
356            synchronized (mToken) {
357                if (mHeld) {
358                    Log.wtf(TAG, "WakeLock finalized while still held: " + mTag);
359                    try {
360                        mService.releaseWakeLock(mToken, 0);
361                    } catch (RemoteException e) {
362                    }
363                }
364            }
365        }
366    }
367
368    /**
369     * Get a wake lock at the level of the flags parameter.  Call
370     * {@link WakeLock#acquire() acquire()} on the object to acquire the
371     * wake lock, and {@link WakeLock#release release()} when you are done.
372     *
373     * {@samplecode
374     *PowerManager pm = (PowerManager)mContext.getSystemService(
375     *                                          Context.POWER_SERVICE);
376     *PowerManager.WakeLock wl = pm.newWakeLock(
377     *                                      PowerManager.SCREEN_DIM_WAKE_LOCK
378     *                                      | PowerManager.ON_AFTER_RELEASE,
379     *                                      TAG);
380     *wl.acquire();
381     * // ...
382     *wl.release();
383     * }
384     *
385     * @param flags Combination of flag values defining the requested behavior of the WakeLock.
386     * @param tag Your class name (or other tag) for debugging purposes.
387     *
388     * @see WakeLock#acquire()
389     * @see WakeLock#release()
390     */
391    public WakeLock newWakeLock(int flags, String tag)
392    {
393        if (tag == null) {
394            throw new NullPointerException("tag is null in PowerManager.newWakeLock");
395        }
396        return new WakeLock(flags, tag);
397    }
398
399    /**
400     * User activity happened.
401     * <p>
402     * Turns the device from whatever state it's in to full on, and resets
403     * the auto-off timer.
404     *
405     * @param when is used to order this correctly with the wake lock calls.
406     *          This time should be in the {@link SystemClock#uptimeMillis
407     *          SystemClock.uptimeMillis()} time base.
408     * @param noChangeLights should be true if you don't want the lights to
409     *          turn on because of this event.  This is set when the power
410     *          key goes down.  We want the device to stay on while the button
411     *          is down, but we're about to turn off.  Otherwise the lights
412     *          flash on and then off and it looks weird.
413     */
414    public void userActivity(long when, boolean noChangeLights)
415    {
416        try {
417            mService.userActivity(when, noChangeLights);
418        } catch (RemoteException e) {
419        }
420    }
421
422   /**
423     * Force the device to go to sleep. Overrides all the wake locks that are
424     * held.
425     *
426     * @param time is used to order this correctly with the wake lock calls.
427     *          The time  should be in the {@link SystemClock#uptimeMillis
428     *          SystemClock.uptimeMillis()} time base.
429     */
430    public void goToSleep(long time)
431    {
432        try {
433            mService.goToSleep(time);
434        } catch (RemoteException e) {
435        }
436    }
437
438    /**
439     * sets the brightness of the backlights (screen, keyboard, button).
440     *
441     * @param brightness value from 0 to 255
442     *
443     * {@hide}
444     */
445    public void setBacklightBrightness(int brightness)
446    {
447        try {
448            mService.setBacklightBrightness(brightness);
449        } catch (RemoteException e) {
450        }
451    }
452
453   /**
454     * Returns the set of flags for {@link #newWakeLock(int, String) newWakeLock()}
455     * that are supported on the device.
456     * For example, to test to see if the {@link #PROXIMITY_SCREEN_OFF_WAKE_LOCK}
457     * is supported:
458     *
459     * {@samplecode
460     * PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
461     * int supportedFlags = pm.getSupportedWakeLockFlags();
462     *  boolean proximitySupported = ((supportedFlags & PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)
463     *                                  == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK);
464     * }
465     *
466     * @return the set of supported WakeLock flags.
467     *
468     * {@hide}
469     */
470    public int getSupportedWakeLockFlags()
471    {
472        try {
473            return mService.getSupportedWakeLockFlags();
474        } catch (RemoteException e) {
475            return 0;
476        }
477    }
478
479    /**
480      * Returns whether the screen is currently on. The screen could be bright
481      * or dim.
482      *
483      * {@samplecode
484      * PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
485      * boolean isScreenOn = pm.isScreenOn();
486      * }
487      *
488      * @return whether the screen is on (bright or dim).
489      */
490    public boolean isScreenOn()
491    {
492        try {
493            return mService.isScreenOn();
494        } catch (RemoteException e) {
495            return false;
496        }
497    }
498
499    /**
500     * Reboot the device.  Will not return if the reboot is
501     * successful.  Requires the {@link android.Manifest.permission#REBOOT}
502     * permission.
503     *
504     * @param reason code to pass to the kernel (e.g., "recovery") to
505     *               request special boot modes, or null.
506     */
507    public void reboot(String reason)
508    {
509        try {
510            mService.reboot(reason);
511        } catch (RemoteException e) {
512        }
513    }
514
515    private PowerManager()
516    {
517    }
518
519    /**
520     * {@hide}
521     */
522    public PowerManager(IPowerManager service, Handler handler)
523    {
524        mService = service;
525        mHandler = handler;
526    }
527
528    /**
529     *  TODO: It would be nice to be able to set the poke lock here,
530     *  but I'm not sure what would be acceptable as an interface -
531     *  either a PokeLock object (like WakeLock) or, possibly just a
532     *  method call to set the poke lock.
533     */
534
535    IPowerManager mService;
536    Handler mHandler;
537}
538