diff options
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. + } } |