From efe48f784045b78cc47319ad0719f515a65f7edb Mon Sep 17 00:00:00 2001 From: dshivangi Date: Mon, 26 Jun 2023 10:58:42 +0000 Subject: Add 'Stay unlocked on fold' toogle to settings Added 'Stay unlocked on fold' toogle to display settings. Enabling this setting will allow user to keep their device awake after the fold the device. Test: atest StayAwakeOnFoldPreferenceControllerTest LogicalDisplayMapperTest Test: manual Built and flashed foldable device and manually tested by 1. unfolded device -> enable setting -> folded device 2. unfolded device -> disabled setting -> folded devices Built and flashed non-foldable device and manually tested by 1. Went to Settings -> display -> the toogle setting is not visible Bug: 274447767 Change-Id: I09915082b759c235811fc143184a19e2b84f1c8e --- core/java/android/provider/Settings.java | 9 ++++ .../provider/settings/backup/SystemSettings.java | 1 + .../validators/SystemSettingsValidators.java | 1 + .../server/display/DisplayManagerService.java | 4 +- .../server/display/LogicalDisplayMapper.java | 19 +++++---- .../android/server/utils/FoldSettingWrapper.java | 48 ++++++++++++++++++++++ .../server/display/LogicalDisplayMapperTest.java | 17 +++++++- 7 files changed, 89 insertions(+), 10 deletions(-) create mode 100644 services/core/java/com/android/server/utils/FoldSettingWrapper.java diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index d425bf8ae557..df1552b47308 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4698,6 +4698,15 @@ public final class Settings { @Readable public static final String PEAK_REFRESH_RATE = "peak_refresh_rate"; + /** + * Control whether to stay awake on fold + * + * If this isn't set, the system falls back to a device specific default. + * @hide + */ + @Readable + public static final String STAY_AWAKE_ON_FOLD = "stay_awake_on_fold"; + /** * The amount of time in milliseconds before the device goes to sleep or begins * to dream after a period of inactivity. This value is also known as the diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java index 6a5535d345db..6b0a9060d782 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java @@ -61,6 +61,7 @@ public class SystemSettings { Settings.System.TTY_MODE, Settings.System.MASTER_MONO, Settings.System.MASTER_BALANCE, + Settings.System.STAY_AWAKE_ON_FOLD, Settings.System.SOUND_EFFECTS_ENABLED, Settings.System.HAPTIC_FEEDBACK_ENABLED, Settings.System.POWER_SOUNDS_ENABLED, // moved to global diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java index 753c860bd5d6..a08d07e1d778 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java @@ -218,6 +218,7 @@ public class SystemSettingsValidators { VALIDATORS.put(System.WIFI_STATIC_DNS1, LENIENT_IP_ADDRESS_VALIDATOR); VALIDATORS.put(System.WIFI_STATIC_DNS2, LENIENT_IP_ADDRESS_VALIDATOR); VALIDATORS.put(System.SHOW_BATTERY_PERCENT, BOOLEAN_VALIDATOR); + VALIDATORS.put(System.STAY_AWAKE_ON_FOLD, BOOLEAN_VALIDATOR); VALIDATORS.put(System.NOTIFICATION_LIGHT_PULSE, BOOLEAN_VALIDATOR); VALIDATORS.put(System.WEAR_ACCESSIBILITY_GESTURE_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(System.CLOCKWORK_BLUETOOTH_SETTINGS_PREF, BOOLEAN_VALIDATOR); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 858800a82151..4ba0db5db746 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -156,6 +156,7 @@ import com.android.server.display.layout.Layout; import com.android.server.display.mode.DisplayModeDirector; import com.android.server.display.utils.SensorUtils; import com.android.server.input.InputManagerInternal; +import com.android.server.utils.FoldSettingWrapper; import com.android.server.wm.SurfaceAnimationThread; import com.android.server.wm.WindowManagerInternal; @@ -539,7 +540,8 @@ public final class DisplayManagerService extends SystemService { mUiHandler = UiThread.getHandler(); mDisplayDeviceRepo = new DisplayDeviceRepository(mSyncRoot, mPersistentDataStore); mLogicalDisplayMapper = new LogicalDisplayMapper(mContext, mDisplayDeviceRepo, - new LogicalDisplayListener(), mSyncRoot, mHandler); + new LogicalDisplayListener(), mSyncRoot, mHandler, + new FoldSettingWrapper(mContext.getContentResolver())); mDisplayModeDirector = new DisplayModeDirector(context, mHandler); mBrightnessSynchronizer = new BrightnessSynchronizer(mContext); Resources resources = mContext.getResources(); diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java index d01b03f836a5..26f8029cf5ac 100644 --- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java +++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java @@ -42,6 +42,7 @@ import android.view.DisplayInfo; import com.android.internal.annotations.VisibleForTesting; import com.android.server.display.layout.DisplayIdProducer; import com.android.server.display.layout.Layout; +import com.android.server.utils.FoldSettingWrapper; import java.io.PrintWriter; import java.util.Arrays; @@ -142,6 +143,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { private final Listener mListener; private final DisplayManagerService.SyncRoot mSyncRoot; private final LogicalDisplayMapperHandler mHandler; + private final FoldSettingWrapper mFoldSettingWrapper; private final PowerManager mPowerManager; /** @@ -189,21 +191,23 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { LogicalDisplayMapper(@NonNull Context context, @NonNull DisplayDeviceRepository repo, @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot, - @NonNull Handler handler) { + @NonNull Handler handler, FoldSettingWrapper foldSettingWrapper) { this(context, repo, listener, syncRoot, handler, new DeviceStateToLayoutMap((isDefault) -> isDefault ? DEFAULT_DISPLAY - : sNextNonDefaultDisplayId++)); + : sNextNonDefaultDisplayId++), foldSettingWrapper); } LogicalDisplayMapper(@NonNull Context context, @NonNull DisplayDeviceRepository repo, @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot, - @NonNull Handler handler, @NonNull DeviceStateToLayoutMap deviceStateToLayoutMap) { + @NonNull Handler handler, @NonNull DeviceStateToLayoutMap deviceStateToLayoutMap, + FoldSettingWrapper foldSettingWrapper) { mSyncRoot = syncRoot; mPowerManager = context.getSystemService(PowerManager.class); mInteractive = mPowerManager.isInteractive(); mHandler = new LogicalDisplayMapperHandler(handler.getLooper()); mDisplayDeviceRepo = repo; mListener = listener; + mFoldSettingWrapper = foldSettingWrapper; mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false); mSupportsConcurrentInternalDisplays = context.getResources().getBoolean( com.android.internal.R.bool.config_supportsConcurrentInternalDisplays); @@ -531,9 +535,10 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { * Returns if the device should be put to sleep or not. * * Includes a check to verify that the device state that we are moving to, {@code pendingState}, - * is the same as the physical state of the device, {@code baseState}. Different values for - * these parameters indicate a device state override is active, and we shouldn't put the device - * to sleep to provide a better user experience. + * is the same as the physical state of the device, {@code baseState}. Also if the + * 'Stay Awake On Fold' is not enabled. Different values for these parameters indicate a device + * state override is active, and we shouldn't put the device to sleep to provide a better user + * experience. * * @param pendingState device state we are moving to * @param currentState device state we are currently in @@ -551,7 +556,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { && mDeviceStatesOnWhichToSleep.get(pendingState) && !mDeviceStatesOnWhichToSleep.get(currentState) && !isOverrideActive - && isInteractive && isBootCompleted; + && isInteractive && isBootCompleted && !mFoldSettingWrapper.shouldStayAwakeOnFold(); } private boolean areAllTransitioningDisplaysOffLocked() { diff --git a/services/core/java/com/android/server/utils/FoldSettingWrapper.java b/services/core/java/com/android/server/utils/FoldSettingWrapper.java new file mode 100644 index 000000000000..97a1ac06e24c --- /dev/null +++ b/services/core/java/com/android/server/utils/FoldSettingWrapper.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.utils; + +import android.content.ContentResolver; +import android.provider.Settings; + +/** + * A wrapper class for the {@link Settings.System#STAY_AWAKE_ON_FOLD} setting. + * + * This class provides a convenient way to access the {@link Settings.System#STAY_AWAKE_ON_FOLD} + * setting for testing. + */ +public class FoldSettingWrapper { + private final ContentResolver mContentResolver; + + public FoldSettingWrapper(ContentResolver contentResolver) { + mContentResolver = contentResolver; + } + + /** + * Returns whether the device should remain awake after folding. + */ + public boolean shouldStayAwakeOnFold() { + try { + return (Settings.System.getIntForUser( + mContentResolver, + Settings.System.STAY_AWAKE_ON_FOLD, + 0) == 1); + } catch (Settings.SettingNotFoundException e) { + return false; + } + } +} diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java index 1eec70da3d20..dc7f6800fc26 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java @@ -65,6 +65,7 @@ import androidx.test.filters.SmallTest; import com.android.server.display.layout.DisplayIdProducer; import com.android.server.display.layout.Layout; +import com.android.server.utils.FoldSettingWrapper; import org.junit.Before; import org.junit.Test; @@ -100,6 +101,7 @@ public class LogicalDisplayMapperTest { @Mock LogicalDisplayMapper.Listener mListenerMock; @Mock Context mContextMock; + @Mock FoldSettingWrapper mFoldSettingWrapperMock; @Mock Resources mResourcesMock; @Mock IPowerManager mIPowerManagerMock; @Mock IThermalService mIThermalServiceMock; @@ -139,6 +141,7 @@ public class LogicalDisplayMapperTest { when(mContextMock.getSystemServiceName(PowerManager.class)) .thenReturn(Context.POWER_SERVICE); + when(mFoldSettingWrapperMock.shouldStayAwakeOnFold()).thenReturn(false); when(mContextMock.getSystemService(PowerManager.class)).thenReturn(mPowerManager); when(mContextMock.getResources()).thenReturn(mResourcesMock); when(mResourcesMock.getBoolean( @@ -155,7 +158,7 @@ public class LogicalDisplayMapperTest { mHandler = new Handler(mLooper.getLooper()); mLogicalDisplayMapper = new LogicalDisplayMapper(mContextMock, mDisplayDeviceRepo, mListenerMock, new DisplayManagerService.SyncRoot(), mHandler, - mDeviceStateToLayoutMapSpy); + mDeviceStateToLayoutMapSpy, mFoldSettingWrapperMock); } @@ -570,6 +573,17 @@ public class LogicalDisplayMapperTest { /* isBootCompleted= */true)); } + @Test + public void testDeviceShouldNotSleepWhenFoldSettingTrue() { + when(mFoldSettingWrapperMock.shouldStayAwakeOnFold()).thenReturn(true); + + assertFalse(mLogicalDisplayMapper.shouldDeviceBePutToSleep(DEVICE_STATE_CLOSED, + DEVICE_STATE_OPEN, + /* isOverrideActive= */false, + /* isInteractive= */true, + /* isBootCompleted= */true)); + } + @Test public void testDeviceShouldNotBePutToSleep() { assertFalse(mLogicalDisplayMapper.shouldDeviceBePutToSleep(DEVICE_STATE_OPEN, @@ -978,4 +992,3 @@ public class LogicalDisplayMapperTest { } } } - -- cgit v1.2.3-59-g8ed1b