From 7012ed5a09b6d3d7d98471dd884690ff66a43dec Mon Sep 17 00:00:00 2001 From: Will Leshner Date: Tue, 27 Sep 2022 16:23:40 -0700 Subject: Implement alternate power button behavior for dreams. This change implements a power button behavior that prioritizes entering a dream on press, rather than turning off the screen. Bug: 245961904 Test: Manually as follows: Make sure config_shortPressOnPowerBehavior is set to SHORT_PRESS_POWER_DREAM_OR_SLEEP. 1. Set screensaver to dock then dock device. Make sure tapping power button when screen on goes to dream. Make sure tapping power button when screen off goes to dream. Make sure tapping power button while dreaming turns display off. 2. Set screensaver to charge then plug in device. Make sure tapping power button when screen on goes to dream. Make sure tapping power button when screen off goes to dream. Make sure tapping power button while dreaming turns display off. 3. Set screensaver to charge or dock and perform the same tests as above. 4. Set screensaver to dock and then undock device. Make sure tapping power button does not start dreaming. Note that the device may charge while docked, so setting screensaver to charge may act the same when docked or charging. Change-Id: I9b6859b4933839841b76b832f275dcc14313f192 --- .../service/dreams/DreamManagerInternal.java | 7 + .../android/server/dreams/DreamManagerService.java | 148 ++++++++++++++++++++- .../android/server/policy/PhoneWindowManager.java | 50 ++++++- 3 files changed, 201 insertions(+), 4 deletions(-) diff --git a/core/java/android/service/dreams/DreamManagerInternal.java b/core/java/android/service/dreams/DreamManagerInternal.java index 295171ca9bbd..5f30ad054c50 100644 --- a/core/java/android/service/dreams/DreamManagerInternal.java +++ b/core/java/android/service/dreams/DreamManagerInternal.java @@ -53,6 +53,13 @@ public abstract class DreamManagerInternal { */ public abstract void requestDream(); + /** + * Whether dreaming can start given user settings and the current dock/charge state. + * + * @param isScreenOn True if the screen is currently on. + */ + public abstract boolean canStartDreaming(boolean isScreenOn); + /** * Called by the ActivityTaskManagerService to verify that the startDreamActivity * request comes from the current active dream component. diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index 6e2ccebb6ff4..4ca4817cc4fe 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -23,12 +23,14 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static com.android.server.wm.ActivityInterceptorCallback.DREAM_MANAGER_ORDERED_ID; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.TaskInfo; import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -38,6 +40,8 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ServiceInfo; import android.database.ContentObserver; import android.hardware.display.AmbientDisplayConfiguration; +import android.net.Uri; +import android.os.BatteryManager; import android.os.Binder; import android.os.Build; import android.os.Handler; @@ -72,6 +76,8 @@ import com.android.server.wm.ActivityTaskManagerInternal; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -88,6 +94,15 @@ public final class DreamManagerService extends SystemService { private static final String DOZE_WAKE_LOCK_TAG = "dream:doze"; private static final String DREAM_WAKE_LOCK_TAG = "dream:dream"; + /** Constants for the when to activate dreams. */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({DREAM_ON_DOCK, DREAM_ON_CHARGE, DREAM_ON_DOCK_OR_CHARGE}) + public @interface WhenToDream {} + private static final int DREAM_DISABLED = 0x0; + private static final int DREAM_ON_DOCK = 0x1; + private static final int DREAM_ON_CHARGE = 0x2; + private static final int DREAM_ON_DOCK_OR_CHARGE = 0x3; + private final Object mLock = new Object(); private final Context mContext; @@ -101,12 +116,20 @@ public final class DreamManagerService extends SystemService { private final DreamUiEventLogger mDreamUiEventLogger; private final ComponentName mAmbientDisplayComponent; private final boolean mDismissDreamOnActivityStart; + private final boolean mDreamsOnlyEnabledForSystemUser; + private final boolean mDreamsEnabledByDefaultConfig; + private final boolean mDreamsActivatedOnChargeByDefault; + private final boolean mDreamsActivatedOnDockByDefault; @GuardedBy("mLock") private DreamRecord mCurrentDream; private boolean mForceAmbientDisplayEnabled; - private final boolean mDreamsOnlyEnabledForSystemUser; + private SettingsObserver mSettingsObserver; + private boolean mDreamsEnabledSetting; + @WhenToDream private int mWhenToDream; + private boolean mIsDocked; + private boolean mIsCharging; // A temporary dream component that, when present, takes precedence over user configured dream // component. @@ -144,6 +167,37 @@ public final class DreamManagerService extends SystemService { } }; + private final BroadcastReceiver mChargingReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + mIsCharging = (BatteryManager.ACTION_CHARGING.equals(intent.getAction())); + } + }; + + private final BroadcastReceiver mDockStateReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_DOCK_EVENT.equals(intent.getAction())) { + int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, + Intent.EXTRA_DOCK_STATE_UNDOCKED); + mIsDocked = dockState != Intent.EXTRA_DOCK_STATE_UNDOCKED; + } + } + }; + + private final class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { + super(handler); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + synchronized (mLock) { + updateWhenToDreamSettings(); + } + } + } + public DreamManagerService(Context context) { super(context); mContext = context; @@ -164,6 +218,14 @@ public final class DreamManagerService extends SystemService { mContext.getResources().getBoolean(R.bool.config_dreamsOnlyEnabledForSystemUser); mDismissDreamOnActivityStart = mContext.getResources().getBoolean( R.bool.config_dismissDreamOnActivityStart); + + mDreamsEnabledByDefaultConfig = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_dreamsEnabledByDefault); + mDreamsActivatedOnChargeByDefault = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault); + mDreamsActivatedOnDockByDefault = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault); + mSettingsObserver = new SettingsObserver(mHandler); } @Override @@ -197,6 +259,30 @@ public final class DreamManagerService extends SystemService { DREAM_MANAGER_ORDERED_ID, mActivityInterceptorCallback); } + + mContext.registerReceiver( + mDockStateReceiver, new IntentFilter(Intent.ACTION_DOCK_EVENT)); + IntentFilter chargingIntentFilter = new IntentFilter(); + chargingIntentFilter.addAction(BatteryManager.ACTION_CHARGING); + chargingIntentFilter.addAction(BatteryManager.ACTION_DISCHARGING); + mContext.registerReceiver(mChargingReceiver, chargingIntentFilter); + + mSettingsObserver = new SettingsObserver(mHandler); + mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP), + false, mSettingsObserver, UserHandle.USER_ALL); + mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK), + false, mSettingsObserver, UserHandle.USER_ALL); + mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.SCREENSAVER_ENABLED), + false, mSettingsObserver, UserHandle.USER_ALL); + + // We don't get an initial broadcast for the batter state, so we have to initialize + // directly from BatteryManager. + mIsCharging = mContext.getSystemService(BatteryManager.class).isCharging(); + + updateWhenToDreamSettings(); } } @@ -207,6 +293,14 @@ public final class DreamManagerService extends SystemService { pw.println("mCurrentDream=" + mCurrentDream); pw.println("mForceAmbientDisplayEnabled=" + mForceAmbientDisplayEnabled); pw.println("mDreamsOnlyEnabledForSystemUser=" + mDreamsOnlyEnabledForSystemUser); + pw.println("mDreamsEnabledSetting=" + mDreamsEnabledSetting); + pw.println("mForceAmbientDisplayEnabled=" + mForceAmbientDisplayEnabled); + pw.println("mDreamsOnlyEnabledForSystemUser=" + mDreamsOnlyEnabledForSystemUser); + pw.println("mDreamsActivatedOnDockByDefault=" + mDreamsActivatedOnDockByDefault); + pw.println("mDreamsActivatedOnChargeByDefault=" + mDreamsActivatedOnChargeByDefault); + pw.println("mIsDocked=" + mIsDocked); + pw.println("mIsCharging=" + mIsCharging); + pw.println("mWhenToDream=" + mWhenToDream); pw.println("getDozeComponent()=" + getDozeComponent()); pw.println(); @@ -214,7 +308,28 @@ public final class DreamManagerService extends SystemService { } } - /** Whether a real dream is occurring. */ + private void updateWhenToDreamSettings() { + synchronized (mLock) { + final ContentResolver resolver = mContext.getContentResolver(); + + final int activateWhenCharging = (Settings.Secure.getIntForUser(resolver, + Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, + mDreamsActivatedOnChargeByDefault ? 1 : 0, + UserHandle.USER_CURRENT) != 0) ? DREAM_ON_CHARGE : DREAM_DISABLED; + final int activateWhenDocked = (Settings.Secure.getIntForUser(resolver, + Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK, + mDreamsActivatedOnDockByDefault ? 1 : 0, + UserHandle.USER_CURRENT) != 0) ? DREAM_ON_DOCK : DREAM_DISABLED; + mWhenToDream = activateWhenCharging + activateWhenDocked; + + mDreamsEnabledSetting = (Settings.Secure.getIntForUser(resolver, + Settings.Secure.SCREENSAVER_ENABLED, + mDreamsEnabledByDefaultConfig ? 1 : 0, + UserHandle.USER_CURRENT) != 0); + } + } + + /** Whether a real dream is occurring. */ private boolean isDreamingInternal() { synchronized (mLock) { return mCurrentDream != null && !mCurrentDream.isPreview @@ -236,6 +351,30 @@ public final class DreamManagerService extends SystemService { } } + /** Whether dreaming can start given user settings and the current dock/charge state. */ + private boolean canStartDreamingInternal(boolean isScreenOn) { + synchronized (mLock) { + // Can't start dreaming if we are already dreaming. + if (isScreenOn && isDreamingInternal()) { + return false; + } + + if (!mDreamsEnabledSetting) { + return false; + } + + if ((mWhenToDream & DREAM_ON_CHARGE) == DREAM_ON_CHARGE) { + return mIsCharging; + } + + if ((mWhenToDream & DREAM_ON_DOCK) == DREAM_ON_DOCK) { + return mIsDocked; + } + + return false; + } + } + protected void requestStartDreamFromShell() { requestDreamInternal(); } @@ -868,6 +1007,11 @@ public final class DreamManagerService extends SystemService { return isDreamingInternal(); } + @Override + public boolean canStartDreaming(boolean isScreenOn) { + return canStartDreamingInternal(isScreenOn); + } + @Override public ComponentName getActiveDreamComponent(boolean doze) { return getActiveDreamComponentInternal(doze); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index c4e122d4497d..98b5c1ba639b 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -256,6 +256,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final int SHORT_PRESS_POWER_GO_HOME = 4; static final int SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME = 5; static final int SHORT_PRESS_POWER_LOCK_OR_SLEEP = 6; + static final int SHORT_PRESS_POWER_DREAM_OR_SLEEP = 7; // must match: config_LongPressOnPowerBehavior in config.xml static final int LONG_PRESS_POWER_NOTHING = 0; @@ -969,7 +970,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior); } else if (count > 3 && count <= getMaxMultiPressPowerCount()) { Slog.d(TAG, "No behavior defined for power press count " + count); - } else if (count == 1 && interactive && !beganFromNonInteractive) { + } else if (count == 1 && interactive) { + if (beganFromNonInteractive) { + // The "screen is off" case, where we might want to start dreaming on power button + // press. + attemptToDreamFromShortPowerButtonPress(false, () -> {}); + return; + } + if (mSideFpsEventHandler.shouldConsumeSinglePress(eventTime)) { Slog.i(TAG, "Suppressing power key because the user is interacting with the " + "fingerprint sensor"); @@ -1018,10 +1026,38 @@ public class PhoneWindowManager implements WindowManagerPolicy { } break; } + case SHORT_PRESS_POWER_DREAM_OR_SLEEP: { + attemptToDreamFromShortPowerButtonPress( + true, + () -> sleepDefaultDisplayFromPowerButton(eventTime, 0)); + break; + } } } } + /** + * Attempt to dream from a power button press. + * + * @param isScreenOn Whether the screen is currently on. + * @param noDreamAction The action to perform if dreaming is not possible. + */ + private void attemptToDreamFromShortPowerButtonPress( + boolean isScreenOn, Runnable noDreamAction) { + if (mShortPressOnPowerBehavior != SHORT_PRESS_POWER_DREAM_OR_SLEEP) { + noDreamAction.run(); + return; + } + + final DreamManagerInternal dreamManagerInternal = getDreamManagerInternal(); + if (dreamManagerInternal == null || !dreamManagerInternal.canStartDreaming(isScreenOn)) { + noDreamAction.run(); + return; + } + + dreamManagerInternal.requestDream(); + } + /** * Sends the default display to sleep as a result of a power button press. * @@ -1593,7 +1629,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // If there's a dream running then use home to escape the dream // but don't actually go home. - if (mDreamManagerInternal != null && mDreamManagerInternal.isDreaming()) { + final DreamManagerInternal dreamManagerInternal = getDreamManagerInternal(); + if (dreamManagerInternal != null && dreamManagerInternal.isDreaming()) { mDreamManagerInternal.stopDream(false /*immediate*/, "short press on home" /*reason*/); return; } @@ -2529,6 +2566,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + private DreamManagerInternal getDreamManagerInternal() { + if (mDreamManagerInternal == null) { + // If mDreamManagerInternal is null, attempt to re-fetch it. + mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class); + } + + return mDreamManagerInternal; + } + private void updateWakeGestureListenerLp() { if (shouldEnableWakeGestureLp()) { mWakeGestureListener.requestWakeUpTrigger(); -- cgit v1.2.3-59-g8ed1b