diff --git a/core/java/android/service/dreams/DreamManagerInternal.java b/core/java/android/service/dreams/DreamManagerInternal.java index 295171ca9bbd2..cd38e8a01d627 100644 --- a/core/java/android/service/dreams/DreamManagerInternal.java +++ b/core/java/android/service/dreams/DreamManagerInternal.java @@ -16,7 +16,6 @@ package android.service.dreams; -import android.content.ComponentName; /** * Dream manager local system service interface. @@ -54,17 +53,9 @@ public abstract class DreamManagerInternal { public abstract void requestDream(); /** - * Called by the ActivityTaskManagerService to verify that the startDreamActivity - * request comes from the current active dream component. + * Whether dreaming can start given user settings and the current dock/charge state. * - * This function and its call path should not acquire the DreamManagerService lock - * to avoid deadlock with the ActivityTaskManager lock. - * - * TODO: Make this interaction push-based - the DreamManager should inform the - * ActivityTaskManager whenever the active dream component changes. - * - * @param doze If true returns the current active doze component. Otherwise, returns the - * active dream component. + * @param isScreenOn True if the screen is currently on. */ - public abstract ComponentName getActiveDreamComponent(boolean doze); + public abstract boolean canStartDreaming(boolean isScreenOn); } diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index f047905f18b62..a4a38c713a66c 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -1327,6 +1327,12 @@ "group": "WM_DEBUG_ANIM", "at": "com\/android\/server\/wm\/WindowState.java" }, + "-787664727": { + "message": "Cannot launch dream activity due to invalid state. dream component: %s packageName: %s", + "level": "ERROR", + "group": "WM_DEBUG_DREAM", + "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" + }, "-784959154": { "message": "Attempted to add private presentation window to a non-private display. Aborting.", "level": "WARN", @@ -2863,6 +2869,12 @@ "group": "WM_DEBUG_BOOT", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "601283564": { + "message": "Dream packageName does not match active dream. Package %s does not match %s", + "level": "ERROR", + "group": "WM_DEBUG_DREAM", + "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" + }, "608694300": { "message": " NEW SURFACE SESSION %s", "level": "INFO", @@ -3103,12 +3115,6 @@ "group": "WM_DEBUG_STARTING_WINDOW", "at": "com\/android\/server\/wm\/WindowStateAnimator.java" }, - "829869827": { - "message": "Cannot launch dream activity due to invalid state. dreaming: %b packageName: %s", - "level": "ERROR", - "group": "WM_DEBUG_DREAM", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, "835814848": { "message": "%s", "level": "INFO", @@ -4141,12 +4147,6 @@ "group": "WM_DEBUG_WINDOW_ORGANIZER", "at": "com\/android\/server\/wm\/TaskOrganizerController.java" }, - "1918771553": { - "message": "Dream packageName does not match active dream. Package %s does not match %s or %s", - "level": "ERROR", - "group": "WM_DEBUG_DREAM", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, "1921821199": { "message": "Preserving %s until the new one is added", "level": "VERBOSE", diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index e4aa5e574955f..b9511c45771be 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(); } @@ -352,10 +491,6 @@ public final class DreamManagerService extends SystemService { } } - private ComponentName getActiveDreamComponentInternal(boolean doze) { - return chooseDreamForUser(doze, ActivityManager.getCurrentUser()); - } - /** * If doze is true, returns the doze component for the user. * Otherwise, returns the system dream component, if present. @@ -508,7 +643,7 @@ public final class DreamManagerService extends SystemService { .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, DREAM_WAKE_LOCK_TAG); final Binder dreamToken = mCurrentDream.token; mHandler.post(wakeLock.wrap(() -> { - mAtmInternal.notifyDreamStateChanged(true); + mAtmInternal.notifyActiveDreamChanged(name); mController.startDream(dreamToken, name, isPreviewMode, canDoze, userId, wakeLock, mDreamOverlayServiceName, reason); })); @@ -533,7 +668,7 @@ public final class DreamManagerService extends SystemService { @GuardedBy("mLock") private void cleanupDreamLocked() { - mHandler.post(() -> mAtmInternal.notifyDreamStateChanged(false /*dreaming*/)); + mHandler.post(() -> mAtmInternal.notifyActiveDreamChanged(null)); if (mCurrentDream == null) { return; @@ -869,8 +1004,8 @@ public final class DreamManagerService extends SystemService { } @Override - public ComponentName getActiveDreamComponent(boolean doze) { - return getActiveDreamComponentInternal(doze); + public boolean canStartDreaming(boolean isScreenOn) { + return canStartDreamingInternal(isScreenOn); } @Override diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index fa0d41c5ac2f8..352d4be6c7cec 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,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. * @@ -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(); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index e7b62b0d66d3b..2792f4265caa5 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -287,8 +287,10 @@ public abstract class ActivityTaskManagerInternal { /** * Called when the device changes its dreaming state. + * + * @param activeDreamComponent The currently active dream. If null, the device is not dreaming. */ - public abstract void notifyDreamStateChanged(boolean dreaming); + public abstract void notifyActiveDreamChanged(@Nullable ComponentName activeDreamComponent); /** * Set a uid that is allowed to bypass stopped app switches, launching an app diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 0398cc84f9db5..1ba7e687a5537 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -208,7 +208,6 @@ import android.os.UserManager; import android.os.WorkSource; import android.provider.Settings; import android.service.dreams.DreamActivity; -import android.service.dreams.DreamManagerInternal; import android.service.voice.IVoiceInteractionSession; import android.service.voice.VoiceInteractionManagerInternal; import android.sysprop.DisplayProperties; @@ -669,11 +668,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private volatile boolean mSleeping; /** - * The mDreaming state is set by the {@link DreamManagerService} when it receives a request to - * start/stop the dream. It is set to true shortly before the {@link DreamService} is started. - * It is set to false after the {@link DreamService} is stopped. + * The mActiveDreamComponent state is set by the {@link DreamManagerService} when it receives a + * request to start/stop the dream. It is set to the active dream shortly before the + * {@link DreamService} is started. It is set to null after the {@link DreamService} is stopped. */ - private volatile boolean mDreaming; + @Nullable + private volatile ComponentName mActiveDreamComponent; /** * The process state used for processes that are running the top activities. @@ -1442,31 +1442,21 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } boolean isDreaming() { - return mDreaming; + return mActiveDreamComponent != null; } boolean canLaunchDreamActivity(String packageName) { - if (!mDreaming || packageName == null) { + if (mActiveDreamComponent == null || packageName == null) { ProtoLog.e(WM_DEBUG_DREAM, "Cannot launch dream activity due to invalid state. " - + "dreaming: %b packageName: %s", mDreaming, packageName); + + "dream component: %s packageName: %s", mActiveDreamComponent, packageName); return false; } - final DreamManagerInternal dreamManager = - LocalServices.getService(DreamManagerInternal.class); - // Verify that the package is the current active dream or doze component. The - // getActiveDreamComponent() call path does not acquire the DreamManager lock and thus - // is safe to use. - final ComponentName activeDream = dreamManager.getActiveDreamComponent(false /* doze */); - if (activeDream != null && packageName.equals(activeDream.getPackageName())) { - return true; - } - final ComponentName activeDoze = dreamManager.getActiveDreamComponent(true /* doze */); - if (activeDoze != null && packageName.equals(activeDoze.getPackageName())) { + if (packageName.equals(mActiveDreamComponent.getPackageName())) { return true; } ProtoLog.e(WM_DEBUG_DREAM, - "Dream packageName does not match active dream. Package %s does not match %s or %s", - packageName, String.valueOf(activeDream), String.valueOf(activeDoze)); + "Dream packageName does not match active dream. Package %s does not match %s", + packageName, String.valueOf(mActiveDreamComponent)); return false; } @@ -5676,9 +5666,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void notifyDreamStateChanged(boolean dreaming) { + public void notifyActiveDreamChanged(@Nullable ComponentName dreamComponent) { synchronized (mGlobalLock) { - mDreaming = dreaming; + mActiveDreamComponent = dreamComponent; } }