summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2014-01-30 21:47:47 -0800
committerJeff Brown <jeffbrown@google.com>2014-02-20 13:39:13 -0800
commit2687550272ba061448f5d5b914700dc335299ee7 (patch)
treefc68c5ecec24ec2aa120ccf490d7a56573704a73
parentd43ad2f35abcc647f7985abd09ed9de5899faf18 (diff)
downloadframeworks_base-2687550272ba061448f5d5b914700dc335299ee7.zip
frameworks_base-2687550272ba061448f5d5b914700dc335299ee7.tar.gz
frameworks_base-2687550272ba061448f5d5b914700dc335299ee7.tar.bz2
Add a new "doze mode" based on Dream components.
When a doze component has been specified in a config.xml resource overlay, the power manager will try to start a preconfigured dream whenever it would have otherwise gone to sleep and turned the screen off. The dream should render whatever it intends to show then call startDozing() to tell the power manager to put the display into a low power "doze" state and allow the application processor to be suspended. The dream may wake up periodically using the alarm manager or other features to update the contents of the display. Added several new config.xml resources related to dreams and dozing. In particular for dozing there are two new resources that pertain to decoupling auto-suspend mode and interactive mode from the display state. This is a requirement to enable the application processor and other components to be suspended while dozing. Most devices do not support these features today. Consolidated the power manager's NAPPING and DREAMING states into one to simplify the logic. The NAPPING state was mostly superfluous and simply indicated that the power manager should attempt to start a new dream. This state is now tracked in the mSandmanSummoned field. Added a new DOZING state which is analoguous to DREAMING. The normal state transition is now: AWAKE -> DREAMING -> DOZING -> ASLEEP. The PowerManager.goToSleep() method now enters the DOZING state instead of immediately going to sleep. While in the doze state, the screen remains on. However, we actually tell the rest of the system that the screen is off. This is somewhat unfortunate but much of the system makes inappropriate assumptions about what it means for the screen to be on or off. In particular, screen on is usually taken to indicate an interactive state where the user is present but that's not at all true for dozing (and is only sometimes true while dreaming). We will probably need to add some more precise externally visible states at some point. The DozeHardware interface encapsulates a generic microcontroller interface to allow a doze dream for off-loading rendering or other functions while dozing. If the device possesses an MCU HAL for dozing then it is exposed to the DreamService here. Removed a number of catch blocks in DreamService that caught Throwable and attempted to cause the dream to finish itself. We actually just want to let the process crash. Cleanup will happen automatically if needed. Catching these exceptions results in mysterious undefined behavior and broken dreams. Bug: 12494706 Change-Id: Ie78336b37dde7250d1ce65b3d367879e3bfb2b8b
-rw-r--r--Android.mk1
-rw-r--r--core/java/android/os/PowerManager.java13
-rw-r--r--core/java/android/service/dreams/DozeHardware.java77
-rw-r--r--core/java/android/service/dreams/DreamManagerInternal.java2
-rw-r--r--core/java/android/service/dreams/DreamService.java294
-rw-r--r--core/java/android/service/dreams/IDozeHardware.aidl24
-rw-r--r--core/java/android/service/dreams/IDreamManager.aidl6
-rw-r--r--core/java/android/service/dreams/IDreamService.aidl2
-rw-r--r--core/java/android/view/WindowManagerPolicy.java22
-rw-r--r--core/res/res/values/config.xml69
-rw-r--r--core/res/res/values/symbols.xml8
-rw-r--r--policy/src/com/android/internal/policy/impl/PhoneWindowManager.java25
-rw-r--r--services/core/java/com/android/server/dreams/DreamController.java19
-rw-r--r--services/core/java/com/android/server/dreams/DreamManagerService.java179
-rw-r--r--services/core/java/com/android/server/dreams/McuHal.java46
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java7
-rw-r--r--services/core/java/com/android/server/power/DisplayPowerController.java37
-rw-r--r--services/core/java/com/android/server/power/DisplayPowerRequest.java24
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java401
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java4
-rw-r--r--services/core/jni/Android.mk1
-rw-r--r--services/core/jni/com_android_server_dreams_McuHal.cpp100
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp20
-rw-r--r--services/core/jni/onload.cpp3
-rw-r--r--tests/DozeTest/Android.mk14
-rw-r--r--tests/DozeTest/AndroidManifest.xml35
-rwxr-xr-xtests/DozeTest/res/drawable-hdpi/ic_app.pngbin0 -> 3608 bytes
-rw-r--r--tests/DozeTest/res/drawable-mdpi/ic_app.pngbin0 -> 5198 bytes
-rw-r--r--tests/DozeTest/res/layout/dream.xml41
-rw-r--r--tests/DozeTest/res/values/strings.xml25
-rw-r--r--tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java165
31 files changed, 1377 insertions, 287 deletions
diff --git a/Android.mk b/Android.mk
index 687989c..7cbc339 100644
--- a/Android.mk
+++ b/Android.mk
@@ -176,6 +176,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 5e0d489..86ef479 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
@@ -418,6 +430,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 c5a1b86..74dda77 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -115,20 +115,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().
@@ -751,8 +737,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);
@@ -765,10 +750,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 b34c792..559b11b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -652,6 +652,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. -->
@@ -1083,6 +1088,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 6d90973..c38e753 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1504,6 +1504,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" />
@@ -1608,11 +1609,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 f33ab40..76f29cd 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -3808,14 +3808,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
@@ -3832,6 +3829,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;
@@ -3839,8 +3838,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;
}
}
@@ -3950,7 +3949,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;
}
}
}
@@ -3994,7 +3994,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;
}
@@ -4064,6 +4065,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
break;
}
}
+
+ if (isWakeKey) {
+ mPowerManager.wakeUp(event.getEventTime());
+ }
return result;
}
@@ -4104,13 +4109,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 69281bc..73033e0 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1329,8 +1329,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.
@@ -1491,7 +1492,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/DisplayPowerController.java b/services/core/java/com/android/server/power/DisplayPowerController.java
index f1be504..bd9acb9 100644
--- a/services/core/java/com/android/server/power/DisplayPowerController.java
+++ b/services/core/java/com/android/server/power/DisplayPowerController.java
@@ -193,6 +193,9 @@ final class DisplayPowerController {
// The light sensor, or null if not available or needed.
private Sensor mLightSensor;
+ // The doze screen brightness.
+ private final int mScreenBrightnessDozeConfig;
+
// The dim screen brightness.
private final int mScreenBrightnessDimConfig;
@@ -364,6 +367,9 @@ final class DisplayPowerController {
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));
@@ -559,7 +565,7 @@ final class DisplayPowerController {
final boolean mustNotify;
boolean mustInitialize = false;
boolean updateAutoBrightness = mTwilightChanged;
- boolean wasDim = false;
+ boolean wasDimOrDoze = false;
mTwilightChanged = false;
synchronized (mLock) {
@@ -579,7 +585,8 @@ final class DisplayPowerController {
!= mPendingRequestLocked.screenAutoBrightnessAdjustment) {
updateAutoBrightness = true;
}
- 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;
@@ -626,12 +633,12 @@ final class DisplayPowerController {
// Turn on the light sensor if needed.
if (mLightSensor != null) {
- setLightSensorEnabled(mPowerRequest.useAutoBrightness
- && wantScreenOn(mPowerRequest.screenState), updateAutoBrightness);
+ setLightSensorEnabled(mPowerRequest.wantLightSensorEnabled(),
+ updateAutoBrightness);
}
// Set the screen brightness.
- if (wantScreenOn(mPowerRequest.screenState)) {
+ if (mPowerRequest.wantScreenOnAny()) {
int target;
boolean slow;
if (mScreenAutoBrightness >= 0 && mLightSensorEnabled) {
@@ -648,12 +655,16 @@ final class DisplayPowerController {
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;
}
@@ -666,7 +677,7 @@ final class DisplayPowerController {
// Animate the screen on or off.
if (!mScreenOffBecauseOfProximity) {
- if (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
@@ -1211,6 +1222,7 @@ final class DisplayPowerController {
pw.println();
pw.println("Display Controller Configuration:");
+ pw.println(" mScreenBrightnessDozeConfig=" + mScreenBrightnessDozeConfig);
pw.println(" mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig);
pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
@@ -1288,15 +1300,6 @@ final class DisplayPowerController {
}
}
- private static boolean wantScreenOn(int state) {
- switch (state) {
- case DisplayPowerRequest.SCREEN_STATE_BRIGHT:
- case DisplayPowerRequest.SCREEN_STATE_DIM:
- return true;
- }
- return false;
- }
-
/**
* Asynchronous callbacks from the power controller to the power manager service.
*/
diff --git a/services/core/java/com/android/server/power/DisplayPowerRequest.java b/services/core/java/com/android/server/power/DisplayPowerRequest.java
index 22f17d7..6061a40 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 3dec234..16b09bc 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();
}
}
@@ -1871,10 +2024,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="
@@ -1890,6 +2046,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="
@@ -1900,6 +2060,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);
@@ -1975,8 +2143,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);
}
@@ -2153,6 +2321,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:
@@ -2165,6 +2334,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 "??? ";
}
@@ -2295,16 +2466,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 1a3ce63..d1cfff4 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -6,6 +6,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 0542ce0..697f56d 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -144,8 +144,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,
};
@@ -834,7 +832,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;
@@ -850,20 +848,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 {
@@ -1402,7 +1386,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
new file mode 100755
index 0000000..66a1984
--- /dev/null
+++ b/tests/DozeTest/res/drawable-hdpi/ic_app.png
Binary files differ
diff --git a/tests/DozeTest/res/drawable-mdpi/ic_app.png b/tests/DozeTest/res/drawable-mdpi/ic_app.png
new file mode 100644
index 0000000..5ae7701
--- /dev/null
+++ b/tests/DozeTest/res/drawable-mdpi/ic_app.png
Binary files differ
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();
+ }
+ };
+}