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.
Merged-In: I9b6859b4933839841b76b832f275dcc14313f192
Change-Id: Id0c37612b866111ed265ac282963611429bfb36b
diff --git a/core/java/android/service/dreams/DreamManagerInternal.java b/core/java/android/service/dreams/DreamManagerInternal.java
index 295171c..5f30ad0 100644
--- a/core/java/android/service/dreams/DreamManagerInternal.java
+++ b/core/java/android/service/dreams/DreamManagerInternal.java
@@ -54,6 +54,13 @@
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 5589673..b785eb0 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 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;
@@ -39,6 +41,8 @@
import android.database.ContentObserver;
import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.input.InputManagerInternal;
+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 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 @@
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 @@
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 @@
}
};
+ 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())) {
+ final 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 @@
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 @@
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 @@
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 @@
}
}
- /** 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 @@
}
}
+ /** 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();
}
@@ -869,6 +1008,11 @@
}
@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 fa0d41c..352d4be 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 @@
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;
@@ -971,7 +972,12 @@
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 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");
@@ -1020,11 +1026,39 @@
}
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.
*
* @return {@code true} if the device was sent to sleep, {@code false} if the device did not
@@ -1595,7 +1629,8 @@
// 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;
}
@@ -2483,6 +2518,15 @@
}
}
+ 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();