diff options
author | Jeff Brown <jeffbrown@google.com> | 2014-02-21 02:07:45 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-02-21 02:07:45 +0000 |
commit | ac8bdfe4f06367e06e43d8759b6009b483a5e668 (patch) | |
tree | ba755b37f08295e213c7b2c945b30badd92d3a33 | |
parent | 0e3b52e5e89ddebeba83ff9ee4360422293c2ee8 (diff) | |
parent | 10102e4c0e501333a12b38a5cfe709d1558d84dd (diff) | |
download | frameworks_base-ac8bdfe4f06367e06e43d8759b6009b483a5e668.zip frameworks_base-ac8bdfe4f06367e06e43d8759b6009b483a5e668.tar.gz frameworks_base-ac8bdfe4f06367e06e43d8759b6009b483a5e668.tar.bz2 |
Merge "resolved conflicts for merge of baaa080b to master"
32 files changed, 1376 insertions, 277 deletions
@@ -183,6 +183,7 @@ LOCAL_SRC_FILES += \ core/java/android/print/IWriteResultCallback.aidl \ core/java/android/printservice/IPrintService.aidl \ core/java/android/printservice/IPrintServiceClient.aidl \ + core/java/android/service/dreams/IDozeHardware.aidl \ core/java/android/service/dreams/IDreamManager.aidl \ core/java/android/service/dreams/IDreamService.aidl \ core/java/android/service/wallpaper/IWallpaperConnection.aidl \ diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 0439eeb..4d4c337 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -191,6 +191,18 @@ public final class PowerManager { public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = 0x00000020; /** + * Wake lock level: Put the screen in a low power state and allow the CPU to suspend + * if no other wake locks are held. + * <p> + * This is used by the dream manager to implement doze mode. It currently + * has no effect unless the power manager is in the dozing state. + * </p> + * + * {@hide} + */ + public static final int DOZE_WAKE_LOCK = 0x00000040; + + /** * Mask for the wake lock level component of a combined wake lock level and flags integer. * * @hide @@ -437,6 +449,7 @@ public final class PowerManager { case SCREEN_BRIGHT_WAKE_LOCK: case FULL_WAKE_LOCK: case PROXIMITY_SCREEN_OFF_WAKE_LOCK: + case DOZE_WAKE_LOCK: break; default: throw new IllegalArgumentException("Must specify a valid wake lock level."); diff --git a/core/java/android/service/dreams/DozeHardware.java b/core/java/android/service/dreams/DozeHardware.java new file mode 100644 index 0000000..b5e7f43 --- /dev/null +++ b/core/java/android/service/dreams/DozeHardware.java @@ -0,0 +1,77 @@ +/** + * Copyright (C) 2014 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 android.service.dreams; + +import android.os.RemoteException; +import android.util.Log; + +/** + * Provides access to low-level hardware features that a dream may use to provide + * a richer user experience while dozing. + * <p> + * This class contains functions that should be called by the dream to configure + * hardware before starting to doze and allowing the application processor to suspend. + * For example, the dream may provide the hardware with enough information to render + * some content on its own without any further assistance from the application processor. + * </p><p> + * This object is obtained by calling {@link DreamService#getDozeHardware()}. + * </p> + * + * @hide experimental + */ +public final class DozeHardware { + private static final String TAG = "DozeHardware"; + + public static final String MSG_ENABLE_MCU = "enable_mcu"; + + public static final byte[] VALUE_ON = "on".getBytes(); + public static final byte[] VALUE_OFF = "off".getBytes(); + + private final IDozeHardware mHardware; + + DozeHardware(IDozeHardware hardware) { + mHardware = hardware; + } + + /** + * Sets whether to enable the microcontroller. + * + * @param enable If true, enables the MCU otherwise disables it. + */ + public void setEnableMcu(boolean enable) { + sendMessage(MSG_ENABLE_MCU, enable ? VALUE_ON : VALUE_OFF); + } + + /** + * Sends a message to the doze hardware module. + * + * @param msg The name of the message to send. + * @param arg An optional argument data blob, may be null. + * @return A result data blob, may be null. + */ + public byte[] sendMessage(String msg, byte[] arg) { + if (msg == null) { + throw new IllegalArgumentException("msg must not be null"); + } + + try { + return mHardware.sendMessage(msg, arg); + } catch (RemoteException ex) { + Log.e(TAG, "Failed to send message to doze hardware module.", ex); + return null; + } + } +} diff --git a/core/java/android/service/dreams/DreamManagerInternal.java b/core/java/android/service/dreams/DreamManagerInternal.java index 17ea996..9f7ddba 100644 --- a/core/java/android/service/dreams/DreamManagerInternal.java +++ b/core/java/android/service/dreams/DreamManagerInternal.java @@ -25,7 +25,7 @@ public abstract class DreamManagerInternal { /** * Called by the power manager to start a dream. */ - public abstract void startDream(); + public abstract void startDream(boolean doze); /** * Called by the power manager to stop a dream. diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index f6b6c89..1abb1d7 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -20,12 +20,14 @@ import java.io.PrintWriter; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.app.AlarmManager; import android.app.Service; import android.content.Intent; import android.graphics.PixelFormat; import android.graphics.drawable.ColorDrawable; import android.os.Handler; import android.os.IBinder; +import android.os.RemoteException; import android.os.ServiceManager; import android.util.Slog; import android.view.ActionMode; @@ -42,6 +44,8 @@ import android.view.WindowManager.LayoutParams; import android.view.accessibility.AccessibilityEvent; import com.android.internal.policy.PolicyManager; +import com.android.internal.util.DumpUtils; +import com.android.internal.util.DumpUtils.Dump; /** * Extend this class to implement a custom dream (available to the user as a "Daydream"). @@ -145,19 +149,26 @@ public class DreamService extends Service implements Window.Callback { */ public static final String DREAM_META_DATA = "android.service.dream"; + private final IDreamManager mSandman; private final Handler mHandler = new Handler(); private IBinder mWindowToken; private Window mWindow; private WindowManager mWindowManager; - private IDreamManager mSandman; private boolean mInteractive = false; private boolean mLowProfile = true; private boolean mFullscreen = false; private boolean mScreenBright = true; private boolean mFinished; + private boolean mCanDoze; + private boolean mDozing; + private DozeHardware mDozeHardware; private boolean mDebug = false; + public DreamService() { + mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE)); + } + /** * @hide */ @@ -444,9 +455,11 @@ public class DreamService extends Service implements Window.Callback { * correct interactions with it (seeing when it is cleared etc). */ public void setLowProfile(boolean lowProfile) { - mLowProfile = lowProfile; - int flag = View.SYSTEM_UI_FLAG_LOW_PROFILE; - applySystemUiVisibilityFlags(mLowProfile ? flag : 0, flag); + if (mLowProfile != lowProfile) { + mLowProfile = lowProfile; + int flag = View.SYSTEM_UI_FLAG_LOW_PROFILE; + applySystemUiVisibilityFlags(mLowProfile ? flag : 0, flag); + } } /** @@ -467,9 +480,11 @@ public class DreamService extends Service implements Window.Callback { * will be cleared. */ public void setFullscreen(boolean fullscreen) { - mFullscreen = fullscreen; - int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN; - applyWindowFlags(mFullscreen ? flag : 0, flag); + if (mFullscreen != fullscreen) { + mFullscreen = fullscreen; + int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN; + applyWindowFlags(mFullscreen ? flag : 0, flag); + } } /** @@ -487,14 +502,16 @@ public class DreamService extends Service implements Window.Callback { * @param screenBright True to keep the screen bright while dreaming. */ public void setScreenBright(boolean screenBright) { - mScreenBright = screenBright; - int flag = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; - applyWindowFlags(mScreenBright ? flag : 0, flag); + if (mScreenBright != screenBright) { + mScreenBright = screenBright; + int flag = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; + applyWindowFlags(mScreenBright ? flag : 0, flag); + } } /** - * Returns whether or not this dream keeps the screen bright while dreaming. Defaults to false, - * allowing the screen to dim if necessary. + * Returns whether or not this dream keeps the screen bright while dreaming. + * Defaults to false, allowing the screen to dim if necessary. * * @see #setScreenBright(boolean) */ @@ -503,6 +520,119 @@ public class DreamService extends Service implements Window.Callback { } /** + * Returns true if this dream is allowed to doze. + * <p> + * The value returned by this method is only meaningful when the dream has started. + * </p> + * + * @return True if this dream can doze. + * @see #startDozing + * @hide experimental + */ + public boolean canDoze() { + return mCanDoze; + } + + /** + * Starts dozing, entering a deep dreamy sleep. + * <p> + * Dozing enables the system to conserve power while the user is not actively interacting + * with the device. While dozing, the display will remain on in a low-power state + * and will continue to show its previous contents but the application processor and + * other system components will be allowed to suspend when possible. + * </p><p> + * While the application processor is suspended, the dream may stop executing code + * for long periods of time. Prior to being suspended, the dream may schedule periodic + * wake-ups to render new content by scheduling an alarm with the {@link AlarmManager}. + * The dream may also keep the CPU awake by acquiring a + * {@link android.os.PowerManager#PARTIAL_WAKE_LOCK partial wake lock} when necessary. + * Note that since the purpose of doze mode is to conserve power (especially when + * running on battery), the dream should not wake the CPU very often or keep it + * awake for very long. + * </p><p> + * It is a good idea to call this method some time after the dream's entry animation + * has completed and the dream is ready to doze. It is important to completely + * finish all of the work needed before dozing since the application processor may + * be suspended at any moment once this method is called unless other wake locks + * are being held. + * </p><p> + * Call {@link #stopDozing} or {@link #finish} to stop dozing. + * </p> + * + * @see #stopDozing + * @hide experimental + */ + public void startDozing() { + if (mCanDoze && !mDozing) { + mDozing = true; + try { + mSandman.startDozing(mWindowToken); + } catch (RemoteException ex) { + // system server died + } + } + } + + /** + * Stops dozing, returns to active dreaming. + * <p> + * This method reverses the effect of {@link #startDozing}. From this moment onward, + * the application processor will be kept awake as long as the dream is running + * or until the dream starts dozing again. + * </p> + * + * @see #startDozing + * @hide experimental + */ + public void stopDozing() { + if (mDozing) { + mDozing = false; + try { + mSandman.stopDozing(mWindowToken); + } catch (RemoteException ex) { + // system server died + } + } + } + + /** + * Returns true if the dream will allow the system to enter a low-power state while + * it is running without actually turning off the screen. Defaults to false, + * keeping the application processor awake while the dream is running. + * + * @return True if the dream is dozing. + * + * @see #setDozing(boolean) + * @hide experimental + */ + public boolean isDozing() { + return mDozing; + } + + /** + * Gets an object that may be used to access low-level hardware features that a + * dream may use to provide a richer user experience while dozing. + * + * @return An instance of {@link DozeHardware} or null if this device does not offer + * hardware support for dozing. + * + * @hide experimental + */ + public DozeHardware getDozeHardware() { + if (mCanDoze && mDozeHardware == null) { + try { + IDozeHardware hardware = mSandman.getDozeHardware(mWindowToken); + if (hardware != null) { + mDozeHardware = new DozeHardware(hardware); + } + } catch (RemoteException ex) { + // system server died + } + } + return mDozeHardware; + } + + /** * Called when this Dream is constructed. */ @Override @@ -536,7 +666,11 @@ public class DreamService extends Service implements Window.Callback { } /** - * Stops the dream, detaches from the window, and wakes up. + * Stops the dream and detaches from the window. + * <p> + * When the dream ends, the system will be allowed to go to sleep fully unless there + * is a reason for it to be awake such as recent user activity or wake locks being held. + * </p> */ public final void finish() { if (mDebug) Slog.v(TAG, "finish()"); @@ -557,10 +691,6 @@ public class DreamService extends Service implements Window.Callback { // end public api - private void loadSandman() { - mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE)); - } - /** * Called by DreamController.stopDream() when the Dream is about to be unbound and destroyed. * @@ -572,23 +702,16 @@ public class DreamService extends Service implements Window.Callback { return; } - try { - onDreamingStopped(); - } catch (Throwable t) { - Slog.w(TAG, "Crashed in onDreamingStopped()", t); - // we were going to stop anyway - } + if (mDebug) Slog.v(TAG, "detach(): Calling onDreamingStopped()"); + onDreamingStopped(); if (mDebug) Slog.v(TAG, "detach(): Removing window from window manager"); - try { - // force our window to be removed synchronously - mWindowManager.removeViewImmediate(mWindow.getDecorView()); - // the following will print a log message if it finds any other leaked windows - WindowManagerGlobal.getInstance().closeAll(mWindowToken, - this.getClass().getName(), "Dream"); - } catch (Throwable t) { - Slog.w(TAG, "Crashed removing window view", t); - } + + // force our window to be removed synchronously + mWindowManager.removeViewImmediate(mWindow.getDecorView()); + // the following will print a log message if it finds any other leaked windows + WindowManagerGlobal.getInstance().closeAll(mWindowToken, + this.getClass().getName(), "Dream"); mWindow = null; mWindowToken = null; @@ -601,23 +724,30 @@ public class DreamService extends Service implements Window.Callback { * * @param windowToken A window token that will allow a window to be created in the correct layer. */ - private final void attach(IBinder windowToken) { + private final void attach(IBinder windowToken, boolean canDoze) { if (mWindowToken != null) { Slog.e(TAG, "attach() called when already attached with token=" + mWindowToken); return; } + if (mFinished) { + Slog.w(TAG, "attach() called after dream already finished"); + try { + mSandman.finishSelf(windowToken); + } catch (RemoteException ex) { + // system server died + } + return; + } if (mDebug) Slog.v(TAG, "Attached on thread " + Thread.currentThread().getId()); - if (mSandman == null) { - loadSandman(); - } mWindowToken = windowToken; mWindow = PolicyManager.makeNewWindow(this); mWindow.setCallback(this); mWindow.requestFeature(Window.FEATURE_NO_TITLE); mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000)); mWindow.setFormat(PixelFormat.OPAQUE); + mCanDoze = canDoze; if (mDebug) Slog.v(TAG, String.format("Attaching window token: %s to window of type %s", windowToken, WindowManager.LayoutParams.TYPE_DREAM)); @@ -642,40 +772,25 @@ public class DreamService extends Service implements Window.Callback { mWindowManager = mWindow.getWindowManager(); if (mDebug) Slog.v(TAG, "Window added on thread " + Thread.currentThread().getId()); - try { - applySystemUiVisibilityFlags( - (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0), - View.SYSTEM_UI_FLAG_LOW_PROFILE); - getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes()); - } catch (Throwable t) { - Slog.w(TAG, "Crashed adding window view", t); - safelyFinish(); - return; - } + applySystemUiVisibilityFlags( + (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0), + View.SYSTEM_UI_FLAG_LOW_PROFILE); + getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes()); // start it up mHandler.post(new Runnable() { @Override public void run() { - try { - onDreamingStarted(); - } catch (Throwable t) { - Slog.w(TAG, "Crashed in onDreamingStarted()", t); - safelyFinish(); - } + if (mDebug) Slog.v(TAG, "Calling onDreamingStarted()"); + onDreamingStarted(); } }); } private void safelyFinish() { if (mDebug) Slog.v(TAG, "safelyFinish()"); - try { - finish(); - } catch (Throwable t) { - Slog.w(TAG, "Crashed in safelyFinish()", t); - finishInternal(); - return; - } + + finish(); if (!mFinished) { Slog.w(TAG, "Bad dream, did not call super.finish()"); @@ -685,19 +800,21 @@ public class DreamService extends Service implements Window.Callback { private void finishInternal() { if (mDebug) Slog.v(TAG, "finishInternal() mFinished = " + mFinished); - if (mFinished) return; - try { + + if (!mFinished) { mFinished = true; - if (mSandman != null) { - mSandman.finishSelf(mWindowToken); + if (mWindowToken == null) { + Slog.w(TAG, "Finish was called before the dream was attached."); } else { - Slog.w(TAG, "No dream manager found"); + try { + mSandman.finishSelf(mWindowToken); + } catch (RemoteException ex) { + // system server died + } } - stopSelf(); // if launched via any other means - } catch (Throwable t) { - Slog.w(TAG, "Crashed in finishInternal()", t); + stopSelf(); // if launched via any other means } } @@ -732,32 +849,39 @@ public class DreamService extends Service implements Window.Callback { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - super.dump(fd, pw, args); - - pw.print(TAG + ": "); - if (mWindowToken == null) { - pw.println("stopped"); - } else { - pw.println("running (token=" + mWindowToken + ")"); - } - pw.println(" window: " + mWindow); - pw.print(" flags:"); - if (isInteractive()) pw.print(" interactive"); - if (isLowProfile()) pw.print(" lowprofile"); - if (isFullscreen()) pw.print(" fullscreen"); - if (isScreenBright()) pw.print(" bright"); - pw.println(); + DumpUtils.dumpAsync(mHandler, new Dump() { + @Override + public void dump(PrintWriter pw) { + pw.print(TAG + ": "); + if (mWindowToken == null) { + pw.println("stopped"); + } else { + pw.println("running (token=" + mWindowToken + ")"); + } + pw.println(" window: " + mWindow); + pw.print(" flags:"); + if (isInteractive()) pw.print(" interactive"); + if (isLowProfile()) pw.print(" lowprofile"); + if (isFullscreen()) pw.print(" fullscreen"); + if (isScreenBright()) pw.print(" bright"); + if (isDozing()) pw.print(" dozing"); + pw.println(); + } + }, pw, 1000); } - private class DreamServiceWrapper extends IDreamService.Stub { - public void attach(final IBinder windowToken) { + private final class DreamServiceWrapper extends IDreamService.Stub { + @Override + public void attach(final IBinder windowToken, final boolean canDoze) { mHandler.post(new Runnable() { @Override public void run() { - DreamService.this.attach(windowToken); + DreamService.this.attach(windowToken, canDoze); } }); } + + @Override public void detach() { mHandler.post(new Runnable() { @Override diff --git a/core/java/android/service/dreams/IDozeHardware.aidl b/core/java/android/service/dreams/IDozeHardware.aidl new file mode 100644 index 0000000..f5a657b --- /dev/null +++ b/core/java/android/service/dreams/IDozeHardware.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2014 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 android.service.dreams; + +/** + * @hide + */ +interface IDozeHardware { + byte[] sendMessage(String msg, in byte[] arg); +} diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl index 1c1b390..2718e31 100644 --- a/core/java/android/service/dreams/IDreamManager.aidl +++ b/core/java/android/service/dreams/IDreamManager.aidl @@ -16,10 +16,11 @@ package android.service.dreams; +import android.content.ComponentName; import android.os.Bundle; import android.os.ParcelFileDescriptor; -import android.content.ComponentName; import android.os.IBinder; +import android.service.dreams.IDozeHardware; /** @hide */ interface IDreamManager { @@ -31,4 +32,7 @@ interface IDreamManager { void testDream(in ComponentName componentName); boolean isDreaming(); void finishSelf(in IBinder token); + void startDozing(in IBinder token); + void stopDozing(in IBinder token); + IDozeHardware getDozeHardware(in IBinder token); }
\ No newline at end of file diff --git a/core/java/android/service/dreams/IDreamService.aidl b/core/java/android/service/dreams/IDreamService.aidl index 99dc0b7..bd58f1d 100644 --- a/core/java/android/service/dreams/IDreamService.aidl +++ b/core/java/android/service/dreams/IDreamService.aidl @@ -20,6 +20,6 @@ package android.service.dreams; * @hide */ oneway interface IDreamService { - void attach(IBinder windowToken); + void attach(IBinder windowToken, boolean canDoze); void detach(); } diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index d9e140e..75656545 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -119,20 +119,6 @@ public interface WindowManagerPolicy { public final static int ACTION_PASS_TO_USER = 0x00000001; /** - * This key event should wake the device. - * To be returned from {@link #interceptKeyBeforeQueueing}. - * Do not return this and {@link #ACTION_GO_TO_SLEEP} or {@link #ACTION_PASS_TO_USER}. - */ - public final static int ACTION_WAKE_UP = 0x00000002; - - /** - * This key event should put the device to sleep (and engage keyguard if necessary) - * To be returned from {@link #interceptKeyBeforeQueueing}. - * Do not return this and {@link #ACTION_WAKE_UP} or {@link #ACTION_PASS_TO_USER}. - */ - public final static int ACTION_GO_TO_SLEEP = 0x00000004; - - /** * Interface to the Window Manager state associated with a particular * window. You can hold on to an instance of this interface from the call * to prepareAddWindow() until removeWindow(). @@ -760,8 +746,7 @@ public interface WindowManagerPolicy { * @param policyFlags The policy flags associated with the key. * @param isScreenOn True if the screen is already on * - * @return The bitwise or of the {@link #ACTION_PASS_TO_USER}, - * {@link #ACTION_WAKE_UP} and {@link #ACTION_GO_TO_SLEEP} flags. + * @return Actions flags: may be {@link #ACTION_PASS_TO_USER}. */ public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn); @@ -774,10 +759,9 @@ public interface WindowManagerPolicy { * because it's the most fragile. * @param policyFlags The policy flags associated with the motion. * - * @return The bitwise or of the {@link #ACTION_PASS_TO_USER}, - * {@link #ACTION_WAKE_UP} and {@link #ACTION_GO_TO_SLEEP} flags. + * @return Actions flags: may be {@link #ACTION_PASS_TO_USER}. */ - public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags); + public int interceptMotionBeforeQueueingWhenScreenOff(long whenNanos, int policyFlags); /** * Called from the input dispatcher thread before a key is dispatched to a window. diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index b1b1f42..519e2ae 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -658,6 +658,11 @@ Must be in the range specified by minimum and maximum. --> <integer name="config_screenBrightnessSettingDefault">102</integer> + <!-- Screen brightness used to dim the screen while dozing in a very low power state. + May be less than the minimum allowed brightness setting + that can be set by the user. --> + <integer name="config_screenBrightnessDoze">1</integer> + <!-- Screen brightness used to dim the screen when the user activity timeout expires. May be less than the minimum allowed brightness setting that can be set by the user. --> @@ -1089,6 +1094,70 @@ <!-- ComponentName of the default dream (Settings.Secure.SCREENSAVER_COMPONENT) --> <string name="config_dreamsDefaultComponent">com.google.android.deskclock/com.android.deskclock.Screensaver</string> + <!-- Are we allowed to dream while not plugged in? --> + <bool name="config_dreamsEnabledOnBattery">false</bool> + <!-- Minimum battery level to allow dreaming when powered. + Use -1 to disable this safety feature. --> + <integer name="config_dreamsBatteryLevelMinimumWhenPowered">-1</integer> + <!-- Minimum battery level to allow dreaming when not powered. + Use -1 to disable this safety feature. --> + <integer name="config_dreamsBatteryLevelMinimumWhenNotPowered">15</integer> + <!-- If the battery level drops by this percentage and the user activity timeout + has expired, then assume the device is receiving insufficient current to charge + effectively and terminate the dream. Use -1 to disable this safety feature. --> + <integer name="config_dreamsBatteryLevelDrainCutoff">5</integer> + + <!-- ComponentName of a dream to show whenever the system would otherwise have + gone to sleep. When the PowerManager is asked to go to sleep, it will instead + try to start this dream if possible. The dream should typically call startDozing() + to put the display into a low power state and allow the application processor + to be suspended. When the dream ends, the system will go to sleep as usual. + Specify the component name (Settings.Secure.SCREENSAVER_COMPONENT) or an + empty string if none. + + Note that doze dreams are not subject to the same start conditions as ordinary dreams. + Doze dreams will run whenever the power manager is in a dozing state. --> + <string name="config_dozeComponent"></string> + + <!-- Power Management: Specifies whether to decouple the auto-suspend state of the + device from the display on/off state. + + When false, autosuspend_disable() will be called before the display is turned on + and autosuspend_enable() will be called after the display is turned off. + This mode provides best compatibility for devices using legacy power management + features such as early suspend / late resume. + + When true, autosuspend_display() and autosuspend_enable() will be called + independently of whether the display is being turned on or off. This mode + enables the power manager to suspend the application processor while the + display is on. + + This resource should be set to "true" when a doze component has been specified + to maximize power savings but not all devices support it. + + Refer to autosuspend.h for details. + --> + <bool name="config_powerDecoupleAutoSuspendModeFromDisplay">false</bool> + + <!-- Power Management: Specifies whether to decouple the interactive state of the + device from the display on/off state. + + When false, setInteractive(..., true) will be called before the display is turned on + and setInteractive(..., false) will be called after the display is turned off. + This mode provides best compatibility for devices that expect the interactive + state to be tied to the display state. + + When true, setInteractive(...) will be called independently of whether the display + is being turned on or off. This mode enables the power manager to reduce + clocks and disable the touch controller while the display is on. + + This resource should be set to "true" when a doze component has been specified + to maximize power savings but not all devices support it. + + Refer to power.h for details. + --> + <bool name="config_powerDecoupleInteractiveModeFromDisplay">false</bool> + <!-- Base "touch slop" value used by ViewConfiguration as a movement threshold where scrolling should begin. --> <dimen name="config_viewConfigurationTouchSlop">8dp</dimen> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index e574e8d..b1bf781 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1500,6 +1500,7 @@ <java-symbol type="integer" name="config_screenBrightnessSettingMaximum" /> <java-symbol type="integer" name="config_screenBrightnessSettingDefault" /> <java-symbol type="integer" name="config_screenBrightnessDim" /> + <java-symbol type="integer" name="config_screenBrightnessDoze" /> <java-symbol type="integer" name="config_shutdownBatteryTemperature" /> <java-symbol type="integer" name="config_undockedHdmiRotation" /> <java-symbol type="integer" name="config_virtualKeyQuietTimeMillis" /> @@ -1604,11 +1605,18 @@ <java-symbol type="xml" name="storage_list" /> <java-symbol type="bool" name="config_dreamsSupported" /> <java-symbol type="bool" name="config_dreamsEnabledByDefault" /> + <java-symbol type="bool" name="config_dreamsEnabledOnBattery" /> <java-symbol type="bool" name="config_dreamsActivatedOnDockByDefault" /> <java-symbol type="bool" name="config_dreamsActivatedOnSleepByDefault" /> + <java-symbol type="integer" name="config_dreamsBatteryLevelMinimumWhenPowered" /> + <java-symbol type="integer" name="config_dreamsBatteryLevelMinimumWhenNotPowered" /> + <java-symbol type="integer" name="config_dreamsBatteryLevelDrainCutoff" /> <java-symbol type="string" name="config_dreamsDefaultComponent" /> + <java-symbol type="string" name="config_dozeComponent" /> <java-symbol type="string" name="enable_explore_by_touch_warning_title" /> <java-symbol type="string" name="enable_explore_by_touch_warning_message" /> + <java-symbol type="bool" name="config_powerDecoupleAutoSuspendModeFromDisplay" /> + <java-symbol type="bool" name="config_powerDecoupleInteractiveModeFromDisplay" /> <java-symbol type="layout" name="resolver_list" /> <java-symbol type="id" name="resolver_list" /> diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index ada649d..7886f5d 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -3799,14 +3799,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (keyCode == KeyEvent.KEYCODE_POWER) { policyFlags |= WindowManagerPolicy.FLAG_WAKE; } - final boolean isWakeKey = (policyFlags & (WindowManagerPolicy.FLAG_WAKE - | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0; if (DEBUG_INPUT) { Log.d(TAG, "interceptKeyTq keycode=" + keyCode + " screenIsOn=" + isScreenOn + " keyguardActive=" + keyguardActive - + " policyFlags=" + Integer.toHexString(policyFlags) - + " isWakeKey=" + isWakeKey); + + " policyFlags=" + Integer.toHexString(policyFlags)); } if (down && (policyFlags & WindowManagerPolicy.FLAG_VIRTUAL) != 0 @@ -3823,6 +3820,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // the device some other way (which is why we have an exemption here for injected // events). int result; + boolean isWakeKey = (policyFlags & (WindowManagerPolicy.FLAG_WAKE + | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0; if (isScreenOn || (isInjected && !isWakeKey)) { // When the screen is on or if the key is injected pass the key to the application. result = ACTION_PASS_TO_USER; @@ -3830,8 +3829,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // When the screen is off and the key is not injected, determine whether // to wake the device but don't pass the key to the application. result = 0; - if (down && isWakeKey && isWakeKeyWhenScreenOff(keyCode)) { - result |= ACTION_WAKE_UP; + if (isWakeKey && (!down || !isWakeKeyWhenScreenOff(keyCode))) { + isWakeKey = false; } } @@ -3941,7 +3940,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if ((mEndcallBehavior & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) { - result = (result & ~ACTION_WAKE_UP) | ACTION_GO_TO_SLEEP; + mPowerManager.goToSleep(event.getEventTime()); + isWakeKey = false; } } } @@ -3988,7 +3988,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { mPowerKeyTriggered = false; cancelPendingScreenshotChordAction(); if (interceptPowerKeyUp(canceled || mPendingPowerKeyUpCanceled)) { - result = (result & ~ACTION_WAKE_UP) | ACTION_GO_TO_SLEEP; + mPowerManager.goToSleep(event.getEventTime()); + isWakeKey = false; } mPendingPowerKeyUpCanceled = false; } @@ -4058,6 +4059,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { break; } } + + if (isWakeKey) { + mPowerManager.wakeUp(event.getEventTime()); + } return result; } @@ -4098,13 +4103,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override - public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags) { + public int interceptMotionBeforeQueueingWhenScreenOff(long whenNanos, int policyFlags) { int result = 0; final boolean isWakeMotion = (policyFlags & (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0; if (isWakeMotion) { - result |= ACTION_WAKE_UP; + mPowerManager.wakeUp(whenNanos / 1000000); } return result; } diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java index 85ef33e..649b5c9 100644 --- a/services/core/java/com/android/server/dreams/DreamController.java +++ b/services/core/java/com/android/server/dreams/DreamController.java @@ -85,6 +85,7 @@ final class DreamController { pw.println(" mToken=" + mCurrentDream.mToken); pw.println(" mName=" + mCurrentDream.mName); pw.println(" mIsTest=" + mCurrentDream.mIsTest); + pw.println(" mCanDoze=" + mCurrentDream.mCanDoze); pw.println(" mUserId=" + mCurrentDream.mUserId); pw.println(" mBound=" + mCurrentDream.mBound); pw.println(" mService=" + mCurrentDream.mService); @@ -94,15 +95,18 @@ final class DreamController { } } - public void startDream(Binder token, ComponentName name, boolean isTest, int userId) { + public void startDream(Binder token, ComponentName name, + boolean isTest, boolean canDoze, int userId) { stopDream(); // Close the notification shade. Don't need to send to all, but better to be explicit. mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL); - Slog.i(TAG, "Starting dream: name=" + name + ", isTest=" + isTest + ", userId=" + userId); + Slog.i(TAG, "Starting dream: name=" + name + + ", isTest=" + isTest + ", canDoze=" + canDoze + + ", userId=" + userId); - mCurrentDream = new DreamRecord(token, name, isTest, userId); + mCurrentDream = new DreamRecord(token, name, isTest, canDoze, userId); try { mIWindowManager.addWindowToken(token, WindowManager.LayoutParams.TYPE_DREAM); @@ -140,7 +144,8 @@ final class DreamController { final DreamRecord oldDream = mCurrentDream; mCurrentDream = null; Slog.i(TAG, "Stopping dream: name=" + oldDream.mName - + ", isTest=" + oldDream.mIsTest + ", userId=" + oldDream.mUserId); + + ", isTest=" + oldDream.mIsTest + ", canDoze=" + oldDream.mCanDoze + + ", userId=" + oldDream.mUserId); mHandler.removeCallbacks(mStopUnconnectedDreamRunnable); @@ -187,7 +192,7 @@ final class DreamController { private void attach(IDreamService service) { try { service.asBinder().linkToDeath(mCurrentDream, 0); - service.attach(mCurrentDream.mToken); + service.attach(mCurrentDream.mToken, mCurrentDream.mCanDoze); } catch (RemoteException ex) { Slog.e(TAG, "The dream service died unexpectedly.", ex); stopDream(); @@ -213,6 +218,7 @@ final class DreamController { public final Binder mToken; public final ComponentName mName; public final boolean mIsTest; + public final boolean mCanDoze; public final int mUserId; public boolean mBound; @@ -221,10 +227,11 @@ final class DreamController { public boolean mSentStartBroadcast; public DreamRecord(Binder token, ComponentName name, - boolean isTest, int userId) { + boolean isTest, boolean canDoze, int userId) { mToken = token; mName = name; mIsTest = isTest; + mCanDoze = canDoze; mUserId = userId; } diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index ffb113c..fd2f8a1 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -30,16 +30,20 @@ import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Binder; +import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.PowerManager; import android.os.SystemClock; +import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; import android.service.dreams.DreamManagerInternal; import android.service.dreams.DreamService; +import android.service.dreams.IDozeHardware; import android.service.dreams.IDreamManager; +import android.text.TextUtils; import android.util.Slog; import java.io.FileDescriptor; @@ -64,11 +68,16 @@ public final class DreamManagerService extends SystemService { private final DreamHandler mHandler; private final DreamController mController; private final PowerManager mPowerManager; + private final PowerManager.WakeLock mDozeWakeLock; + private final McuHal mMcuHal; // synchronized on self private Binder mCurrentDreamToken; private ComponentName mCurrentDreamName; private int mCurrentDreamUserId; private boolean mCurrentDreamIsTest; + private boolean mCurrentDreamCanDoze; + private boolean mCurrentDreamIsDozing; + private DozeHardwareWrapper mCurrentDreamDozeHardware; public DreamManagerService(Context context) { super(context); @@ -77,6 +86,12 @@ public final class DreamManagerService extends SystemService { mController = new DreamController(context, mHandler, mControllerListener); mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); + mDozeWakeLock = mPowerManager.newWakeLock(PowerManager.DOZE_WAKE_LOCK, TAG); + + mMcuHal = McuHal.open(); + if (mMcuHal != null) { + mMcuHal.reset(); + } } @Override @@ -103,10 +118,15 @@ public final class DreamManagerService extends SystemService { pw.println("DREAM MANAGER (dumpsys dreams)"); pw.println(); + pw.println("mMcuHal=" + mMcuHal); + pw.println(); pw.println("mCurrentDreamToken=" + mCurrentDreamToken); pw.println("mCurrentDreamName=" + mCurrentDreamName); pw.println("mCurrentDreamUserId=" + mCurrentDreamUserId); pw.println("mCurrentDreamIsTest=" + mCurrentDreamIsTest); + pw.println("mCurrentDreamCanDoze=" + mCurrentDreamCanDoze); + pw.println("mCurrentDreamIsDozing=" + mCurrentDreamIsDozing); + pw.println("mCurrentDreamDozeHardware=" + mCurrentDreamDozeHardware); pw.println(); DumpUtils.dumpAsync(mHandler, new DumpUtils.Dump() { @@ -163,16 +183,16 @@ public final class DreamManagerService extends SystemService { private void testDreamInternal(ComponentName dream, int userId) { synchronized (mLock) { - startDreamLocked(dream, true /*isTest*/, userId); + startDreamLocked(dream, true /*isTest*/, false /*canDoze*/, userId); } } - private void startDreamInternal() { - int userId = ActivityManager.getCurrentUser(); - ComponentName dream = chooseDreamForUser(userId); + private void startDreamInternal(boolean doze) { + final int userId = ActivityManager.getCurrentUser(); + final ComponentName dream = doze ? getDozeComponent() : chooseDreamForUser(userId); if (dream != null) { synchronized (mLock) { - startDreamLocked(dream, false /*isTest*/, userId); + startDreamLocked(dream, false /*isTest*/, doze, userId); } } } @@ -183,6 +203,44 @@ public final class DreamManagerService extends SystemService { } } + private void startDozingInternal(IBinder token) { + if (DEBUG) { + Slog.d(TAG, "Dream requested to start dozing: " + token); + } + + synchronized (mLock) { + if (mCurrentDreamToken == token && mCurrentDreamCanDoze + && !mCurrentDreamIsDozing) { + mCurrentDreamIsDozing = true; + mDozeWakeLock.acquire(); + } + } + } + + private void stopDozingInternal(IBinder token) { + if (DEBUG) { + Slog.d(TAG, "Dream requested to stop dozing: " + token); + } + + synchronized (mLock) { + if (mCurrentDreamToken == token && mCurrentDreamIsDozing) { + mCurrentDreamIsDozing = false; + mDozeWakeLock.release(); + } + } + } + + private IDozeHardware getDozeHardwareInternal(IBinder token) { + synchronized (mLock) { + if (mCurrentDreamToken == token && mCurrentDreamCanDoze + && mCurrentDreamDozeHardware == null && mMcuHal != null) { + mCurrentDreamDozeHardware = new DozeHardwareWrapper(); + return mCurrentDreamDozeHardware; + } + return null; + } + } + private ComponentName chooseDreamForUser(int userId) { ComponentName[] dreams = getDreamComponentsForUser(userId); return dreams != null && dreams.length != 0 ? dreams[0] : null; @@ -231,6 +289,20 @@ public final class DreamManagerService extends SystemService { return name == null ? null : ComponentName.unflattenFromString(name); } + private ComponentName getDozeComponent() { + // Read the component from a system property to facilitate debugging. + // Note that for production devices, the dream should actually be declared in + // a config.xml resource. + String name = Build.IS_DEBUGGABLE ? SystemProperties.get("debug.doze.component") : null; + if (TextUtils.isEmpty(name)) { + // Read the component from a config.xml resource. + // The value should be specified in a resource overlay for the product. + name = mContext.getResources().getString( + com.android.internal.R.string.config_dozeComponent); + } + return TextUtils.isEmpty(name) ? null : ComponentName.unflattenFromString(name); + } + private boolean serviceExists(ComponentName name) { try { return name != null && mContext.getPackageManager().getServiceInfo(name, 0) != null; @@ -240,9 +312,10 @@ public final class DreamManagerService extends SystemService { } private void startDreamLocked(final ComponentName name, - final boolean isTest, final int userId) { + final boolean isTest, final boolean canDoze, final int userId) { if (Objects.equal(mCurrentDreamName, name) && mCurrentDreamIsTest == isTest + && mCurrentDreamCanDoze == canDoze && mCurrentDreamUserId == userId) { return; } @@ -255,12 +328,13 @@ public final class DreamManagerService extends SystemService { mCurrentDreamToken = newToken; mCurrentDreamName = name; mCurrentDreamIsTest = isTest; + mCurrentDreamCanDoze = canDoze; mCurrentDreamUserId = userId; mHandler.post(new Runnable() { @Override public void run() { - mController.startDream(newToken, name, isTest, userId); + mController.startDream(newToken, name, isTest, canDoze, userId); } }); } @@ -284,7 +358,16 @@ public final class DreamManagerService extends SystemService { mCurrentDreamToken = null; mCurrentDreamName = null; mCurrentDreamIsTest = false; + mCurrentDreamCanDoze = false; mCurrentDreamUserId = 0; + if (mCurrentDreamIsDozing) { + mCurrentDreamIsDozing = false; + mDozeWakeLock.release(); + } + if (mCurrentDreamDozeHardware != null) { + mCurrentDreamDozeHardware.release(); + mCurrentDreamDozeHardware = null; + } } private void checkPermission(String permission) { @@ -473,12 +556,57 @@ public final class DreamManagerService extends SystemService { Binder.restoreCallingIdentity(ident); } } + + @Override // Binder call + public void startDozing(IBinder token) { + // Requires no permission, called by Dream from an arbitrary process. + if (token == null) { + throw new IllegalArgumentException("token must not be null"); + } + + final long ident = Binder.clearCallingIdentity(); + try { + startDozingInternal(token); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override // Binder call + public void stopDozing(IBinder token) { + // Requires no permission, called by Dream from an arbitrary process. + if (token == null) { + throw new IllegalArgumentException("token must not be null"); + } + + final long ident = Binder.clearCallingIdentity(); + try { + stopDozingInternal(token); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override // Binder call + public IDozeHardware getDozeHardware(IBinder token) { + // Requires no permission, called by Dream from an arbitrary process. + if (token == null) { + throw new IllegalArgumentException("token must not be null"); + } + + final long ident = Binder.clearCallingIdentity(); + try { + return getDozeHardwareInternal(token); + } finally { + Binder.restoreCallingIdentity(ident); + } + } } private final class LocalService extends DreamManagerInternal { @Override - public void startDream() { - startDreamInternal(); + public void startDream(boolean doze) { + startDreamInternal(doze); } @Override @@ -491,4 +619,37 @@ public final class DreamManagerService extends SystemService { return isDreamingInternal(); } } + + private final class DozeHardwareWrapper extends IDozeHardware.Stub { + private boolean mReleased; + + public void release() { + synchronized (mMcuHal) { + if (!mReleased) { + mReleased = true; + mMcuHal.reset(); + } + } + } + + @Override // Binder call + public byte[] sendMessage(String msg, byte[] arg) { + if (msg == null) { + throw new IllegalArgumentException("msg must not be null"); + } + + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mMcuHal) { + if (mReleased) { + throw new IllegalStateException("This operation cannot be performed " + + "because the dream has ended."); + } + return mMcuHal.sendMessage(msg, arg); + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } } diff --git a/services/core/java/com/android/server/dreams/McuHal.java b/services/core/java/com/android/server/dreams/McuHal.java new file mode 100644 index 0000000..1dc79c7 --- /dev/null +++ b/services/core/java/com/android/server/dreams/McuHal.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014 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.dreams; + +import android.service.dreams.DozeHardware; + +/** + * Provides access to the low-level microcontroller hardware abstraction layer. + */ +final class McuHal { + private final long mPtr; + + private static native long nativeOpen(); + private static native byte[] nativeSendMessage(long ptr, String msg, byte[] arg); + + private McuHal(long ptr) { + mPtr = ptr; + } + + public static McuHal open() { + long ptr = nativeOpen(); + return ptr != 0 ? new McuHal(ptr) : null; + } + + public void reset() { + sendMessage(DozeHardware.MSG_ENABLE_MCU, DozeHardware.VALUE_OFF); + } + + public byte[] sendMessage(String msg, byte[] arg) { + return nativeSendMessage(mPtr, msg, arg); + } +} diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 3aa3851..e49382e 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -1366,8 +1366,9 @@ public class InputManagerService extends IInputManager.Stub } // Native callback. - private int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags) { - return mWindowManagerCallbacks.interceptMotionBeforeQueueingWhenScreenOff(policyFlags); + private int interceptMotionBeforeQueueingWhenScreenOff(long whenNanos, int policyFlags) { + return mWindowManagerCallbacks.interceptMotionBeforeQueueingWhenScreenOff( + whenNanos, policyFlags); } // Native callback. @@ -1527,7 +1528,7 @@ public class InputManagerService extends IInputManager.Stub public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn); - public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags); + public int interceptMotionBeforeQueueingWhenScreenOff(long whenNanos, int policyFlags); public long interceptKeyBeforeDispatching(InputWindowHandle focus, KeyEvent event, int policyFlags); diff --git a/services/core/java/com/android/server/power/AutomaticBrightnessController.java b/services/core/java/com/android/server/power/AutomaticBrightnessController.java index b24c572..3ca628a 100644 --- a/services/core/java/com/android/server/power/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/power/AutomaticBrightnessController.java @@ -201,7 +201,7 @@ class AutomaticBrightnessController { public void updatePowerState(DisplayPowerRequest request) { if (setScreenAutoBrightnessAdjustment(request.screenAutoBrightnessAdjustment) || setLightSensorEnabled(request.useAutoBrightness - && DisplayPowerRequest.wantScreenOn(request.screenState))) { + && request.wantScreenOnNormal())) { updateAutoBrightness(false /*sendUpdate*/); } } diff --git a/services/core/java/com/android/server/power/DisplayPowerController.java b/services/core/java/com/android/server/power/DisplayPowerController.java index 291bb64..94f26dd 100644 --- a/services/core/java/com/android/server/power/DisplayPowerController.java +++ b/services/core/java/com/android/server/power/DisplayPowerController.java @@ -147,6 +147,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // The proximity sensor, or null if not available or needed. private Sensor mProximitySensor; + // The doze screen brightness. + private final int mScreenBrightnessDozeConfig; + // The dim screen brightness. private final int mScreenBrightnessDimConfig; @@ -259,6 +262,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call final Resources resources = context.getResources(); + mScreenBrightnessDozeConfig = clampAbsoluteBrightness(resources.getInteger( + com.android.internal.R.integer.config_screenBrightnessDoze)); + mScreenBrightnessDimConfig = clampAbsoluteBrightness(resources.getInteger( com.android.internal.R.integer.config_screenBrightnessDim)); @@ -432,7 +438,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Update the power state request. final boolean mustNotify; boolean mustInitialize = false; - boolean wasDim = false; + boolean wasDimOrDoze = false; synchronized (mLock) { mPendingUpdatePowerStateLocked = false; @@ -447,7 +453,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mPendingRequestChangedLocked = false; mustInitialize = true; } else if (mPendingRequestChangedLocked) { - wasDim = (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DIM); + wasDimOrDoze = (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DIM + || mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DOZE); mPowerRequest.copyFrom(mPendingRequestLocked); mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked; mPendingWaitForNegativeProximityLocked = false; @@ -498,7 +505,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } // Set the screen brightness. - if (DisplayPowerRequest.wantScreenOn(mPowerRequest.screenState)) { + if (mPowerRequest.wantScreenOnAny()) { int target; boolean slow; int screenAutoBrightness = mAutomaticBrightnessController != null ? @@ -517,12 +524,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call slow = false; mUsingScreenAutoBrightness = false; } - if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DIM) { + if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DOZE) { + // Dim quickly to the doze state. + target = mScreenBrightnessDozeConfig; + slow = false; + } else if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DIM) { // Dim quickly by at least some minimum amount. target = Math.min(target - SCREEN_DIM_MINIMUM_REDUCTION, mScreenBrightnessDimConfig); slow = false; - } else if (wasDim) { + } else if (wasDimOrDoze) { // Brighten quickly. slow = false; } @@ -535,7 +546,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Animate the screen on or off. if (!mScreenOffBecauseOfProximity) { - if (DisplayPowerRequest.wantScreenOn(mPowerRequest.screenState)) { + if (mPowerRequest.wantScreenOnAny()) { // Want screen on. // Wait for previous off animation to complete beforehand. // It is relatively short but if we cancel it and switch to the @@ -804,6 +815,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pw.println(); pw.println("Display Controller Configuration:"); + pw.println(" mScreenBrightnessDozeConfig=" + mScreenBrightnessDozeConfig); pw.println(" mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig); pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum); pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum); diff --git a/services/core/java/com/android/server/power/DisplayPowerRequest.java b/services/core/java/com/android/server/power/DisplayPowerRequest.java index 7626f76..c5e46cb 100644 --- a/services/core/java/com/android/server/power/DisplayPowerRequest.java +++ b/services/core/java/com/android/server/power/DisplayPowerRequest.java @@ -30,10 +30,11 @@ import android.os.PowerManager; */ final class DisplayPowerRequest { public static final int SCREEN_STATE_OFF = 0; - public static final int SCREEN_STATE_DIM = 1; - public static final int SCREEN_STATE_BRIGHT = 2; + public static final int SCREEN_STATE_DOZE = 1; + public static final int SCREEN_STATE_DIM = 2; + public static final int SCREEN_STATE_BRIGHT = 3; - // The requested minimum screen power state: off, dim or bright. + // The requested minimum screen power state: off, doze, dim or bright. public int screenState; // If true, the proximity sensor overrides the screen state when an object is @@ -75,6 +76,23 @@ final class DisplayPowerRequest { copyFrom(other); } + // Returns true if we want the screen on in any mode, including doze. + public boolean wantScreenOnAny() { + return screenState != SCREEN_STATE_OFF; + } + + // Returns true if we want the screen on in a normal mode, excluding doze. + // This is usually what we want to tell the rest of the system. For compatibility + // reasons, we pretend the screen is off when dozing. + public boolean wantScreenOnNormal() { + return screenState == SCREEN_STATE_DIM || screenState == SCREEN_STATE_BRIGHT; + } + + public boolean wantLightSensorEnabled() { + // Specifically, we don't want the light sensor while dozing. + return useAutoBrightness && wantScreenOnNormal(); + } + public void copyFrom(DisplayPowerRequest other) { screenState = other.screenState; useProximitySensor = other.useProximitySensor; diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index ecde184..0ba55b6 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -83,7 +83,7 @@ public final class PowerManagerService extends com.android.server.SystemService // Message: Sent when a user activity timeout occurs to update the power state. private static final int MSG_USER_ACTIVITY_TIMEOUT = 1; - // Message: Sent when the device enters or exits a napping or dreaming state. + // Message: Sent when the device enters or exits a dreaming or dozing state. private static final int MSG_SANDMAN = 2; // Message: Sent when the screen on blocker is released. private static final int MSG_SCREEN_ON_BLOCKER_RELEASED = 3; @@ -117,19 +117,21 @@ public final class PowerManagerService extends com.android.server.SystemService // Wakefulness: The device is asleep and can only be awoken by a call to wakeUp(). // The screen should be off or in the process of being turned off by the display controller. + // The device typically passes through the dozing state first. private static final int WAKEFULNESS_ASLEEP = 0; // Wakefulness: The device is fully awake. It can be put to sleep by a call to goToSleep(). - // When the user activity timeout expires, the device may start napping or go to sleep. + // When the user activity timeout expires, the device may start dreaming or go to sleep. private static final int WAKEFULNESS_AWAKE = 1; - // Wakefulness: The device is napping. It is deciding whether to dream or go to sleep - // but hasn't gotten around to it yet. It can be awoken by a call to wakeUp(), which - // ends the nap. User activity may brighten the screen but does not end the nap. - private static final int WAKEFULNESS_NAPPING = 2; // Wakefulness: The device is dreaming. It can be awoken by a call to wakeUp(), // which ends the dream. The device goes to sleep when goToSleep() is called, when // the dream ends or when unplugged. // User activity may brighten the screen but does not end the dream. - private static final int WAKEFULNESS_DREAMING = 3; + private static final int WAKEFULNESS_DREAMING = 2; + // Wakefulness: The device is dozing. It is almost asleep but is allowing a special + // low-power "doze" dream to run which keeps the display on but lets the application + // processor be suspended. It can be awoken by a call to wakeUp() which ends the dream. + // The device fully goes to sleep if the dream cannot be started or ends on its own. + private static final int WAKEFULNESS_DOZING = 3; // Summarizes the state of all active wakelocks. private static final int WAKE_LOCK_CPU = 1 << 0; @@ -138,6 +140,7 @@ public final class PowerManagerService extends com.android.server.SystemService private static final int WAKE_LOCK_BUTTON_BRIGHT = 1 << 3; private static final int WAKE_LOCK_PROXIMITY_SCREEN_OFF = 1 << 4; private static final int WAKE_LOCK_STAY_AWAKE = 1 << 5; // only set if already awake + private static final int WAKE_LOCK_DOZE = 1 << 6; // Summarizes the user activity state. private static final int USER_ACTIVITY_SCREEN_BRIGHT = 1 << 0; @@ -164,11 +167,6 @@ public final class PowerManagerService extends com.android.server.SystemService // Poll interval in milliseconds for watching boot animation finished. private static final int BOOT_ANIMATION_POLL_INTERVAL = 200; - // If the battery level drops by this percentage and the user activity timeout - // has expired, then assume the device is receiving insufficient current to charge - // effectively and terminate the dream. - private static final int DREAM_BATTERY_LEVEL_DRAIN_CUTOFF = 5; - private final Context mContext; private LightsManager mLightsManager; private BatteryService mBatteryService; @@ -195,6 +193,10 @@ public final class PowerManagerService extends com.android.server.SystemService // This is distinct from the screen power state, which is managed separately. private int mWakefulness; + // True if the sandman has just been summoned for the first time since entering the + // dreaming or dozing state. Indicates whether a new dream should begin. + private boolean mSandmanSummoned; + // True if MSG_SANDMAN has been scheduled. private boolean mSandmanScheduled; @@ -265,6 +267,14 @@ public final class PowerManagerService extends com.android.server.SystemService // True if boot completed occurred. We keep the screen on until this happens. private boolean mBootCompleted; + // True if auto-suspend mode is enabled. + // Refer to autosuspend.h. + private boolean mAutoSuspendModeEnabled; + + // True if interactive mode is enabled. + // Refer to power.h. + private boolean mInteractiveModeEnabled; + // True if the device is plugged into a power source. private boolean mIsPowered; @@ -282,6 +292,12 @@ public final class PowerManagerService extends com.android.server.SystemService // The current dock state. private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; + // True to decouple auto-suspend mode from the display state. + private boolean mDecoupleAutoSuspendModeFromDisplayConfig; + + // True to decouple interactive mode from the display state. + private boolean mDecoupleInteractiveModeFromDisplayConfig; + // True if the device should wake up when plugged or unplugged. private boolean mWakeUpWhenPluggedOrUnpluggedConfig; @@ -300,6 +316,22 @@ public final class PowerManagerService extends com.android.server.SystemService // Default value for dreams activate-on-dock private boolean mDreamsActivatedOnDockByDefaultConfig; + // True if dreams can run while not plugged in. + private boolean mDreamsEnabledOnBatteryConfig; + + // Minimum battery level to allow dreaming when powered. + // Use -1 to disable this safety feature. + private int mDreamsBatteryLevelMinimumWhenPoweredConfig; + + // Minimum battery level to allow dreaming when not powered. + // Use -1 to disable this safety feature. + private int mDreamsBatteryLevelMinimumWhenNotPoweredConfig; + + // If the battery level drops by this percentage and the user activity timeout + // has expired, then assume the device is receiving insufficient current to charge + // effectively and terminate the dream. Use -1 to disable this safety feature. + private int mDreamsBatteryLevelDrainCutoffConfig; + // True if dreams are enabled by the user. private boolean mDreamsEnabledSetting; @@ -523,6 +555,10 @@ public final class PowerManagerService extends com.android.server.SystemService private void readConfigurationLocked() { final Resources resources = mContext.getResources(); + mDecoupleAutoSuspendModeFromDisplayConfig = resources.getBoolean( + com.android.internal.R.bool.config_powerDecoupleAutoSuspendModeFromDisplay); + mDecoupleInteractiveModeFromDisplayConfig = resources.getBoolean( + com.android.internal.R.bool.config_powerDecoupleInteractiveModeFromDisplay); mWakeUpWhenPluggedOrUnpluggedConfig = resources.getBoolean( com.android.internal.R.bool.config_unplugTurnsOnScreen); mSuspendWhenScreenOffDueToProximityConfig = resources.getBoolean( @@ -535,6 +571,14 @@ public final class PowerManagerService extends com.android.server.SystemService com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault); mDreamsActivatedOnDockByDefaultConfig = resources.getBoolean( com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault); + mDreamsEnabledOnBatteryConfig = resources.getBoolean( + com.android.internal.R.bool.config_dreamsEnabledOnBattery); + mDreamsBatteryLevelMinimumWhenPoweredConfig = resources.getInteger( + com.android.internal.R.integer.config_dreamsBatteryLevelMinimumWhenPowered); + mDreamsBatteryLevelMinimumWhenNotPoweredConfig = resources.getInteger( + com.android.internal.R.integer.config_dreamsBatteryLevelMinimumWhenNotPowered); + mDreamsBatteryLevelDrainCutoffConfig = resources.getInteger( + com.android.internal.R.integer.config_dreamsBatteryLevelDrainCutoff); } private void updateSettingsLocked() { @@ -762,6 +806,7 @@ public final class PowerManagerService extends com.android.server.SystemService case PowerManager.SCREEN_DIM_WAKE_LOCK: case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: case PowerManager.FULL_WAKE_LOCK: + case PowerManager.DOZE_WAKE_LOCK: return true; case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK: @@ -794,7 +839,8 @@ public final class PowerManagerService extends com.android.server.SystemService } if (eventTime < mLastSleepTime || eventTime < mLastWakeTime - || mWakefulness == WAKEFULNESS_ASLEEP || !mBootCompleted || !mSystemReady) { + || mWakefulness == WAKEFULNESS_ASLEEP || mWakefulness == WAKEFULNESS_DOZING + || !mBootCompleted || !mSystemReady) { return false; } @@ -843,18 +889,21 @@ public final class PowerManagerService extends com.android.server.SystemService switch (mWakefulness) { case WAKEFULNESS_ASLEEP: Slog.i(TAG, "Waking up from sleep..."); - sendPendingNotificationsLocked(); - mNotifier.onWakeUpStarted(); - mSendWakeUpFinishedNotificationWhenReady = true; break; case WAKEFULNESS_DREAMING: Slog.i(TAG, "Waking up from dream..."); break; - case WAKEFULNESS_NAPPING: - Slog.i(TAG, "Waking up from nap..."); + case WAKEFULNESS_DOZING: + Slog.i(TAG, "Waking up from dozing..."); break; } + if (mWakefulness != WAKEFULNESS_DREAMING) { + sendPendingNotificationsLocked(); + mNotifier.onWakeUpStarted(); + mSendWakeUpFinishedNotificationWhenReady = true; + } + mLastWakeTime = eventTime; mWakefulness = WAKEFULNESS_AWAKE; mDirty |= DIRTY_WAKEFULNESS; @@ -877,13 +926,17 @@ public final class PowerManagerService extends com.android.server.SystemService } } + // This method is called goToSleep for historical reasons but we actually start + // dozing before really going to sleep. @SuppressWarnings("deprecation") private boolean goToSleepNoUpdateLocked(long eventTime, int reason) { if (DEBUG_SPEW) { Slog.d(TAG, "goToSleepNoUpdateLocked: eventTime=" + eventTime + ", reason=" + reason); } - if (eventTime < mLastWakeTime || mWakefulness == WAKEFULNESS_ASLEEP + if (eventTime < mLastWakeTime + || mWakefulness == WAKEFULNESS_ASLEEP + || mWakefulness == WAKEFULNESS_DOZING || !mBootCompleted || !mSystemReady) { return false; } @@ -907,7 +960,8 @@ public final class PowerManagerService extends com.android.server.SystemService mLastSleepTime = eventTime; mDirty |= DIRTY_WAKEFULNESS; - mWakefulness = WAKEFULNESS_ASLEEP; + mWakefulness = WAKEFULNESS_DOZING; + mSandmanSummoned = true; // Report the number of wake locks that will be cleared by going to sleep. int numWakeLocksCleared = 0; @@ -947,7 +1001,26 @@ public final class PowerManagerService extends com.android.server.SystemService Slog.i(TAG, "Nap time..."); mDirty |= DIRTY_WAKEFULNESS; - mWakefulness = WAKEFULNESS_NAPPING; + mWakefulness = WAKEFULNESS_DREAMING; + mSandmanSummoned = true; + return true; + } + + // Done dozing, drop everything and go to sleep. + private boolean reallyGoToSleepNoUpdateLocked(long eventTime) { + if (DEBUG_SPEW) { + Slog.d(TAG, "reallyGoToSleepNoUpdateLocked: eventTime=" + eventTime); + } + + if (eventTime < mLastWakeTime || mWakefulness == WAKEFULNESS_ASLEEP + || !mBootCompleted || !mSystemReady) { + return false; + } + + Slog.i(TAG, "Sleeping..."); + + mDirty |= DIRTY_WAKEFULNESS; + mWakefulness = WAKEFULNESS_ASLEEP; return true; } @@ -1023,7 +1096,7 @@ public final class PowerManagerService extends com.android.server.SystemService mPlugType = mBatteryService.getPlugType(); mBatteryLevel = mBatteryService.getBatteryLevel(); - if (DEBUG) { + if (DEBUG_SPEW) { Slog.d(TAG, "updateIsPoweredLocked: wasPowered=" + wasPowered + ", mIsPowered=" + mIsPowered + ", oldPlugType=" + oldPlugType @@ -1083,8 +1156,7 @@ public final class PowerManagerService extends com.android.server.SystemService } // If already dreaming and becoming powered, then don't wake. - if (mIsPowered && (mWakefulness == WAKEFULNESS_NAPPING - || mWakefulness == WAKEFULNESS_DREAMING)) { + if (mIsPowered && mWakefulness == WAKEFULNESS_DREAMING) { return false; } @@ -1131,35 +1203,45 @@ public final class PowerManagerService extends com.android.server.SystemService mWakeLockSummary |= WAKE_LOCK_CPU; break; case PowerManager.FULL_WAKE_LOCK: - if (mWakefulness != WAKEFULNESS_ASLEEP) { + if (mWakefulness == WAKEFULNESS_AWAKE + || mWakefulness == WAKEFULNESS_DREAMING) { mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_BUTTON_BRIGHT; - if (mWakefulness == WAKEFULNESS_AWAKE) { - mWakeLockSummary |= WAKE_LOCK_STAY_AWAKE; - } + } + if (mWakefulness == WAKEFULNESS_AWAKE) { + mWakeLockSummary |= WAKE_LOCK_STAY_AWAKE; } break; case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: - if (mWakefulness != WAKEFULNESS_ASLEEP) { + if (mWakefulness == WAKEFULNESS_AWAKE + || mWakefulness == WAKEFULNESS_DREAMING) { mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_SCREEN_BRIGHT; - if (mWakefulness == WAKEFULNESS_AWAKE) { - mWakeLockSummary |= WAKE_LOCK_STAY_AWAKE; - } + } + if (mWakefulness == WAKEFULNESS_AWAKE) { + mWakeLockSummary |= WAKE_LOCK_STAY_AWAKE; } break; case PowerManager.SCREEN_DIM_WAKE_LOCK: - if (mWakefulness != WAKEFULNESS_ASLEEP) { + if (mWakefulness == WAKEFULNESS_AWAKE + || mWakefulness == WAKEFULNESS_DREAMING) { mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_SCREEN_DIM; - if (mWakefulness == WAKEFULNESS_AWAKE) { - mWakeLockSummary |= WAKE_LOCK_STAY_AWAKE; - } + } + if (mWakefulness == WAKEFULNESS_AWAKE) { + mWakeLockSummary |= WAKE_LOCK_STAY_AWAKE; } break; case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK: - if (mWakefulness != WAKEFULNESS_ASLEEP) { + if (mWakefulness == WAKEFULNESS_AWAKE + || mWakefulness == WAKEFULNESS_DREAMING + || mWakefulness == WAKEFULNESS_DOZING) { mWakeLockSummary |= WAKE_LOCK_PROXIMITY_SCREEN_OFF; } break; + case PowerManager.DOZE_WAKE_LOCK: + if (mWakefulness == WAKEFULNESS_DOZING) { + mWakeLockSummary |= WAKE_LOCK_DOZE; + } + break; } } @@ -1184,7 +1266,8 @@ public final class PowerManagerService extends com.android.server.SystemService mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT); long nextTimeout = 0; - if (mWakefulness != WAKEFULNESS_ASLEEP) { + if (mWakefulness == WAKEFULNESS_AWAKE + || mWakefulness == WAKEFULNESS_DREAMING) { final int screenOffTimeout = getScreenOffTimeoutLocked(); final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout); @@ -1205,8 +1288,7 @@ public final class PowerManagerService extends com.android.server.SystemService && mLastUserActivityTimeNoChangeLights >= mLastWakeTime) { nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout; if (now < nextTimeout - && mDisplayPowerRequest.screenState - != DisplayPowerRequest.SCREEN_STATE_OFF) { + && mDisplayPowerRequest.wantScreenOnNormal()) { mUserActivitySummary = mDisplayPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_BRIGHT ? USER_ACTIVITY_SCREEN_BRIGHT : USER_ACTIVITY_SCREEN_DIM; @@ -1268,7 +1350,7 @@ public final class PowerManagerService extends com.android.server.SystemService /** * Updates the wakefulness of the device. * - * This is the function that decides whether the device should start napping + * This is the function that decides whether the device should start dreaming * based on the current wake locks and user activity state. It may modify mDirty * if the wakefulness changes. * @@ -1357,7 +1439,7 @@ public final class PowerManagerService extends com.android.server.SystemService } /** - * Called when the device enters or exits a napping or dreaming state. + * Called when the device enters or exits a dreaming or dozing state. * * We do this asynchronously because we must call out of the power manager to start * the dream and we don't want to hold our lock while doing so. There is a risk that @@ -1365,46 +1447,60 @@ public final class PowerManagerService extends com.android.server.SystemService */ private void handleSandman() { // runs on handler thread // Handle preconditions. - boolean startDreaming = false; + final boolean startDreaming; + final int wakefulness; synchronized (mLock) { mSandmanScheduled = false; - boolean canDream = canDreamLocked(); - if (DEBUG_SPEW) { - Slog.d(TAG, "handleSandman: canDream=" + canDream - + ", mWakefulness=" + wakefulnessToString(mWakefulness)); - } - - if (canDream && mWakefulness == WAKEFULNESS_NAPPING) { - startDreaming = true; + wakefulness = mWakefulness; + if (mSandmanSummoned) { + startDreaming = ((wakefulness == WAKEFULNESS_DREAMING && canDreamLocked()) + || wakefulness == WAKEFULNESS_DOZING); + mSandmanSummoned = false; + } else { + startDreaming = false; } } // Start dreaming if needed. // We only control the dream on the handler thread, so we don't need to worry about // concurrent attempts to start or stop the dream. - boolean isDreaming = false; + final boolean isDreaming; if (mDreamManager != null) { + // Restart the dream whenever the sandman is summoned. if (startDreaming) { - mDreamManager.startDream(); + mDreamManager.stopDream(); + mDreamManager.startDream(wakefulness == WAKEFULNESS_DOZING); } isDreaming = mDreamManager.isDreaming(); + } else { + isDreaming = false; } // Update dream state. - // We might need to stop the dream again if the preconditions changed. - boolean continueDreaming = false; synchronized (mLock) { - if (isDreaming && canDreamLocked()) { - if (mWakefulness == WAKEFULNESS_NAPPING) { - mWakefulness = WAKEFULNESS_DREAMING; - mDirty |= DIRTY_WAKEFULNESS; - mBatteryLevelWhenDreamStarted = mBatteryLevel; - updatePowerStateLocked(); - continueDreaming = true; - } else if (mWakefulness == WAKEFULNESS_DREAMING) { - if (!isBeingKeptAwakeLocked() + // Remember the initial battery level when the dream started. + if (startDreaming && isDreaming) { + mBatteryLevelWhenDreamStarted = mBatteryLevel; + if (wakefulness == WAKEFULNESS_DOZING) { + Slog.i(TAG, "Dozing..."); + } else { + Slog.i(TAG, "Dreaming..."); + } + } + + // If preconditions changed, wait for the next iteration to determine + // whether the dream should continue (or be restarted). + if (mSandmanSummoned || mWakefulness != wakefulness) { + return; // wait for next cycle + } + + // Determine whether the dream should continue. + if (wakefulness == WAKEFULNESS_DREAMING) { + if (isDreaming && canDreamLocked()) { + if (mDreamsBatteryLevelDrainCutoffConfig >= 0 && mBatteryLevel < mBatteryLevelWhenDreamStarted - - DREAM_BATTERY_LEVEL_DRAIN_CUTOFF) { + - mDreamsBatteryLevelDrainCutoffConfig + && !isBeingKeptAwakeLocked()) { // If the user activity timeout expired and the battery appears // to be draining faster than it is charging then stop dreaming // and go to sleep. @@ -1414,53 +1510,64 @@ public final class PowerManagerService extends com.android.server.SystemService + mBatteryLevelWhenDreamStarted + "%. " + "Battery level now: " + mBatteryLevel + "%."); } else { - continueDreaming = true; + return; // continue dreaming } } - } - if (!continueDreaming) { - handleDreamFinishedLocked(); + + // Dream has ended or will be stopped. Update the power state. + if (isItBedTimeYetLocked()) { + goToSleepNoUpdateLocked(SystemClock.uptimeMillis(), + PowerManager.GO_TO_SLEEP_REASON_TIMEOUT); + updatePowerStateLocked(); + } else { + wakeUpNoUpdateLocked(SystemClock.uptimeMillis()); + updatePowerStateLocked(); + } + } else if (wakefulness == WAKEFULNESS_DOZING) { + if (isDreaming) { + return; // continue dozing + } + + // Doze has ended or will be stopped. Update the power state. + reallyGoToSleepNoUpdateLocked(SystemClock.uptimeMillis()); + updatePowerStateLocked(); } } - // Stop dreaming if needed. - // It's possible that something else changed to make us need to start the dream again. - // If so, then the power manager will have posted another message to the handler - // to take care of it later. - if (mDreamManager != null) { - if (!continueDreaming) { - mDreamManager.stopDream(); - } + // Stop dream. + if (isDreaming) { + mDreamManager.stopDream(); } } /** - * Returns true if the device is allowed to dream in its current state - * assuming that it is currently napping or dreaming. + * Returns true if the device is allowed to dream in its current state. + * This function is not called when dozing. */ private boolean canDreamLocked() { - return mDreamsSupportedConfig - && mDreamsEnabledSetting - && mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF - && mBootCompleted - && (mIsPowered || isBeingKeptAwakeLocked()); - } - - /** - * Called when a dream is ending to figure out what to do next. - */ - private void handleDreamFinishedLocked() { - if (mWakefulness == WAKEFULNESS_NAPPING - || mWakefulness == WAKEFULNESS_DREAMING) { - if (isItBedTimeYetLocked()) { - goToSleepNoUpdateLocked(SystemClock.uptimeMillis(), - PowerManager.GO_TO_SLEEP_REASON_TIMEOUT); - updatePowerStateLocked(); - } else { - wakeUpNoUpdateLocked(SystemClock.uptimeMillis()); - updatePowerStateLocked(); + if (mWakefulness != WAKEFULNESS_DREAMING + || !mDreamsSupportedConfig + || !mDreamsEnabledSetting + || !mDisplayPowerRequest.wantScreenOnNormal() + || !mBootCompleted) { + return false; + } + if (!isBeingKeptAwakeLocked()) { + if (!mIsPowered && !mDreamsEnabledByDefaultConfig) { + return false; + } + if (!mIsPowered + && mDreamsBatteryLevelMinimumWhenNotPoweredConfig >= 0 + && mBatteryLevel < mDreamsBatteryLevelMinimumWhenNotPoweredConfig) { + return false; + } + if (mIsPowered + && mDreamsBatteryLevelMinimumWhenPoweredConfig >= 0 + && mBatteryLevel < mDreamsBatteryLevelMinimumWhenPoweredConfig) { + return false; } } + return true; } private void handleScreenOnBlockerReleased() { @@ -1482,11 +1589,11 @@ public final class PowerManagerService extends com.android.server.SystemService if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED | DIRTY_SETTINGS | DIRTY_SCREEN_ON_BLOCKER_RELEASED)) != 0) { - int newScreenState = getDesiredScreenPowerStateLocked(); + final int newScreenState = getDesiredScreenPowerStateLocked(); if (newScreenState != mDisplayPowerRequest.screenState) { mDisplayPowerRequest.screenState = newScreenState; nativeSetPowerState( - newScreenState != DisplayPowerRequest.SCREEN_STATE_OFF, + mDisplayPowerRequest.wantScreenOnNormal(), newScreenState == DisplayPowerRequest.SCREEN_STATE_BRIGHT); } @@ -1555,6 +1662,10 @@ public final class PowerManagerService extends com.android.server.SystemService return DisplayPowerRequest.SCREEN_STATE_OFF; } + if ((mWakeLockSummary & WAKE_LOCK_DOZE) != 0) { + return DisplayPowerRequest.SCREEN_STATE_DOZE; + } + if ((mWakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0 || (mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0 || !mBootCompleted) { @@ -1606,7 +1717,18 @@ public final class PowerManagerService extends com.android.server.SystemService */ private void updateSuspendBlockerLocked() { final boolean needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0); - final boolean needDisplaySuspendBlocker = needDisplaySuspendBlocker(); + final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked(); + final boolean autoSuspend = !needDisplaySuspendBlocker; + + // Disable auto-suspend if needed. + if (!autoSuspend) { + if (mDecoupleAutoSuspendModeFromDisplayConfig) { + setAutoSuspendModeLocked(false); + } + if (mDecoupleInteractiveModeFromDisplayConfig) { + setInteractiveModeLocked(true); + } + } // First acquire suspend blockers if needed. if (needWakeLockSuspendBlocker && !mHoldingWakeLockSuspendBlocker) { @@ -1627,17 +1749,27 @@ public final class PowerManagerService extends com.android.server.SystemService mDisplaySuspendBlocker.release(); mHoldingDisplaySuspendBlocker = false; } + + // Enable auto-suspend if needed. + if (autoSuspend) { + if (mDecoupleInteractiveModeFromDisplayConfig) { + setInteractiveModeLocked(false); + } + if (mDecoupleAutoSuspendModeFromDisplayConfig) { + setAutoSuspendModeLocked(true); + } + } } /** * Return true if we must keep a suspend blocker active on behalf of the display. * We do so if the screen is on or is in transition between states. */ - private boolean needDisplaySuspendBlocker() { + private boolean needDisplaySuspendBlockerLocked() { if (!mDisplayReady) { return true; } - if (mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF) { + if (mDisplayPowerRequest.wantScreenOnNormal()) { // If we asked for the screen to be on but it is off due to the proximity // sensor then we may suspend but only if the configuration allows it. // On some hardware it may not be safe to suspend because the proximity @@ -1647,13 +1779,34 @@ public final class PowerManagerService extends com.android.server.SystemService return true; } } + // Let the system suspend if the screen is off or dozing. return false; } + private void setAutoSuspendModeLocked(boolean enable) { + if (enable != mAutoSuspendModeEnabled) { + if (DEBUG) { + Slog.d(TAG, "Setting auto-suspend mode to " + enable); + } + mAutoSuspendModeEnabled = enable; + nativeSetAutoSuspend(enable); + } + } + + private void setInteractiveModeLocked(boolean enable) { + if (enable != mInteractiveModeEnabled) { + if (DEBUG) { + Slog.d(TAG, "Setting interactive mode to " + enable); + } + mInteractiveModeEnabled = enable; + nativeSetInteractive(enable); + } + } + private boolean isScreenOnInternal() { synchronized (mLock) { return !mSystemReady - || mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF; + || mDisplayPowerRequest.wantScreenOnNormal(); } } @@ -1887,10 +2040,13 @@ public final class PowerManagerService extends com.android.server.SystemService pw.println(" mProximityPositive=" + mProximityPositive); pw.println(" mBootCompleted=" + mBootCompleted); pw.println(" mSystemReady=" + mSystemReady); + pw.println(" mAutoSuspendModeEnabled=" + mAutoSuspendModeEnabled); + pw.println(" mInteactiveModeEnabled=" + mInteractiveModeEnabled); pw.println(" mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary)); pw.println(" mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)); pw.println(" mRequestWaitForNegativeProximity=" + mRequestWaitForNegativeProximity); pw.println(" mSandmanScheduled=" + mSandmanScheduled); + pw.println(" mSandmanSummoned=" + mSandmanSummoned); pw.println(" mLastWakeTime=" + TimeUtils.formatUptime(mLastWakeTime)); pw.println(" mLastSleepTime=" + TimeUtils.formatUptime(mLastSleepTime)); pw.println(" mSendWakeUpFinishedNotificationWhenReady=" @@ -1906,6 +2062,10 @@ public final class PowerManagerService extends com.android.server.SystemService pw.println(); pw.println("Settings and Configuration:"); + pw.println(" mDecoupleAutoSuspendModeFromDisplayConfig=" + + mDecoupleAutoSuspendModeFromDisplayConfig); + pw.println(" mDecoupleInteractiveModeFromDisplayConfig=" + + mDecoupleInteractiveModeFromDisplayConfig); pw.println(" mWakeUpWhenPluggedOrUnpluggedConfig=" + mWakeUpWhenPluggedOrUnpluggedConfig); pw.println(" mSuspendWhenScreenOffDueToProximityConfig=" @@ -1916,6 +2076,14 @@ public final class PowerManagerService extends com.android.server.SystemService + mDreamsActivatedOnSleepByDefaultConfig); pw.println(" mDreamsActivatedOnDockByDefaultConfig=" + mDreamsActivatedOnDockByDefaultConfig); + pw.println(" mDreamsEnabledOnBatteryConfig=" + + mDreamsEnabledOnBatteryConfig); + pw.println(" mDreamsBatteryLevelMinimumWhenPoweredConfig=" + + mDreamsBatteryLevelMinimumWhenPoweredConfig); + pw.println(" mDreamsBatteryLevelMinimumWhenNotPoweredConfig=" + + mDreamsBatteryLevelMinimumWhenNotPoweredConfig); + pw.println(" mDreamsBatteryLevelDrainCutoffConfig=" + + mDreamsBatteryLevelDrainCutoffConfig); pw.println(" mDreamsEnabledSetting=" + mDreamsEnabledSetting); pw.println(" mDreamsActivateOnSleepSetting=" + mDreamsActivateOnSleepSetting); pw.println(" mDreamsActivateOnDockSetting=" + mDreamsActivateOnDockSetting); @@ -1991,8 +2159,8 @@ public final class PowerManagerService extends com.android.server.SystemService return "Awake"; case WAKEFULNESS_DREAMING: return "Dreaming"; - case WAKEFULNESS_NAPPING: - return "Napping"; + case WAKEFULNESS_DOZING: + return "Dozing"; default: return Integer.toString(wakefulness); } @@ -2169,6 +2337,7 @@ public final class PowerManagerService extends com.android.server.SystemService + " (uid=" + mOwnerUid + ", pid=" + mOwnerPid + ", ws=" + mWorkSource + ")"; } + @SuppressWarnings("deprecation") private String getLockLevelString() { switch (mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) { case PowerManager.FULL_WAKE_LOCK: @@ -2181,6 +2350,8 @@ public final class PowerManagerService extends com.android.server.SystemService return "PARTIAL_WAKE_LOCK "; case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK: return "PROXIMITY_SCREEN_OFF_WAKE_LOCK"; + case PowerManager.DOZE_WAKE_LOCK: + return "DOZE_WAKE_LOCK "; default: return "??? "; } @@ -2311,16 +2482,24 @@ public final class PowerManagerService extends com.android.server.SystemService synchronized (this) { mBlanked = true; mDisplayManagerInternal.blankAllDisplaysFromPowerManager(); - nativeSetInteractive(false); - nativeSetAutoSuspend(true); + if (!mDecoupleInteractiveModeFromDisplayConfig) { + setInteractiveModeLocked(false); + } + if (!mDecoupleAutoSuspendModeFromDisplayConfig) { + setAutoSuspendModeLocked(true); + } } } @Override public void unblankAllDisplays() { synchronized (this) { - nativeSetAutoSuspend(false); - nativeSetInteractive(true); + if (!mDecoupleAutoSuspendModeFromDisplayConfig) { + setAutoSuspendModeLocked(false); + } + if (!mDecoupleInteractiveModeFromDisplayConfig) { + setInteractiveModeLocked(true); + } mDisplayManagerInternal.unblankAllDisplaysFromPowerManager(); mBlanked = false; } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 803b9ac..4aae5c1 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -364,8 +364,8 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks { * motion event processing when the screen is off since these events are normally * dropped. */ @Override - public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags) { - return mService.mPolicy.interceptMotionBeforeQueueingWhenScreenOff(policyFlags); + public int interceptMotionBeforeQueueingWhenScreenOff(long whenNanos, int policyFlags) { + return mService.mPolicy.interceptMotionBeforeQueueingWhenScreenOff(whenNanos, policyFlags); } /* Provides an opportunity for the window manager policy to process a key before diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk index 85f69a4..7675ba6 100644 --- a/services/core/jni/Android.mk +++ b/services/core/jni/Android.mk @@ -8,6 +8,7 @@ LOCAL_SRC_FILES += \ $(LOCAL_REL_DIR)/com_android_server_AlarmManagerService.cpp \ $(LOCAL_REL_DIR)/com_android_server_AssetAtlasService.cpp \ $(LOCAL_REL_DIR)/com_android_server_ConsumerIrService.cpp \ + $(LOCAL_REL_DIR)/com_android_server_dreams_McuHal.cpp \ $(LOCAL_REL_DIR)/com_android_server_input_InputApplicationHandle.cpp \ $(LOCAL_REL_DIR)/com_android_server_input_InputManagerService.cpp \ $(LOCAL_REL_DIR)/com_android_server_input_InputWindowHandle.cpp \ diff --git a/services/core/jni/com_android_server_dreams_McuHal.cpp b/services/core/jni/com_android_server_dreams_McuHal.cpp new file mode 100644 index 0000000..a6d9297 --- /dev/null +++ b/services/core/jni/com_android_server_dreams_McuHal.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2014 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. + */ + +#define LOG_TAG "McuHal" + +//#define LOG_NDEBUG 0 + +#include "JNIHelp.h" +#include "jni.h" + +#include <ScopedUtfChars.h> +#include <ScopedPrimitiveArray.h> + +#include <utils/Errors.h> +#include <utils/Log.h> +#include <hardware/mcu.h> + +namespace android { + +static jlong nativeOpen(JNIEnv* env, jclass clazz) { + mcu_module_t* module = NULL; + status_t err = hw_get_module(MCU_HARDWARE_MODULE_ID, + (hw_module_t const**)&module); + if (err) { + ALOGE("Couldn't load %s module (%s)", MCU_HARDWARE_MODULE_ID, strerror(-err)); + return 0; + } + + err = module->init(module); + if (err) { + ALOGE("Couldn't initialize %s module (%s)", MCU_HARDWARE_MODULE_ID, strerror(-err)); + return 0; + } + + return reinterpret_cast<jlong>(module); +} + +static jbyteArray nativeSendMessage(JNIEnv* env, jclass clazz, + jlong ptr, jstring msgStr, jbyteArray argArray) { + mcu_module_t* module = reinterpret_cast<mcu_module_t*>(ptr); + + ScopedUtfChars msg(env, msgStr); + ALOGV("Sending message %s to MCU", msg.c_str()); + + void* result = NULL; + size_t resultSize = 0; + status_t err; + if (argArray) { + ScopedByteArrayRO arg(env, argArray); + err = module->sendMessage(module, msg.c_str(), arg.get(), arg.size(), + &result, &resultSize); + } else { + err = module->sendMessage(module, msg.c_str(), NULL, 0, &result, &resultSize); + } + if (err) { + ALOGE("Couldn't send message to MCU (%s)", strerror(-err)); + return NULL; + } + + if (!result) { + return NULL; + } + + jbyteArray resultArray = env->NewByteArray(resultSize); + if (resultArray) { + env->SetByteArrayRegion(resultArray, 0, resultSize, static_cast<jbyte*>(result)); + } + free(result); + return resultArray; +} + +static JNINativeMethod gMcuHalMethods[] = { + /* name, signature, funcPtr */ + { "nativeOpen", "()J", + (void*) nativeOpen }, + { "nativeSendMessage", "(JLjava/lang/String;[B)[B", + (void*) nativeSendMessage }, +}; + +int register_android_server_dreams_McuHal(JNIEnv* env) { + int res = jniRegisterNativeMethods(env, "com/android/server/dreams/McuHal", + gMcuHalMethods, NELEM(gMcuHalMethods)); + LOG_FATAL_IF(res < 0, "Unable to register native methods."); + return 0; +} + +} /* namespace android */ diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 9fe0082..0207c55 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -151,8 +151,6 @@ static void loadSystemIconAsSprite(JNIEnv* env, jobject contextObj, int32_t styl enum { WM_ACTION_PASS_TO_USER = 1, - WM_ACTION_WAKE_UP = 2, - WM_ACTION_GO_TO_SLEEP = 4, }; @@ -844,7 +842,7 @@ void NativeInputManager::interceptMotionBeforeQueueing(nsecs_t when, uint32_t& p JNIEnv* env = jniEnv(); jint wmActions = env->CallIntMethod(mServiceObj, gServiceClassInfo.interceptMotionBeforeQueueingWhenScreenOff, - policyFlags); + when, policyFlags); if (checkAndClearExceptionFromCallback(env, "interceptMotionBeforeQueueingWhenScreenOff")) { wmActions = 0; @@ -860,20 +858,6 @@ void NativeInputManager::interceptMotionBeforeQueueing(nsecs_t when, uint32_t& p void NativeInputManager::handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags) { - if (wmActions & WM_ACTION_GO_TO_SLEEP) { -#if DEBUG_INPUT_DISPATCHER_POLICY - ALOGD("handleInterceptActions: Going to sleep."); -#endif - android_server_PowerManagerService_goToSleep(when); - } - - if (wmActions & WM_ACTION_WAKE_UP) { -#if DEBUG_INPUT_DISPATCHER_POLICY - ALOGD("handleInterceptActions: Waking up."); -#endif - android_server_PowerManagerService_wakeUp(when); - } - if (wmActions & WM_ACTION_PASS_TO_USER) { policyFlags |= POLICY_FLAG_PASS_TO_USER; } else { @@ -1412,7 +1396,7 @@ int register_android_server_InputManager(JNIEnv* env) { GET_METHOD_ID(gServiceClassInfo.interceptMotionBeforeQueueingWhenScreenOff, clazz, - "interceptMotionBeforeQueueingWhenScreenOff", "(I)I"); + "interceptMotionBeforeQueueingWhenScreenOff", "(JI)I"); GET_METHOD_ID(gServiceClassInfo.interceptKeyBeforeDispatching, clazz, "interceptKeyBeforeDispatching", diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index efc34a2..00986d5 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -36,6 +36,7 @@ int register_android_server_location_GpsLocationProvider(JNIEnv* env); int register_android_server_location_FlpHardwareProvider(JNIEnv* env); int register_android_server_connectivity_Vpn(JNIEnv* env); int register_android_server_AssetAtlasService(JNIEnv* env); +int register_android_server_dreams_McuHal(JNIEnv* env); }; using namespace android; @@ -67,7 +68,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) register_android_server_connectivity_Vpn(env); register_android_server_AssetAtlasService(env); register_android_server_ConsumerIrService(env); - + register_android_server_dreams_McuHal(env); return JNI_VERSION_1_4; } diff --git a/tests/DozeTest/Android.mk b/tests/DozeTest/Android.mk new file mode 100644 index 0000000..01f10e5 --- /dev/null +++ b/tests/DozeTest/Android.mk @@ -0,0 +1,14 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := optional + +# Only compile source java files in this apk. +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_PACKAGE_NAME := DozeTest + +include $(BUILD_PACKAGE) + +# Use the following include to make our test apk. +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/tests/DozeTest/AndroidManifest.xml b/tests/DozeTest/AndroidManifest.xml new file mode 100644 index 0000000..c199f69 --- /dev/null +++ b/tests/DozeTest/AndroidManifest.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 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. +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.dreams.dozetest"> + <uses-permission android:name="android.permission.WAKE_LOCK" /> + + <application android:label="@string/app_name"> + <service + android:name="DozeTestDream" + android:exported="true" + android:icon="@drawable/ic_app" + android:label="@string/doze_dream_name"> + <!-- Commented out to prevent this dream from appearing in the list of + dreams that the user can select via the Settings application. + <intent-filter> + <action android:name="android.service.dreams.DreamService" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + --> + </service> + </application> +</manifest> diff --git a/tests/DozeTest/res/drawable-hdpi/ic_app.png b/tests/DozeTest/res/drawable-hdpi/ic_app.png Binary files differnew file mode 100755 index 0000000..66a1984 --- /dev/null +++ b/tests/DozeTest/res/drawable-hdpi/ic_app.png diff --git a/tests/DozeTest/res/drawable-mdpi/ic_app.png b/tests/DozeTest/res/drawable-mdpi/ic_app.png Binary files differnew file mode 100644 index 0000000..5ae7701 --- /dev/null +++ b/tests/DozeTest/res/drawable-mdpi/ic_app.png diff --git a/tests/DozeTest/res/layout/dream.xml b/tests/DozeTest/res/layout/dream.xml new file mode 100644 index 0000000..1c8fd3f --- /dev/null +++ b/tests/DozeTest/res/layout/dream.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center_vertical" + android:orientation="vertical"> + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/alarm_clock_label" /> + <TextView android:id="@+id/alarm_clock" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + + <Space + android:layout_width="match_parent" + android:layout_height="32dp" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/tick_clock_label" /> + <TextClock android:id="@+id/tick_clock" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> +</LinearLayout> diff --git a/tests/DozeTest/res/values/strings.xml b/tests/DozeTest/res/values/strings.xml new file mode 100644 index 0000000..f21911f --- /dev/null +++ b/tests/DozeTest/res/values/strings.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 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. +--> +<resources> + <!-- Name of the package of basic screensavers, shown in Settings > Apps. [CHAR LIMIT=40] --> + <string name="app_name">Doze Test</string> + + <!-- Name of the screensaver. [CHAR LIMIT=40] --> + <string name="doze_dream_name">Doze Test</string> + + <string name="alarm_clock_label">This clock is updated using the Alarm Manager</string> + <string name="tick_clock_label">This clock is updated using TIME_TICK Broadcasts</string> +</resources> diff --git a/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java b/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java new file mode 100644 index 0000000..bf35db4 --- /dev/null +++ b/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2014 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.dreams.dozetest; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.PowerManager; +import android.service.dreams.DozeHardware; +import android.service.dreams.DreamService; +import android.text.format.DateFormat; +import android.util.Log; +import android.widget.TextView; + +import java.util.Date; + +/** + * Simple test for doze mode. + * <p> + * adb shell setprop debug.doze.component com.android.dreams.dozetest/.DozeTestDream + * </p> + */ +public class DozeTestDream extends DreamService { + private static final String TAG = DozeTestDream.class.getSimpleName(); + private static final boolean DEBUG = false; + + // Amount of time to allow to update the time shown on the screen before releasing + // the wakelock. This timeout is design to compensate for the fact that we don't + // currently have a way to know when time display contents have actually been + // refreshed once the dream has finished rendering a new frame. + private static final int UPDATE_TIME_TIMEOUT = 100; + + // A doze hardware message string we use for end-to-end testing. + // Doesn't mean anything. Real hardware won't handle it. + private static final String TEST_PING_MESSAGE = "test.ping"; + + private PowerManager mPowerManager; + private PowerManager.WakeLock mWakeLock; + private AlarmManager mAlarmManager; + private PendingIntent mAlarmIntent; + + private TextView mAlarmClock; + + private final Date mTime = new Date(); + private java.text.DateFormat mTimeFormat; + + private boolean mDreaming; + private DozeHardware mDozeHardware; + + @Override + public void onCreate() { + super.onCreate(); + + mPowerManager = (PowerManager)getSystemService(Context.POWER_SERVICE); + mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); + + mAlarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE); + + Intent intent = new Intent("com.android.dreams.dozetest.ACTION_ALARM"); + intent.setPackage(getPackageName()); + IntentFilter filter = new IntentFilter(); + filter.addAction(intent.getAction()); + registerReceiver(mAlarmReceiver, filter); + mAlarmIntent = PendingIntent.getBroadcast(this, 0, intent, + PendingIntent.FLAG_CANCEL_CURRENT); + } + + @Override + public void onDestroy() { + super.onDestroy(); + + unregisterReceiver(mAlarmReceiver); + mAlarmIntent.cancel(); + } + + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + setInteractive(false); + setLowProfile(true); + setFullscreen(true); + setContentView(R.layout.dream); + + mAlarmClock = (TextView)findViewById(R.id.alarm_clock); + + mTimeFormat = DateFormat.getTimeFormat(this); + } + + @Override + public void onDreamingStarted() { + super.onDreamingStarted(); + + mDreaming = true; + mDozeHardware = getDozeHardware(); + + Log.d(TAG, "Dream started: canDoze=" + canDoze() + + ", dozeHardware=" + mDozeHardware); + + performTimeUpdate(); + + if (mDozeHardware != null) { + mDozeHardware.sendMessage(TEST_PING_MESSAGE, null); + mDozeHardware.setEnableMcu(true); + } + startDozing(); + } + + @Override + public void onDreamingStopped() { + super.onDreamingStopped(); + + mDreaming = false; + if (mDozeHardware != null) { + mDozeHardware.setEnableMcu(false); + mDozeHardware = null; + } + + Log.d(TAG, "Dream ended: isDozing=" + isDozing()); + + stopDozing(); + cancelTimeUpdate(); + } + + private void performTimeUpdate() { + if (mDreaming) { + long now = System.currentTimeMillis(); + now -= now % 60000; // back up to last minute boundary + + mTime.setTime(now); + mAlarmClock.setText(mTimeFormat.format(mTime)); + + mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, now + 60000, mAlarmIntent); + + mWakeLock.acquire(UPDATE_TIME_TIMEOUT); + } + } + + private void cancelTimeUpdate() { + mAlarmManager.cancel(mAlarmIntent); + } + + private final BroadcastReceiver mAlarmReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + performTimeUpdate(); + } + }; +} |