diff options
3 files changed, 199 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 @@ -54,6 +54,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 5589673973c3..b785eb017e29 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; @@ -39,6 +41,8 @@ import android.content.pm.ServiceInfo; 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 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())) { + 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 @@ 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(); } @@ -869,6 +1008,11 @@ public final class DreamManagerService extends SystemService { } @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 fa0d41c5ac2f..352d4be6c7ce 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; @@ -971,7 +972,12 @@ 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 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 @@ 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. * * @return {@code true} if the device was sent to sleep, {@code false} if the device did not @@ -1595,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; } @@ -2483,6 +2518,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(); |