diff options
5 files changed, 690 insertions, 40 deletions
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index f215ae7ce405..024738a50d81 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6789,7 +6789,8 @@ public final class Settings { * Represented as milliseconds from midnight (e.g. 79200000 == 10pm). * @hide */ - public static final String NIGHT_DISPLAY_CUSTOM_START_TIME = "night_display_custom_start_time"; + public static final String NIGHT_DISPLAY_CUSTOM_START_TIME = + "night_display_custom_start_time"; /** * Custom time when Night display is scheduled to deactivate. @@ -6799,6 +6800,14 @@ public final class Settings { public static final String NIGHT_DISPLAY_CUSTOM_END_TIME = "night_display_custom_end_time"; /** + * Time in milliseconds (since epoch) when Night display was last activated. Use to decide + * whether to apply the current activated state after a reboot or user change. + * @hide + */ + public static final String NIGHT_DISPLAY_LAST_ACTIVATED_TIME = + "night_display_last_activated_time"; + + /** * Names of the service components that the current user has explicitly allowed to * be a VR mode listener, separated by ':'. * @@ -7024,6 +7033,7 @@ public final class Settings { NIGHT_DISPLAY_CUSTOM_END_TIME, NIGHT_DISPLAY_COLOR_TEMPERATURE, NIGHT_DISPLAY_AUTO_MODE, + NIGHT_DISPLAY_LAST_ACTIVATED_TIME, NIGHT_DISPLAY_ACTIVATED, SYNC_PARENT_SOUNDS, CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED, diff --git a/services/core/java/com/android/server/display/NightDisplayService.java b/services/core/java/com/android/server/display/NightDisplayService.java index d1275bb2224a..78498967b344 100644 --- a/services/core/java/com/android/server/display/NightDisplayService.java +++ b/services/core/java/com/android/server/display/NightDisplayService.java @@ -47,7 +47,6 @@ import com.android.server.SystemService; import com.android.server.twilight.TwilightListener; import com.android.server.twilight.TwilightManager; import com.android.server.twilight.TwilightState; -import com.android.server.vr.VrManagerService; import java.util.concurrent.atomic.AtomicBoolean; import java.util.Calendar; @@ -62,7 +61,6 @@ public final class NightDisplayService extends SystemService implements NightDisplayController.Callback { private static final String TAG = "NightDisplayService"; - private static final boolean DEBUG = false; /** * The transition time, in milliseconds, for Night Display to turn on/off. @@ -151,8 +149,9 @@ public final class NightDisplayService extends SystemService @Override public void onBootPhase(int phase) { - if (phase == PHASE_SYSTEM_SERVICES_READY) { - IVrManager vrManager = (IVrManager) getBinderService(Context.VR_SERVICE); + if (phase >= PHASE_SYSTEM_SERVICES_READY) { + final IVrManager vrManager = IVrManager.Stub.asInterface( + getBinderService(Context.VR_SERVICE)); if (vrManager != null) { try { vrManager.registerListener(mVrStateCallbacks); @@ -160,7 +159,9 @@ public final class NightDisplayService extends SystemService Slog.e(TAG, "Failed to register VR mode state listener: " + e); } } - } else if (phase == PHASE_BOOT_COMPLETED) { + } + + if (phase >= PHASE_BOOT_COMPLETED) { mBootCompleted = true; // Register listeners now that boot is complete. @@ -284,12 +285,18 @@ public final class NightDisplayService extends SystemService if (mIsActivated == null || mIsActivated != activated) { Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display"); - if (mAutoMode != null) { - mAutoMode.onActivated(activated); + if (mIsActivated != null) { + Secure.putLongForUser(getContext().getContentResolver(), + Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, System.currentTimeMillis(), + mCurrentUser); } mIsActivated = activated; + if (mAutoMode != null) { + mAutoMode.onActivated(activated); + } + applyTint(false); } } @@ -401,7 +408,7 @@ public final class NightDisplayService extends SystemService * Set the color transformation {@code MATRIX_NIGHT} to the given color temperature. * * @param colorTemperature color temperature in Kelvin - * @param outTemp the 4x4 display transformation matrix for that color temperature + * @param outTemp the 4x4 display transformation matrix for that color temperature */ private void setMatrix(int colorTemperature, float[] outTemp) { if (outTemp.length != 16) { @@ -423,8 +430,22 @@ public final class NightDisplayService extends SystemService outTemp[10] = blue; } + private Calendar getLastActivatedTime() { + final ContentResolver cr = getContext().getContentResolver(); + final long lastActivatedTimeMillis = Secure.getLongForUser( + cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, -1, mCurrentUser); + if (lastActivatedTimeMillis < 0) { + return null; + } + + final Calendar lastActivatedTime = Calendar.getInstance(); + lastActivatedTime.setTimeInMillis(lastActivatedTimeMillis); + return lastActivatedTime; + } + private abstract class AutoMode implements NightDisplayController.Callback { public abstract void onStart(); + public abstract void onStop(); } @@ -438,7 +459,7 @@ public final class NightDisplayService extends SystemService private Calendar mLastActivatedTime; - public CustomAutoMode() { + CustomAutoMode() { mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE); mTimeChangedReceiver = new BroadcastReceiver() { @Override @@ -452,10 +473,10 @@ public final class NightDisplayService extends SystemService final Calendar now = Calendar.getInstance(); final Calendar startTime = mStartTime.getDateTimeBefore(now); final Calendar endTime = mEndTime.getDateTimeAfter(startTime); - final boolean activated = now.before(endTime); - boolean setActivated = mIsActivated == null || mLastActivatedTime == null; - if (!setActivated && mIsActivated != activated) { + boolean activate = now.before(endTime); + if (mLastActivatedTime != null) { + // Convert mLastActivatedTime to the current timezone if needed. final TimeZone currentTimeZone = now.getTimeZone(); if (!currentTimeZone.equals(mLastActivatedTime.getTimeZone())) { final int year = mLastActivatedTime.get(Calendar.YEAR); @@ -470,17 +491,16 @@ public final class NightDisplayService extends SystemService mLastActivatedTime.set(Calendar.MINUTE, minute); } - if (mIsActivated) { - setActivated = now.before(mStartTime.getDateTimeBefore(mLastActivatedTime)) - || now.after(mEndTime.getDateTimeAfter(mLastActivatedTime)); - } else { - setActivated = now.before(mEndTime.getDateTimeBefore(mLastActivatedTime)) - || now.after(mStartTime.getDateTimeAfter(mLastActivatedTime)); + // Maintain the existing activated state if within the current period. + if (mLastActivatedTime.before(now) + && mLastActivatedTime.after(startTime) + && (mLastActivatedTime.after(endTime) || now.before(endTime))) { + activate = mController.isActivated(); } } - if (setActivated) { - mController.setActivated(activated); + if (mIsActivated == null || mIsActivated != activate) { + mController.setActivated(activate); } updateNextAlarm(mIsActivated, now); } @@ -502,6 +522,8 @@ public final class NightDisplayService extends SystemService mStartTime = mController.getCustomStartTime(); mEndTime = mController.getCustomEndTime(); + mLastActivatedTime = getLastActivatedTime(); + // Force an update to initialize state. updateActivated(); } @@ -516,11 +538,8 @@ public final class NightDisplayService extends SystemService @Override public void onActivated(boolean activated) { - final Calendar now = Calendar.getInstance(); - if (mIsActivated != null) { - mLastActivatedTime = now; - } - updateNextAlarm(activated, now); + mLastActivatedTime = getLastActivatedTime(); + updateNextAlarm(activated, Calendar.getInstance()); } @Override @@ -550,33 +569,33 @@ public final class NightDisplayService extends SystemService private Calendar mLastActivatedTime; - public TwilightAutoMode() { + TwilightAutoMode() { mTwilightManager = getLocalService(TwilightManager.class); } private void updateActivated(TwilightState state) { - final boolean isNight = state != null && state.isNight(); - boolean setActivated = mIsActivated == null || mIsActivated != isNight; - if (setActivated && state != null && mLastActivatedTime != null) { + boolean activate = state != null && state.isNight(); + if (state != null && mLastActivatedTime != null) { + final Calendar now = Calendar.getInstance(); final Calendar sunrise = state.sunrise(); final Calendar sunset = state.sunset(); - if (sunrise.before(sunset)) { - setActivated = mLastActivatedTime.before(sunrise) - || mLastActivatedTime.after(sunset); - } else { - setActivated = mLastActivatedTime.before(sunset) - || mLastActivatedTime.after(sunrise); + + // Maintain the existing activated state if within the current period. + if (mLastActivatedTime.before(now) + && (mLastActivatedTime.after(sunrise) ^ mLastActivatedTime.after(sunset))) { + activate = mController.isActivated(); } } - if (setActivated) { - mController.setActivated(isNight); + if (mIsActivated == null || mIsActivated != activate) { + mController.setActivated(activate); } } @Override public void onStart() { mTwilightManager.registerListener(this, mHandler); + mLastActivatedTime = getLastActivatedTime(); // Force an update to initialize state. updateActivated(mTwilightManager.getLastTwilightState()); @@ -591,7 +610,7 @@ public final class NightDisplayService extends SystemService @Override public void onActivated(boolean activated) { if (mIsActivated != null) { - mLastActivatedTime = Calendar.getInstance(); + mLastActivatedTime = getLastActivatedTime(); } } diff --git a/services/core/java/com/android/server/twilight/TwilightState.java b/services/core/java/com/android/server/twilight/TwilightState.java index a12965df11c0..30a8cccb6ad5 100644 --- a/services/core/java/com/android/server/twilight/TwilightState.java +++ b/services/core/java/com/android/server/twilight/TwilightState.java @@ -31,7 +31,7 @@ public final class TwilightState { private final long mSunriseTimeMillis; private final long mSunsetTimeMillis; - TwilightState(long sunriseTimeMillis, long sunsetTimeMillis) { + public TwilightState(long sunriseTimeMillis, long sunsetTimeMillis) { mSunriseTimeMillis = sunriseTimeMillis; mSunsetTimeMillis = sunsetTimeMillis; } diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index cc682c4ee985..fa72416edd16 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -18,6 +18,7 @@ package="com.android.frameworks.servicestests"> <uses-permission android:name="android.permission.READ_LOGS" /> + <uses-permission android:name="android.permission.ACCESS_VR_MANAGER" /> <uses-permission android:name="android.permission.ACCOUNT_MANAGER" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> diff --git a/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java new file mode 100644 index 000000000000..9a9c243023eb --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java @@ -0,0 +1,620 @@ +/* + * Copyright (C) 2017 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; + +import android.app.ActivityManager; +import android.app.AlarmManager; +import android.content.Context; +import android.content.ContextWrapper; +import android.os.UserHandle; +import android.provider.Settings; +import android.provider.Settings.Secure; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; +import android.test.mock.MockContentResolver; + +import com.android.internal.app.NightDisplayController; +import com.android.internal.app.NightDisplayController.LocalTime; +import com.android.internal.util.test.FakeSettingsProvider; +import com.android.server.display.DisplayTransformManager; +import com.android.server.display.NightDisplayService; +import com.android.server.twilight.TwilightManager; +import com.android.server.twilight.TwilightState; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; + +import java.util.Calendar; + +import static com.google.common.truth.Truth.assertWithMessage; +import static org.mockito.Mockito.doReturn; + +@RunWith(AndroidJUnit4.class) +public class NightDisplayServiceTest { + + private Context mContext; + private int mUserId; + + private TwilightManager mTwilightManager; + + private NightDisplayController mNightDisplayController; + private NightDisplayService mNightDisplayService; + + @Before + public void setUp() { + mContext = Mockito.spy(new ContextWrapper(InstrumentationRegistry.getTargetContext())); + mUserId = ActivityManager.getCurrentUser(); + + doReturn(mContext).when(mContext).getApplicationContext(); + + final MockContentResolver cr = new MockContentResolver(mContext); + cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + doReturn(cr).when(mContext).getContentResolver(); + + final AlarmManager am = Mockito.mock(AlarmManager.class); + doReturn(am).when(mContext).getSystemService(Context.ALARM_SERVICE); + + final DisplayTransformManager dtm = Mockito.mock(DisplayTransformManager.class); + LocalServices.addService(DisplayTransformManager.class, dtm); + + mTwilightManager = Mockito.mock(TwilightManager.class); + LocalServices.addService(TwilightManager.class, mTwilightManager); + + mNightDisplayController = new NightDisplayController(mContext, mUserId); + mNightDisplayService = new NightDisplayService(mContext); + } + + @After + public void tearDown() { + LocalServices.removeServiceForTest(DisplayTransformManager.class); + LocalServices.removeServiceForTest(TwilightManager.class); + + mNightDisplayService = null; + mNightDisplayController = null; + + mTwilightManager = null; + + mUserId = UserHandle.USER_NULL; + mContext = null; + } + + @Test + public void customSchedule_whenStartedAfterNight_ifOffAfterNight_turnsOff() { + setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */); + setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedAfterNight_ifOffBeforeNight_turnsOff() { + setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */); + setActivated(false /* activated */, -180 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedAfterNight_ifOffDuringNight_turnsOff() { + setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */); + setActivated(false /* activated */, -90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedAfterNight_ifOffInFuture_turnsOff() { + setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */); + setActivated(false /* activated */, 30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedAfterNight_ifOnAfterNight_turnsOn() { + setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */); + setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void customSchedule_whenStartedAfterNight_ifOnBeforeNight_turnsOff() { + setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */); + setActivated(true /* activated */, -180 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedAfterNight_ifOnDuringNight_turnsOff() { + setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */); + setActivated(true /* activated */, -90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedAfterNight_ifOnInFuture_turnsOff() { + setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */); + setActivated(true /* activated */, 30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedBeforeNight_ifOffAfterNight_turnsOff() { + setAutoModeCustom(60 /* startTimeOffset */, 120 /* endTimeOffset */); + setActivated(false /* activated */, 180 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedBeforeNight_ifOffBeforeNight_turnsOff() { + setAutoModeCustom(60 /* startTimeOffset */, 120 /* endTimeOffset */); + setActivated(false /* activated */, 30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedBeforeNight_ifOffDuringNight_turnsOff() { + setAutoModeCustom(60 /* startTimeOffset */, 120 /* endTimeOffset */); + setActivated(false /* activated */, 90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedBeforeNight_ifOffInPast_turnsOff() { + setAutoModeCustom(60 /* startTimeOffset */, 120 /* endTimeOffset */); + setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedBeforeNight_ifOnAfterNight_turnsOff() { + setAutoModeCustom(60 /* startTimeOffset */, 120 /* endTimeOffset */); + setActivated(true /* activated */, 180 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedBeforeNight_ifOnBeforeNight_turnsOff() { + setAutoModeCustom(60 /* startTimeOffset */, 120 /* endTimeOffset */); + setActivated(true /* activated */, 30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedBeforeNight_ifOnDuringNight_turnsOff() { + setAutoModeCustom(60 /* startTimeOffset */, 120 /* endTimeOffset */); + setActivated(true /* activated */, 90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedBeforeNight_ifOnInPast_turnsOn() { + setAutoModeCustom(60 /* startTimeOffset */, 120 /* endTimeOffset */); + setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void customSchedule_whenStartedDuringNight_ifOffAfterNight_turnsOn() { + setAutoModeCustom(-60 /* startTimeOffset */, 60 /* endTimeOffset */); + setActivated(false /* activated */, 90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void customSchedule_whenStartedDuringNight_ifOffBeforeNight_turnsOn() { + setAutoModeCustom(-60 /* startTimeOffset */, 60 /* endTimeOffset */); + setActivated(false /* activated */, -90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void customSchedule_whenStartedDuringNight_ifOffDuringNightInFuture_turnsOn() { + setAutoModeCustom(-60 /* startTimeOffset */, 60 /* endTimeOffset */); + setActivated(false /* activated */, 30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void customSchedule_whenStartedDuringNight_ifOffDuringNightInPast_turnsOff() { + setAutoModeCustom(-60 /* startTimeOffset */, 60 /* endTimeOffset */); + setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void customSchedule_whenStartedDuringNight_ifOnAfterNight_turnsOn() { + setAutoModeCustom(-60 /* startTimeOffset */, 60 /* endTimeOffset */); + setActivated(true /* activated */, 90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void customSchedule_whenStartedDuringNight_ifOnBeforeNight_turnsOn() { + setAutoModeCustom(-60 /* startTimeOffset */, 60 /* endTimeOffset */); + setActivated(true /* activated */, -90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void customSchedule_whenStartedDuringNight_ifOnDuringNightInFuture_turnsOn() { + setAutoModeCustom(-60 /* startTimeOffset */, 60 /* endTimeOffset */); + setActivated(true /* activated */, 30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void customSchedule_whenStartedDuringNight_ifOnDuringNightInPast_turnsOn() { + setAutoModeCustom(-60 /* startTimeOffset */, 60 /* endTimeOffset */); + setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void twilightSchedule_whenStartedAfterNight_ifOffAfterNight_turnsOff() { + setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */); + setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedAfterNight_ifOffBeforeNight_turnsOff() { + setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */); + setActivated(false /* activated */, -180 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedAfterNight_ifOffDuringNight_turnsOff() { + setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */); + setActivated(false /* activated */, -90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedAfterNight_ifOffInFuture_turnsOff() { + setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */); + setActivated(false /* activated */, 30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedAfterNight_ifOnAfterNight_turnsOn() { + setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */); + setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void twilightSchedule_whenStartedAfterNight_ifOnBeforeNight_turnsOff() { + setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */); + setActivated(true /* activated */, -180 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedAfterNight_ifOnDuringNight_turnsOff() { + setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */); + setActivated(true /* activated */, -90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedAfterNight_ifOnInFuture_turnsOff() { + setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */); + setActivated(true /* activated */, 30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedBeforeNight_ifOffAfterNight_turnsOff() { + setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */); + setActivated(false /* activated */, 180 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedBeforeNight_ifOffBeforeNight_turnsOff() { + setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */); + setActivated(false /* activated */, 30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedBeforeNight_ifOffDuringNight_turnsOff() { + setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */); + setActivated(false /* activated */, 90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedBeforeNight_ifOffInPast_turnsOff() { + setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */); + setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedBeforeNight_ifOnAfterNight_turnsOff() { + setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */); + setActivated(true /* activated */, 180 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedBeforeNight_ifOnBeforeNight_turnsOff() { + setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */); + setActivated(true /* activated */, 30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedBeforeNight_ifOnDuringNight_turnsOff() { + setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */); + setActivated(true /* activated */, 90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedBeforeNight_ifOnInPast_turnsOn() { + setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */); + setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void twilightSchedule_whenStartedDuringNight_ifOffAfterNight_turnsOn() { + setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */); + setActivated(false /* activated */, 90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void twilightSchedule_whenStartedDuringNight_ifOffBeforeNight_turnsOn() { + setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */); + setActivated(false /* activated */, -90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void twilightSchedule_whenStartedDuringNight_ifOffDuringNightInFuture_turnsOn() { + setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */); + setActivated(false /* activated */, 30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void twilightSchedule_whenStartedDuringNight_ifOffDuringNightInPast_turnsOff() { + setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */); + setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(false /* activated */); + } + + @Test + public void twilightSchedule_whenStartedDuringNight_ifOnAfterNight_turnsOn() { + setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */); + setActivated(true /* activated */, 90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void twilightSchedule_whenStartedDuringNight_ifOnBeforeNight_turnsOn() { + setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */); + setActivated(true /* activated */, -90 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void twilightSchedule_whenStartedDuringNight_ifOnDuringNightInFuture_turnsOn() { + setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */); + setActivated(true /* activated */, 30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + @Test + public void twilightSchedule_whenStartedDuringNight_ifOnDuringNightInPast_turnsOn() { + setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */); + setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */); + + startService(); + assertActivated(true /* activated */); + } + + /** + * Convenience for making a {@link LocalTime} instance with an offset relative to now. + * + * @param offsetMinutes the offset relative to now (in minutes) + * @return the LocalTime instance + */ + private LocalTime getLocalTimeRelativeToNow(int offsetMinutes) { + final Calendar c = Calendar.getInstance(); + c.add(Calendar.MINUTE, offsetMinutes); + return new LocalTime(c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE)); + } + + /** + * Configures Night display to use a custom schedule. + * + * @param startTimeOffset the offset relative to now to activate Night display (in minutes) + * @param endTimeOffset the offset relative to now to deactivate Night display (in minutes) + */ + private void setAutoModeCustom(int startTimeOffset, int endTimeOffset) { + mNightDisplayController.setAutoMode(NightDisplayController.AUTO_MODE_CUSTOM); + mNightDisplayController.setCustomStartTime(getLocalTimeRelativeToNow(startTimeOffset)); + mNightDisplayController.setCustomEndTime(getLocalTimeRelativeToNow(endTimeOffset)); + } + + /** + * Configures Night display to use the twilight schedule. + * + * @param sunsetOffset the offset relative to now for sunset (in minutes) + * @param sunriseOffset the offset relative to now for sunrise (in minutes) + */ + private void setAutoModeTwilight(int sunsetOffset, int sunriseOffset) { + mNightDisplayController.setAutoMode(NightDisplayController.AUTO_MODE_TWILIGHT); + + final LocalTime sunset = getLocalTimeRelativeToNow(sunsetOffset); + final LocalTime sunrise = getLocalTimeRelativeToNow(sunriseOffset); + + final Calendar now = Calendar.getInstance(); + long sunsetMillis = sunset.getDateTimeBefore(now).getTimeInMillis(); + long sunriseMillis = sunrise.getDateTimeBefore(now).getTimeInMillis(); + if (sunsetMillis < sunriseMillis) { + sunsetMillis = sunset.getDateTimeAfter(now).getTimeInMillis(); + } else { + sunriseMillis = sunrise.getDateTimeAfter(now).getTimeInMillis(); + } + + final TwilightState state = new TwilightState(sunriseMillis, sunsetMillis); + doReturn(state).when(mTwilightManager).getLastTwilightState(); + } + + /** + * Configures the Night display activated state. + * + * @param activated {@code true} if Night display should be activated + * @param lastActivatedTimeOffset the offset relative to now to record that Night display was + activated (in minutes) + */ + private void setActivated(boolean activated, int lastActivatedTimeOffset) { + mNightDisplayController.setActivated(activated); + + final Calendar c = Calendar.getInstance(); + c.add(Calendar.MINUTE, lastActivatedTimeOffset); + Secure.putLongForUser(mContext.getContentResolver(), + Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, c.getTimeInMillis(), mUserId); + } + + /** + * Convenience method to start {@link #mNightDisplayService}. + */ + private void startService() { + Secure.putIntForUser(mContext.getContentResolver(), Secure.USER_SETUP_COMPLETE, 1, mUserId); + + InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + mNightDisplayService.onStart(); + mNightDisplayService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + mNightDisplayService.onStartUser(mUserId); + } + }); + } + + /** + * Convenience method for asserting whether Night display should be activated. + * + * @param activated the expected activated state of Night display + */ + private void assertActivated(boolean activated) { + assertWithMessage("Invalid Night display activated state") + .that(mNightDisplayController.isActivated()) + .isEqualTo(activated); + } +} |