diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 75c9be966f816..f67a71a181bef 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -896,15 +896,21 @@ package android.app { method @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE) public void addOnProjectionStateChangedListener(int, @NonNull java.util.concurrent.Executor, @NonNull android.app.UiModeManager.OnProjectionStateChangedListener); method @RequiresPermission(android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED) public void enableCarMode(@IntRange(from=0) int, int); method @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE) public int getActiveProjectionTypes(); + method @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) public int getNightModeCustomType(); method @NonNull @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE) public java.util.Set getProjectingPackages(int); method @RequiresPermission(value=android.Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION, conditional=true) public boolean releaseProjection(int); method @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE) public void removeOnProjectionStateChangedListener(@NonNull android.app.UiModeManager.OnProjectionStateChangedListener); method @RequiresPermission(value=android.Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION, conditional=true) public boolean requestProjection(int); + method @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) public boolean setNightModeActivatedForCustomMode(int, boolean); + method @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) public void setNightModeCustomType(int); field public static final String ACTION_ENTER_CAR_MODE_PRIORITIZED = "android.app.action.ENTER_CAR_MODE_PRIORITIZED"; field public static final String ACTION_EXIT_CAR_MODE_PRIORITIZED = "android.app.action.EXIT_CAR_MODE_PRIORITIZED"; field public static final int DEFAULT_PRIORITY = 0; // 0x0 field public static final String EXTRA_CALLING_PACKAGE = "android.app.extra.CALLING_PACKAGE"; field public static final String EXTRA_PRIORITY = "android.app.extra.PRIORITY"; + field public static final int MODE_NIGHT_CUSTOM_TYPE_BEDTIME = 1; // 0x1 + field public static final int MODE_NIGHT_CUSTOM_TYPE_SCHEDULE = 0; // 0x0 + field public static final int MODE_NIGHT_CUSTOM_TYPE_UNKNOWN = -1; // 0xffffffff field public static final int PROJECTION_TYPE_ALL = -1; // 0xffffffff field public static final int PROJECTION_TYPE_AUTOMOTIVE = 1; // 0x1 field public static final int PROJECTION_TYPE_NONE = 0; // 0x0 diff --git a/core/java/android/app/IUiModeManager.aidl b/core/java/android/app/IUiModeManager.aidl index 440dd629c114c..55afed20c826f 100644 --- a/core/java/android/app/IUiModeManager.aidl +++ b/core/java/android/app/IUiModeManager.aidl @@ -48,19 +48,42 @@ interface IUiModeManager { /** * Sets the night mode. + *

* The mode can be one of: - * 1 - notnight mode - * 2 - night mode - * 3 - automatic mode switching + *

    notnight mode
+ *
    night mode
+ *
    custom schedule mode switching
*/ void setNightMode(int mode); /** - * Gets the currently configured night mode. Return 1 for notnight, - * 2 for night, and 3 for automatic mode switching. + * Gets the currently configured night mode. + *

+ * Returns + *

    notnight mode
+ *
    night mode
+ *
    custom schedule mode switching
*/ int getNightMode(); + /** + * Sets the current night mode to {@link #MODE_NIGHT_CUSTOM} with the custom night mode type + * {@code nightModeCustomType}. + * + * @param nightModeCustomType + * @hide + */ + void setNightModeCustomType(int nightModeCustomType); + + /** + * Returns the custom night mode type. + *

+ * If the current night mode is not {@link #MODE_NIGHT_CUSTOM}, returns + * {@link #MODE_NIGHT_CUSTOM_TYPE_UNKNOWN}. + * @hide + */ + int getNightModeCustomType(); + /** * Sets the dark mode for the given application. This setting is persisted and will override the * system configuration for this application. @@ -81,8 +104,21 @@ interface IUiModeManager { boolean isNightModeLocked(); /** - * [De]Activates night mode - */ + * [De]activating night mode for the current user if the current night mode is custom and the + * custom type matches {@code nightModeCustomType}. + * + * @param nightModeCustomType the specify type of custom mode + * @param active {@code true} to activate night mode. Otherwise, deactivate night mode + * @return {@code true} if night mode has successfully activated for the requested + * {@code nightModeCustomType}. + * @hide + */ + boolean setNightModeActivatedForCustomMode(int nightModeCustom, boolean active); + + /** + * [De]Activates night mode. + * @hide + */ boolean setNightModeActivated(boolean active); /** diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java index 973a8fb068d16..73a9e5a221c71 100644 --- a/core/java/android/app/UiModeManager.java +++ b/core/java/android/app/UiModeManager.java @@ -243,6 +243,45 @@ public class UiModeManager { */ public static final int MODE_NIGHT_YES = 2; + /** + * Granular types for {@link MODE_NIGHT_CUSTOM_TYPE_BEDTIME} + * @hide + */ + @IntDef(prefix = { "MODE_NIGHT_CUSTOM_TYPE_" }, value = { + MODE_NIGHT_CUSTOM_TYPE_UNKNOWN, + MODE_NIGHT_CUSTOM_TYPE_SCHEDULE, + MODE_NIGHT_CUSTOM_TYPE_BEDTIME, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface NightModeCustomType {} + + /** + * A granular type for {@link #MODE_NIGHT_CUSTOM} which is unknown. + *

+ * This is the default value when the night mode is set to value other than + * {@link #MODE_NIGHT_CUSTOM}. + * @hide + */ + @SystemApi + public static final int MODE_NIGHT_CUSTOM_TYPE_UNKNOWN = -1; + + /** + * A granular type for {@link #MODE_NIGHT_CUSTOM} which is based on a custom schedule. + *

+ * This is the default value when night mode is set to {@link #MODE_NIGHT_CUSTOM} unless the + * the night mode custom type is specified by calling {@link #setNightModeCustomType(int)}. + * @hide + */ + @SystemApi + public static final int MODE_NIGHT_CUSTOM_TYPE_SCHEDULE = 0; + + /** + * A granular type for {@link #MODE_NIGHT_CUSTOM} which is based on the bedtime schedule. + * @hide + */ + @SystemApi + public static final int MODE_NIGHT_CUSTOM_TYPE_BEDTIME = 1; + private IUiModeManager mService; /** @@ -495,6 +534,45 @@ public class UiModeManager { } } + /** + * Sets the current night mode to {@link #MODE_NIGHT_CUSTOM} with the custom night mode type + * {@code nightModeCustomType}. + * + * @param nightModeCustomType + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) + public void setNightModeCustomType(@NightModeCustomType int nightModeCustomType) { + if (mService != null) { + try { + mService.setNightModeCustomType(nightModeCustomType); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Returns the custom night mode type. + *

+ * If the current night mode is not {@link #MODE_NIGHT_CUSTOM}, returns + * {@link #MODE_NIGHT_CUSTOM_TYPE_UNKNOWN}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) + public int getNightModeCustomType() { + if (mService != null) { + try { + return mService.getNightModeCustomType(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return MODE_NIGHT_CUSTOM_TYPE_UNKNOWN; + } + /** * Sets and persist the night mode for this application. *

@@ -598,12 +676,37 @@ public class UiModeManager { return true; } + /** + * [De]activating night mode for the current user if the current night mode is custom and the + * custom type matches {@code nightModeCustomType}. + * + * @param nightModeCustomType the specify type of custom mode + * @param active {@code true} to activate night mode. Otherwise, deactivate night mode + * @return {@code true} if night mode has successfully activated for the requested + * {@code nightModeCustomType}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) + public boolean setNightModeActivatedForCustomMode(@NightModeCustomType int nightModeCustomType, + boolean active) { + if (mService != null) { + try { + return mService.setNightModeActivatedForCustomMode(nightModeCustomType, active); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return false; + } + /** * Activating night mode for the current user * * @return {@code true} if the change is successful * @hide */ + @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) public boolean setNightModeActivated(boolean active) { if (mService != null) { try { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 190b8f66949b3..e653d8f06cc42 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -8898,6 +8898,15 @@ public final class Settings { @Readable public static final String UI_NIGHT_MODE = "ui_night_mode"; + /** + * The current night mode custom type that has been selected by the user. Owned + * and controlled by UiModeManagerService. Constants are as per UiModeManager. + * @hide + */ + @Readable + @SuppressLint("NoSettingsProvider") + public static final String UI_NIGHT_MODE_CUSTOM_TYPE = "ui_night_mode_custom_type"; + /** * The current night mode that has been overridden to turn on by the system. Owned * and controlled by UiModeManagerService. Constants are as per diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index 81627a05c9a40..c236a7f800004 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -19,6 +19,9 @@ package com.android.server; import static android.app.UiModeManager.DEFAULT_PRIORITY; import static android.app.UiModeManager.MODE_NIGHT_AUTO; import static android.app.UiModeManager.MODE_NIGHT_CUSTOM; +import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME; +import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_SCHEDULE; +import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN; import static android.app.UiModeManager.MODE_NIGHT_NO; import static android.app.UiModeManager.MODE_NIGHT_YES; import static android.app.UiModeManager.PROJECTION_TYPE_AUTOMOTIVE; @@ -40,6 +43,7 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.app.StatusBarManager; import android.app.UiModeManager; +import android.app.UiModeManager.NightModeCustomType; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -115,6 +119,7 @@ final class UiModeManagerService extends SystemService { private int mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED; private int mNightMode = UiModeManager.MODE_NIGHT_NO; + private int mNightModeCustomType = UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN; private final LocalTime DEFAULT_CUSTOM_NIGHT_START_TIME = LocalTime.of(22, 0); private final LocalTime DEFAULT_CUSTOM_NIGHT_END_TIME = LocalTime.of(6, 0); private LocalTime mCustomAutoNightModeStartMilliseconds = DEFAULT_CUSTOM_NIGHT_START_TIME; @@ -136,6 +141,7 @@ final class UiModeManagerService extends SystemService { private boolean mWatch; private boolean mVrHeadset; private boolean mComputedNightMode; + private boolean mLastBedtimeRequestedNightMode = false; private int mCarModeEnableFlags; private boolean mSetupWizardComplete; @@ -541,7 +547,9 @@ final class UiModeManagerService extends SystemService { mNightMode = Secure.getIntForUser(context.getContentResolver(), Secure.UI_NIGHT_MODE, res.getInteger( com.android.internal.R.integer.config_defaultNightMode), userId); - mOverrideNightModeOn = Secure.getIntForUser(context.getContentResolver(), + mNightModeCustomType = Secure.getIntForUser(context.getContentResolver(), + Secure.UI_NIGHT_MODE_CUSTOM_TYPE, MODE_NIGHT_CUSTOM_TYPE_UNKNOWN, userId); + mOverrideNightModeOn = Secure.getIntForUser(context.getContentResolver(), Secure.UI_NIGHT_MODE_OVERRIDE_ON, 0, userId) != 0; mOverrideNightModeOff = Secure.getIntForUser(context.getContentResolver(), Secure.UI_NIGHT_MODE_OVERRIDE_OFF, 0, userId) != 0; @@ -702,6 +710,14 @@ final class UiModeManagerService extends SystemService { @Override public void setNightMode(int mode) { + // MODE_NIGHT_CUSTOM_TYPE_SCHEDULE is the default for MODE_NIGHT_CUSTOM. + int customModeType = mode == MODE_NIGHT_CUSTOM + ? MODE_NIGHT_CUSTOM_TYPE_SCHEDULE + : MODE_NIGHT_CUSTOM_TYPE_UNKNOWN; + setNightModeInternal(mode, customModeType); + } + + private void setNightModeInternal(int mode, int customModeType) { if (isNightModeLocked() && (getContext().checkCallingOrSelfPermission( android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) != PackageManager.PERMISSION_GRANTED)) { @@ -722,12 +738,14 @@ final class UiModeManagerService extends SystemService { final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { - if (mNightMode != mode) { + if (mNightMode != mode || mNightModeCustomType != customModeType) { if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) { unregisterScreenOffEventLocked(); cancelCustomAlarm(); } - + mNightModeCustomType = mode == MODE_NIGHT_CUSTOM + ? customModeType + : MODE_NIGHT_CUSTOM_TYPE_UNKNOWN; mNightMode = mode; resetNightModeOverrideLocked(); persistNightMode(user); @@ -753,6 +771,30 @@ final class UiModeManagerService extends SystemService { } } + @Override + public void setNightModeCustomType(@NightModeCustomType int nightModeCustomType) { + if (getContext().checkCallingOrSelfPermission( + android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException( + "setNightModeCustomType requires MODIFY_DAY_NIGHT_MODE permission"); + } + setNightModeInternal(MODE_NIGHT_CUSTOM, nightModeCustomType); + } + + @Override + public int getNightModeCustomType() { + if (getContext().checkCallingOrSelfPermission( + android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException( + "getNightModeCustomType requires MODIFY_DAY_NIGHT_MODE permission"); + } + synchronized (mLock) { + return mNightModeCustomType; + } + } + @Override public void setApplicationNightMode(@UiModeManager.NightMode int mode) { switch (mode) { @@ -807,11 +849,20 @@ final class UiModeManagerService extends SystemService { dumpImpl(pw); } + @Override + public boolean setNightModeActivatedForCustomMode(int modeNightCustomType, boolean active) { + return setNightModeActivatedForModeInternal(modeNightCustomType, active); + } + @Override public boolean setNightModeActivated(boolean active) { - if (isNightModeLocked() && (getContext().checkCallingOrSelfPermission( + return setNightModeActivatedForModeInternal(mNightModeCustomType, active); + } + + private boolean setNightModeActivatedForModeInternal(int modeCustomType, boolean active) { + if (getContext().checkCallingOrSelfPermission( android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) - != PackageManager.PERMISSION_GRANTED)) { + != PackageManager.PERMISSION_GRANTED) { Slog.e(TAG, "Night mode locked, requires MODIFY_DAY_NIGHT_MODE permission"); return false; } @@ -824,6 +875,14 @@ final class UiModeManagerService extends SystemService { return false; } + // Store the last requested bedtime night mode state so that we don't need to notify + // anyone if the user decides to switch to the night mode to bedtime. + if (modeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME) { + mLastBedtimeRequestedNightMode = active; + } + if (modeCustomType != mNightModeCustomType) { + return false; + } synchronized (mLock) { final long ident = Binder.clearCallingIdentity(); try { @@ -1421,6 +1480,8 @@ final class UiModeManagerService extends SystemService { if (mCarModeEnabled || mCar) return; Secure.putIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE, mNightMode, user); + Secure.putLongForUser(getContext().getContentResolver(), + Secure.UI_NIGHT_MODE_CUSTOM_TYPE, mNightModeCustomType, user); Secure.putLongForUser(getContext().getContentResolver(), Secure.DARK_THEME_CUSTOM_START_TIME, mCustomAutoNightModeStartMilliseconds.toNanoOfDay() / 1000, user); @@ -1473,10 +1534,14 @@ final class UiModeManagerService extends SystemService { } if (mNightMode == MODE_NIGHT_CUSTOM) { - registerTimeChangeEvent(); - final boolean activate = computeCustomNightMode(); - updateComputedNightModeLocked(activate); - scheduleNextCustomTimeListener(); + if (mNightModeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME) { + updateComputedNightModeLocked(mLastBedtimeRequestedNightMode); + } else { + registerTimeChangeEvent(); + final boolean activate = computeCustomNightMode(); + updateComputedNightModeLocked(activate); + scheduleNextCustomTimeListener(); + } } else { unregisterTimeChangeEvent(); } @@ -1494,6 +1559,7 @@ final class UiModeManagerService extends SystemService { "updateConfigurationLocked: mDockState=" + mDockState + "; mCarMode=" + mCarModeEnabled + "; mNightMode=" + mNightMode + + "; mNightModeCustomType=" + mNightModeCustomType + "; uiMode=" + uiMode); } @@ -1534,7 +1600,8 @@ final class UiModeManagerService extends SystemService { } private boolean shouldApplyAutomaticChangesImmediately() { - return mCar || !mPowerManager.isInteractive(); + return mCar || !mPowerManager.isInteractive() + || mNightModeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME; } private void scheduleNextCustomTimeListener() { diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java index f21991defbec6..a12bc3b4c59ff 100644 --- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java @@ -16,14 +16,20 @@ package com.android.server; +import static android.Manifest.permission.MODIFY_DAY_NIGHT_MODE; import static android.app.UiModeManager.MODE_NIGHT_AUTO; import static android.app.UiModeManager.MODE_NIGHT_CUSTOM; +import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME; +import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_SCHEDULE; +import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN; import static android.app.UiModeManager.MODE_NIGHT_NO; import static android.app.UiModeManager.MODE_NIGHT_YES; import static android.app.UiModeManager.PROJECTION_TYPE_ALL; import static android.app.UiModeManager.PROJECTION_TYPE_AUTOMOTIVE; import static android.app.UiModeManager.PROJECTION_TYPE_NONE; +import static com.google.common.truth.Truth.assertThat; + import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertTrue; @@ -194,7 +200,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase { @Ignore // b/152719290 - Fails on stage-aosp-master @Test - public void setNightMoveActivated_overridesFunctionCorrectly() throws RemoteException { + public void setNightModeActivated_overridesFunctionCorrectly() throws RemoteException { // set up when(mPowerManager.isInteractive()).thenReturn(false); mService.setNightMode(MODE_NIGHT_NO); @@ -224,6 +230,29 @@ public class UiModeManagerServiceTest extends UiServiceTestCase { assertFalse(mUiManagerService.getConfiguration().isNightModeActive()); } + @Test + public void setNightModeActivated_true_withCustomModeBedtime_shouldOverrideNightModeCorrectly() + throws RemoteException { + mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + assertFalse(mUiManagerService.getConfiguration().isNightModeActive()); + + mService.setNightModeActivated(true); + + assertThat(mUiManagerService.getConfiguration().isNightModeActive()).isTrue(); + } + + @Test + public void setNightModeActivated_false_withCustomModeBedtime_shouldOverrideNightModeCorrectly() + throws RemoteException { + mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + assertFalse(mUiManagerService.getConfiguration().isNightModeActive()); + + mService.setNightModeActivated(true); + mService.setNightModeActivated(false); + + assertThat(mUiManagerService.getConfiguration().isNightModeActive()).isFalse(); + } + @Test public void setAutoMode_screenOffRegistered() throws RemoteException { try { @@ -247,7 +276,44 @@ public class UiModeManagerServiceTest extends UiServiceTestCase { } @Test - public void setNightModeActivated_fromNoToYesAndBAck() throws RemoteException { + public void setNightModeCustomType_bedtime_shouldNotActivateNightMode() throws RemoteException { + try { + mService.setNightMode(MODE_NIGHT_NO); + } catch (SecurityException e) { /* we should ignore this update config exception*/ } + mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + + assertThat(isNightModeActivated()).isFalse(); + } + + @Test + public void setNightModeCustomType_noPermission_shouldThrow() throws RemoteException { + when(mContext.checkCallingOrSelfPermission(eq(MODIFY_DAY_NIGHT_MODE))) + .thenReturn(PackageManager.PERMISSION_DENIED); + + assertThrows(SecurityException.class, + () -> mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME)); + } + + @Test + public void setNightModeCustomType_bedtime_shouldHaveNoScreenOffRegistered() + throws RemoteException { + try { + mService.setNightMode(MODE_NIGHT_NO); + } catch (SecurityException e) { /* we should ignore this update config exception*/ } + mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + ArgumentCaptor intentFiltersCaptor = ArgumentCaptor.forClass( + IntentFilter.class); + verify(mContext, atLeastOnce()).registerReceiver(any(BroadcastReceiver.class), + intentFiltersCaptor.capture()); + + List intentFilters = intentFiltersCaptor.getAllValues(); + for (IntentFilter intentFilter : intentFilters) { + assertThat(intentFilter.hasAction(Intent.ACTION_SCREEN_OFF)).isFalse(); + } + } + + @Test + public void setNightModeActivated_fromNoToYesAndBack() throws RemoteException { mService.setNightMode(MODE_NIGHT_NO); mService.setNightModeActivated(true); assertTrue(isNightModeActivated()); @@ -256,7 +322,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase { } @Test - public void setNightModeActivated_permissiontoChangeOtherUsers() throws RemoteException { + public void setNightModeActivated_permissionToChangeOtherUsers() throws RemoteException { SystemService.TargetUser user = mock(SystemService.TargetUser.class); doReturn(9).when(user).getUserIdentifier(); mUiManagerService.onUserSwitching(user, user); @@ -266,6 +332,89 @@ public class UiModeManagerServiceTest extends UiServiceTestCase { assertFalse(mService.setNightModeActivated(true)); } + @Test + public void setNightModeActivatedForCustomMode_customTypeBedtime_withParamOnAndBedtime_shouldActivate() + throws RemoteException { + mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + mService.setNightModeActivatedForCustomMode( + MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */); + + assertThat(isNightModeActivated()).isTrue(); + } + + @Test + public void setNightModeActivatedForCustomMode_customTypeBedtime_withParamOffAndBedtime_shouldDeactivate() + throws RemoteException { + mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + mService.setNightModeActivatedForCustomMode( + MODE_NIGHT_CUSTOM_TYPE_BEDTIME, false /* active */); + + assertThat(isNightModeActivated()).isFalse(); + } + + @Test + public void setNightModeActivatedForCustomMode_customTypeBedtime_withParamOnAndSchedule_shouldNotActivate() + throws RemoteException { + mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + mService.setNightModeActivatedForCustomMode( + MODE_NIGHT_CUSTOM_TYPE_SCHEDULE, true /* active */); + + assertThat(isNightModeActivated()).isFalse(); + } + + @Test + public void setNightModeActivatedForCustomMode_customTypeSchedule_withParamOnAndBedtime_shouldNotActivate() + throws RemoteException { + mService.setNightMode(MODE_NIGHT_CUSTOM); + mService.setNightModeActivatedForCustomMode( + MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */); + + assertThat(isNightModeActivated()).isFalse(); + } + + @Test + public void setNightModeActivatedForCustomMode_customTypeSchedule_withParamOnAndBedtime_thenCustomTypeBedtime_shouldActivate() + throws RemoteException { + mService.setNightMode(MODE_NIGHT_CUSTOM); + mService.setNightModeActivatedForCustomMode( + MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */); + + mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + + assertThat(isNightModeActivated()).isTrue(); + } + + @Test + public void setNightModeActivatedForCustomMode_customTypeBedtime_withParamOnAndBedtime_thenCustomTypeSchedule_shouldKeepNightModeActivate() + throws RemoteException { + mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + mService.setNightModeActivatedForCustomMode( + MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */); + + mService.setNightMode(MODE_NIGHT_CUSTOM); + LocalTime now = LocalTime.now(); + mService.setCustomNightModeStart(now.plusHours(1L).toNanoOfDay() / 1000); + mService.setCustomNightModeEnd(now.plusHours(2L).toNanoOfDay() / 1000); + + assertThat(isNightModeActivated()).isTrue(); + } + + @Test + public void setNightModeActivatedForCustomMode_customTypeBedtime_withParamOnAndBedtime_thenCustomTypeScheduleAndScreenOff_shouldDeactivateNightMode() + throws RemoteException { + mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + mService.setNightModeActivatedForCustomMode( + MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */); + + mService.setNightMode(MODE_NIGHT_CUSTOM); + LocalTime now = LocalTime.now(); + mService.setCustomNightModeStart(now.plusHours(1L).toNanoOfDay() / 1000); + mService.setCustomNightModeEnd(now.plusHours(2L).toNanoOfDay() / 1000); + mScreenOffCallback.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF)); + + assertThat(isNightModeActivated()).isFalse(); + } + @Test public void autoNightModeSwitch_batterySaverOn() throws RemoteException { mService.setNightMode(MODE_NIGHT_NO); @@ -282,6 +431,191 @@ public class UiModeManagerServiceTest extends UiServiceTestCase { assertTrue(isNightModeActivated()); } + @Test + public void nightModeCustomBedtime_batterySaverOn_notInBedtime_shouldActivateNightMode() + throws RemoteException { + mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + + mPowerSaveConsumer.accept( + new PowerSaveState.Builder().setBatterySaverEnabled(true).build()); + + assertThat(isNightModeActivated()).isTrue(); + } + + @Test + public void nightModeCustomBedtime_batterySaverOn_afterBedtime_shouldKeepNightModeActivated() + throws RemoteException { + mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + mPowerSaveConsumer.accept( + new PowerSaveState.Builder().setBatterySaverEnabled(true).build()); + + mService.setNightModeActivatedForCustomMode( + MODE_NIGHT_CUSTOM_TYPE_BEDTIME, false /* active */); + + assertThat(isNightModeActivated()).isTrue(); + } + + @Test + public void nightModeBedtime_duringBedtime_batterySaverOnThenOff_shouldKeepNightModeActivated() + throws RemoteException { + mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + mService.setNightModeActivatedForCustomMode( + MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */); + + mPowerSaveConsumer.accept( + new PowerSaveState.Builder().setBatterySaverEnabled(true).build()); + mPowerSaveConsumer.accept( + new PowerSaveState.Builder().setBatterySaverEnabled(false).build()); + + assertThat(isNightModeActivated()).isTrue(); + } + + @Test + public void nightModeCustomBedtime_duringBedtime_batterySaverOnThenOff_finallyAfterBedtime_shouldDeactivateNightMode() + throws RemoteException { + mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + mService.setNightModeActivatedForCustomMode( + MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */); + mPowerSaveConsumer.accept( + new PowerSaveState.Builder().setBatterySaverEnabled(true).build()); + mPowerSaveConsumer.accept( + new PowerSaveState.Builder().setBatterySaverEnabled(false).build()); + + mService.setNightModeActivatedForCustomMode( + MODE_NIGHT_CUSTOM_TYPE_BEDTIME, false /* active */); + + assertThat(isNightModeActivated()).isFalse(); + } + + @Test + public void nightModeCustomBedtime_duringBedtime_changeModeToNo_shouldDeactivateNightMode() + throws RemoteException { + mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + mService.setNightModeActivatedForCustomMode( + MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */); + + mService.setNightMode(MODE_NIGHT_NO); + + assertThat(isNightModeActivated()).isFalse(); + } + + @Test + public void nightModeCustomBedtime_duringBedtime_changeModeToNoAndThenExitBedtime_shouldKeepNightModeDeactivated() + throws RemoteException { + mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + mService.setNightModeActivatedForCustomMode( + MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */); + mService.setNightMode(MODE_NIGHT_NO); + + mService.setNightModeActivatedForCustomMode( + MODE_NIGHT_CUSTOM_TYPE_BEDTIME, false /* active */); + + assertThat(isNightModeActivated()).isFalse(); + } + + @Test + public void nightModeCustomBedtime_duringBedtime_changeModeToYes_shouldKeepNightModeActivated() + throws RemoteException { + mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + mService.setNightModeActivatedForCustomMode( + MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */); + + mService.setNightMode(MODE_NIGHT_YES); + + assertThat(isNightModeActivated()).isTrue(); + } + + @Test + public void nightModeCustomBedtime_duringBedtime_changeModeToYesAndThenExitBedtime_shouldKeepNightModeActivated() + throws RemoteException { + mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + mService.setNightModeActivatedForCustomMode( + MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */); + + mService.setNightMode(MODE_NIGHT_YES); + mService.setNightModeActivatedForCustomMode( + MODE_NIGHT_CUSTOM_TYPE_BEDTIME, false /* active */); + + assertThat(isNightModeActivated()).isTrue(); + } + + @Test + public void nightModeNo_duringBedtime_shouldKeepNightModeDeactivated() + throws RemoteException { + mService.setNightMode(MODE_NIGHT_NO); + + mService.setNightModeActivatedForCustomMode( + MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */); + + assertThat(isNightModeActivated()).isFalse(); + } + + @Test + public void nightModeNo_thenChangeToCustomTypeBedtimeAndActivate_shouldActivateNightMode() + throws RemoteException { + mService.setNightMode(MODE_NIGHT_NO); + + mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + mService.setNightModeActivatedForCustomMode( + MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */); + + assertThat(isNightModeActivated()).isTrue(); + } + + @Test + public void nightModeYes_thenChangeToCustomTypeBedtime_shouldDeactivateNightMode() + throws RemoteException { + mService.setNightMode(MODE_NIGHT_YES); + + mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + + assertThat(isNightModeActivated()).isFalse(); + } + + @Test + public void nightModeYes_thenChangeToCustomTypeBedtimeAndActivate_shouldActivateNightMode() + throws RemoteException { + mService.setNightMode(MODE_NIGHT_YES); + + mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + mService.setNightModeActivatedForCustomMode( + MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */); + + assertThat(isNightModeActivated()).isTrue(); + } + + @Test + public void nightModeAuto_thenChangeToCustomTypeBedtime_notInBedtime_shouldDeactivateNightMode() + throws RemoteException { + // set mode to auto + mService.setNightMode(MODE_NIGHT_AUTO); + mService.setNightModeActivated(true); + // now it is night time + doReturn(true).when(mTwilightState).isNight(); + mTwilightListener.onTwilightStateChanged(mTwilightState); + + mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + + assertThat(isNightModeActivated()).isFalse(); + } + + @Test + public void nightModeAuto_thenChangeToCustomTypeBedtime_duringBedtime_shouldActivateNightMode() + throws RemoteException { + // set mode to auto + mService.setNightMode(MODE_NIGHT_AUTO); + mService.setNightModeActivated(true); + // now it is night time + doReturn(true).when(mTwilightState).isNight(); + mTwilightListener.onTwilightStateChanged(mTwilightState); + + mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + mService.setNightModeActivatedForCustomMode( + MODE_NIGHT_CUSTOM_TYPE_BEDTIME, true /* active */); + + assertThat(isNightModeActivated()).isTrue(); + } + @Test public void setAutoMode_clearCache() throws RemoteException { try { @@ -326,6 +660,62 @@ public class UiModeManagerServiceTest extends UiServiceTestCase { assertEquals(MODE_NIGHT_AUTO, mService.getNightMode()); } + @Test + public void getNightModeCustomType_nightModeNo_shouldReturnUnknown() throws RemoteException { + try { + mService.setNightMode(MODE_NIGHT_NO); + } catch (SecurityException e) { /* we should ignore this update config exception*/ } + + assertThat(mService.getNightModeCustomType()).isEqualTo(MODE_NIGHT_CUSTOM_TYPE_UNKNOWN); + } + + @Test + public void getNightModeCustomType_nightModeYes_shouldReturnUnknown() throws RemoteException { + try { + mService.setNightMode(MODE_NIGHT_YES); + } catch (SecurityException e) { /* we should ignore this update config exception*/ } + + assertThat(mService.getNightModeCustomType()).isEqualTo(MODE_NIGHT_CUSTOM_TYPE_UNKNOWN); + } + + @Test + public void getNightModeCustomType_nightModeAuto_shouldReturnUnknown() throws RemoteException { + try { + mService.setNightMode(MODE_NIGHT_AUTO); + } catch (SecurityException e) { /* we should ignore this update config exception*/ } + + assertThat(mService.getNightModeCustomType()).isEqualTo(MODE_NIGHT_CUSTOM_TYPE_UNKNOWN); + } + + @Test + public void getNightModeCustomType_nightModeCustom_shouldReturnSchedule() + throws RemoteException { + try { + mService.setNightMode(MODE_NIGHT_CUSTOM); + } catch (SecurityException e) { /* we should ignore this update config exception*/ } + + assertThat(mService.getNightModeCustomType()).isEqualTo(MODE_NIGHT_CUSTOM_TYPE_SCHEDULE); + } + + @Test + public void getNightModeCustomType_nightModeCustomBedtime_shouldReturnBedtime() + throws RemoteException { + try { + mService.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + } catch (SecurityException e) { /* we should ignore this update config exception*/ } + + assertThat(mService.getNightModeCustomType()).isEqualTo(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + } + + @Test + public void getNightModeCustomType_permissionNotGranted_shouldThrow() + throws RemoteException { + when(mContext.checkCallingOrSelfPermission(eq(MODIFY_DAY_NIGHT_MODE))) + .thenReturn(PackageManager.PERMISSION_DENIED); + + assertThrows(SecurityException.class, () -> mService.getNightModeCustomType()); + } + @Test public void isNightModeActive_nightModeYes() throws RemoteException { try {