summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/os/PowerManager.java15
-rw-r--r--core/java/android/view/IWindowSession.aidl15
-rw-r--r--core/java/android/view/ViewRootImpl.java16
-rwxr-xr-xcore/res/res/values/config.xml6
-rwxr-xr-xcore/res/res/values/symbols.xml1
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java15
-rw-r--r--services/core/java/com/android/server/wm/Session.java10
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java17
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java44
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java5
10 files changed, 141 insertions, 3 deletions
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index d52dd30..e303f61 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -205,6 +205,20 @@ public final class PowerManager {
public static final int DOZE_WAKE_LOCK = 0x00000040;
/**
+ * Wake lock level: Keep the device awake enough to allow drawing to occur.
+ * <p>
+ * This is used by the window manager to allow applications to draw while the
+ * system is dozing. It currently has no effect unless the power manager is in
+ * the dozing state.
+ * </p><p>
+ * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
+ * </p>
+ *
+ * {@hide}
+ */
+ public static final int DRAW_WAKE_LOCK = 0x00000080;
+
+ /**
* Mask for the wake lock level component of a combined wake lock level and flags integer.
*
* @hide
@@ -489,6 +503,7 @@ public final class PowerManager {
case FULL_WAKE_LOCK:
case PROXIMITY_SCREEN_OFF_WAKE_LOCK:
case DOZE_WAKE_LOCK:
+ case DRAW_WAKE_LOCK:
break;
default:
throw new IllegalArgumentException("Must specify a valid wake lock level.");
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 7b13e84..63e1a85 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -197,4 +197,19 @@ interface IWindowSession {
void onRectangleOnScreenRequested(IBinder token, in Rect rectangle);
IWindowId getWindowId(IBinder window);
+
+ /**
+ * When the system is dozing in a low-power partially suspended state, pokes a short
+ * lived wake lock and ensures that the display is ready to accept the next frame
+ * of content drawn in the window.
+ *
+ * This mechanism is bound to the window rather than to the display manager or the
+ * power manager so that the system can ensure that the window is actually visible
+ * and prevent runaway applications from draining the battery. This is similar to how
+ * FLAG_KEEP_SCREEN_ON works.
+ *
+ * This method is synchronous because it may need to acquire a wake lock before returning.
+ * The assumption is that this method will be called rather infrequently.
+ */
+ void pokeDrawLock(IBinder window);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e4d82b1..90c2bd1 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -47,6 +47,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -831,6 +832,7 @@ public final class ViewRootImpl implements ViewParent,
final int newDisplayState = mDisplay.getState();
if (oldDisplayState != newDisplayState) {
mAttachInfo.mDisplayState = newDisplayState;
+ pokeDrawLockIfNeeded();
if (oldDisplayState != Display.STATE_UNKNOWN) {
final int oldScreenState = toViewScreenState(oldDisplayState);
final int newScreenState = toViewScreenState(newDisplayState);
@@ -861,6 +863,19 @@ public final class ViewRootImpl implements ViewParent,
}
};
+ void pokeDrawLockIfNeeded() {
+ final int displayState = mAttachInfo.mDisplayState;
+ if (mView != null && mAdded && mTraversalScheduled
+ && (displayState == Display.STATE_DOZE
+ || displayState == Display.STATE_DOZE_SUSPEND)) {
+ try {
+ mWindowSession.pokeDrawLock(mWindow);
+ } catch (RemoteException ex) {
+ // System server died, oh well.
+ }
+ }
+ }
+
@Override
public void requestFitSystemWindows() {
checkThread();
@@ -1035,6 +1050,7 @@ public final class ViewRootImpl implements ViewParent,
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
+ pokeDrawLockIfNeeded();
}
}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 945b505..739ff1e 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1844,6 +1844,12 @@
<item>users</item>
</string-array>
+ <!-- Number of milliseconds to hold a wake lock to ensure that drawing is fully
+ flushed to the display while dozing. This value needs to be large enough
+ to account for processing and rendering time plus a frame or two of latency
+ in the display pipeline plus some slack just to be sure. -->
+ <integer name="config_drawLockTimeoutMillis">120</integer>
+
<!-- default telephony hardware configuration for this platform.
-->
<!-- this string array should be overridden by the device to present a list
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 84ebe74..25a1462 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -345,6 +345,7 @@
<java-symbol type="integer" name="config_bluetooth_max_advertisers" />
<java-symbol type="integer" name="config_bluetooth_max_scan_filters" />
<java-symbol type="integer" name="config_cursorWindowSize" />
+ <java-symbol type="integer" name="config_drawLockTimeoutMillis" />
<java-symbol type="integer" name="config_doublePressOnPowerBehavior" />
<java-symbol type="integer" name="config_extraFreeKbytesAdjust" />
<java-symbol type="integer" name="config_extraFreeKbytesAbsolute" />
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 9786b42..66c2f5f 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -128,6 +128,7 @@ public final class PowerManagerService extends SystemService
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;
+ private static final int WAKE_LOCK_DRAW = 1 << 7;
// Summarizes the user activity state.
private static final int USER_ACTIVITY_SCREEN_BRIGHT = 1 << 0;
@@ -1398,12 +1399,15 @@ public final class PowerManagerService extends SystemService
case PowerManager.DOZE_WAKE_LOCK:
mWakeLockSummary |= WAKE_LOCK_DOZE;
break;
+ case PowerManager.DRAW_WAKE_LOCK:
+ mWakeLockSummary |= WAKE_LOCK_DRAW;
+ break;
}
}
// Cancel wake locks that make no sense based on the current state.
if (mWakefulness != WAKEFULNESS_DOZING) {
- mWakeLockSummary &= ~WAKE_LOCK_DOZE;
+ mWakeLockSummary &= ~(WAKE_LOCK_DOZE | WAKE_LOCK_DRAW);
}
if (mWakefulness == WAKEFULNESS_ASLEEP
|| (mWakeLockSummary & WAKE_LOCK_DOZE) != 0) {
@@ -1422,6 +1426,9 @@ public final class PowerManagerService extends SystemService
mWakeLockSummary |= WAKE_LOCK_CPU;
}
}
+ if ((mWakeLockSummary & WAKE_LOCK_DRAW) != 0) {
+ mWakeLockSummary |= WAKE_LOCK_CPU;
+ }
if (DEBUG_SPEW) {
Slog.d(TAG, "updateWakeLockSummaryLocked: mWakefulness="
@@ -1845,6 +1852,10 @@ public final class PowerManagerService extends SystemService
if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) {
mDisplayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager;
+ if (mDisplayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND
+ && (mWakeLockSummary & WAKE_LOCK_DRAW) != 0) {
+ mDisplayPowerRequest.dozeScreenState = Display.STATE_DOZE;
+ }
mDisplayPowerRequest.dozeScreenBrightness =
mDozeScreenBrightnessOverrideFromDreamManager;
} else {
@@ -2712,6 +2723,8 @@ public final class PowerManagerService extends SystemService
return "PROXIMITY_SCREEN_OFF_WAKE_LOCK";
case PowerManager.DOZE_WAKE_LOCK:
return "DOZE_WAKE_LOCK ";
+ case PowerManager.DRAW_WAKE_LOCK:
+ return "DRAW_WAKE_LOCK ";
default:
return "??? ";
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index a4dfd8a..a1d145c 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -475,6 +475,16 @@ final class Session extends IWindowSession.Stub
return mService.getWindowId(window);
}
+ @Override
+ public void pokeDrawLock(IBinder window) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mService.pokeDrawLock(this, window);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
void windowAddedLocked() {
if (mSurfaceSession == null) {
if (WindowManagerService.localLOGV) Slog.v(
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4dd4e1e..9a984ed 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -194,6 +194,7 @@ public class WindowManagerService extends IWindowManager.Stub
static final boolean DEBUG_TASK_MOVEMENT = false;
static final boolean DEBUG_STACK = false;
static final boolean DEBUG_DISPLAY = false;
+ static final boolean DEBUG_POWER = false;
static final boolean SHOW_SURFACE_ALLOC = false;
static final boolean SHOW_TRANSACTIONS = false;
static final boolean SHOW_LIGHT_TRANSACTIONS = false || SHOW_TRANSACTIONS;
@@ -338,6 +339,7 @@ public class WindowManagerService extends IWindowManager.Stub
final boolean mHaveInputMethods;
final boolean mHasPermanentDpad;
+ final long mDrawLockTimeoutMillis;
final boolean mAllowBootMessages;
@@ -827,6 +829,8 @@ public class WindowManagerService extends IWindowManager.Stub
com.android.internal.R.bool.config_hasPermanentDpad);
mInTouchMode = context.getResources().getBoolean(
com.android.internal.R.bool.config_defaultInTouchMode);
+ mDrawLockTimeoutMillis = context.getResources().getInteger(
+ com.android.internal.R.integer.config_drawLockTimeoutMillis);
mInputManager = inputManager; // Must be before createDisplayContentLocked.
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mDisplaySettings = new DisplaySettings();
@@ -2992,6 +2996,15 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ public void pokeDrawLock(Session session, IBinder token) {
+ synchronized (mWindowMap) {
+ WindowState window = windowForClientLocked(session, token, false);
+ if (window != null) {
+ window.pokeDrawLockLw(mDrawLockTimeoutMillis);
+ }
+ }
+ }
+
public int relayoutWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int requestedWidth,
int requestedHeight, int viewVisibility, int flags,
@@ -10137,7 +10150,9 @@ public class WindowManagerService extends IWindowManager.Stub
if (mAllowTheaterModeWakeFromLayout
|| Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.THEATER_MODE_ON, 0) == 0) {
- if (DEBUG_VISIBILITY) Slog.v(TAG, "Turning screen on after layout!");
+ if (DEBUG_VISIBILITY || DEBUG_POWER) {
+ Slog.v(TAG, "Turning screen on after layout!");
+ }
mPowerManager.wakeUp(SystemClock.uptimeMillis());
}
mTurnOnScreen = false;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 978f5c3..11cb68b 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -19,9 +19,9 @@ package com.android.server.wm;
import static com.android.server.wm.WindowManagerService.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerService.DEBUG_LAYOUT;
import static com.android.server.wm.WindowManagerService.DEBUG_ORIENTATION;
+import static com.android.server.wm.WindowManagerService.DEBUG_POWER;
import static com.android.server.wm.WindowManagerService.DEBUG_RESIZE;
import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
-
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
@@ -34,12 +34,15 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import android.app.AppOpsManager;
import android.os.Debug;
+import android.os.PowerManager;
import android.os.RemoteCallbackList;
import android.os.SystemClock;
+import android.os.WorkSource;
import android.util.TimeUtils;
import android.view.Display;
import android.view.IWindowFocusObserver;
import android.view.IWindowId;
+
import com.android.server.input.InputWindowHandle;
import android.content.Context;
@@ -345,6 +348,15 @@ final class WindowState implements WindowManagerPolicy.WindowState {
* the status bar */
boolean mUnderStatusBar = true;
+ /**
+ * Wake lock for drawing.
+ * Even though it's slightly more expensive to do so, we will use a separate wake lock
+ * for each app that is requesting to draw while dozing so that we can accurately track
+ * who is preventing the system from suspending.
+ * This lock is only acquired on first use.
+ */
+ PowerManager.WakeLock mDrawLock;
+
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState attachedWindow, int appOp, int seq, WindowManager.LayoutParams a,
int viewVisibility, final DisplayContent displayContent) {
@@ -1270,6 +1282,33 @@ final class WindowState implements WindowManagerPolicy.WindowState {
}
}
+ public void pokeDrawLockLw(long timeout) {
+ if (isVisibleOrAdding()) {
+ if (mDrawLock == null) {
+ // We want the tag name to be somewhat stable so that it is easier to correlate
+ // in wake lock statistics. So in particular, we don't want to include the
+ // window's hash code as in toString().
+ CharSequence tag = mAttrs.getTitle();
+ if (tag == null) {
+ tag = mAttrs.packageName;
+ }
+ mDrawLock = mService.mPowerManager.newWakeLock(
+ PowerManager.DRAW_WAKE_LOCK, "Window:" + tag);
+ mDrawLock.setReferenceCounted(false);
+ mDrawLock.setWorkSource(new WorkSource(mOwnerUid, mAttrs.packageName));
+ }
+ // Each call to acquire resets the timeout.
+ if (DEBUG_POWER) {
+ Slog.d(TAG, "pokeDrawLock: poking draw lock on behalf of visible window owned by "
+ + mAttrs.packageName);
+ }
+ mDrawLock.acquire(timeout);
+ } else if (DEBUG_POWER) {
+ Slog.d(TAG, "pokeDrawLock: suppressed draw lock request for invisible window "
+ + "owned by " + mAttrs.packageName);
+ }
+ }
+
@Override
public boolean isAlive() {
return mClient.asBinder().isBinderAlive();
@@ -1624,6 +1663,9 @@ final class WindowState implements WindowManagerPolicy.WindowState {
pw.print(" mWallpaperDisplayOffsetY=");
pw.println(mWallpaperDisplayOffsetY);
}
+ if (mDrawLock != null) {
+ pw.println("mDrawLock=" + mDrawLock);
+ }
}
String makeInputChannelName() {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index 0f51d00..25f7078 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -212,4 +212,9 @@ public final class BridgeWindowSession implements IWindowSession {
// pass for now.
return null;
}
+
+ @Override
+ public void pokeDrawLock(IBinder window) {
+ // pass for now.
+ }
}