Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2ce862c3 authored by Christine Franks's avatar Christine Franks
Browse files

Handle night display state when timezone changes

Bug: 64458884
Test: runtest -c com.android.server.NightDisplayServiceTest \
frameworks-services and manually tested Hawaii and London

Change-Id: I052034a4c64eb73c42672215e8847c11e00efeb5
parent 406042a5
Loading
Loading
Loading
Loading
+0 −1
Original line number Original line Diff line number Diff line
@@ -5243,7 +5243,6 @@ com.android.internal.app.IVoiceInteractor
com.android.internal.app.IVoiceInteractor$Stub
com.android.internal.app.IVoiceInteractor$Stub
com.android.internal.app.NightDisplayController
com.android.internal.app.NightDisplayController
com.android.internal.app.NightDisplayController$Callback
com.android.internal.app.NightDisplayController$Callback
com.android.internal.app.NightDisplayController$LocalTime
com.android.internal.app.ProcessMap
com.android.internal.app.ProcessMap
com.android.internal.app.ResolverActivity
com.android.internal.app.ResolverActivity
com.android.internal.app.ToolbarActionBar
com.android.internal.app.ToolbarActionBar
+0 −1
Original line number Original line Diff line number Diff line
@@ -2776,7 +2776,6 @@ com.android.internal.app.IVoiceInteractionManagerService$Stub
com.android.internal.app.IVoiceInteractor
com.android.internal.app.IVoiceInteractor
com.android.internal.app.IVoiceInteractor$Stub
com.android.internal.app.IVoiceInteractor$Stub
com.android.internal.app.NightDisplayController
com.android.internal.app.NightDisplayController
com.android.internal.app.NightDisplayController$1
com.android.internal.appwidget.IAppWidgetService
com.android.internal.appwidget.IAppWidgetService
com.android.internal.appwidget.IAppWidgetService$Stub
com.android.internal.appwidget.IAppWidgetService$Stub
com.android.internal.appwidget.IAppWidgetService$Stub$Proxy
com.android.internal.appwidget.IAppWidgetService$Stub$Proxy
+3 −2
Original line number Original line Diff line number Diff line
@@ -6909,8 +6909,9 @@ public final class Settings {
        public static final String NIGHT_DISPLAY_CUSTOM_END_TIME = "night_display_custom_end_time";
        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
         * A String representing the LocalDateTime when Night display was last activated. Use to
         * whether to apply the current activated state after a reboot or user change.
         * decide whether to apply the current activated state after a reboot or user change. In
         * legacy cases, this is represented by the time in milliseconds (since epoch).
         * @hide
         * @hide
         */
         */
        public static final String NIGHT_DISPLAY_LAST_ACTIVATED_TIME =
        public static final String NIGHT_DISPLAY_LAST_ACTIVATED_TIME =
+31 −119
Original line number Original line Diff line number Diff line
@@ -32,8 +32,12 @@ import com.android.internal.R;


import java.lang.annotation.Retention;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.RetentionPolicy;
import java.util.Calendar;
import java.time.DateTimeException;
import java.util.Locale;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeParseException;


/**
/**
 * Controller for managing Night display settings.
 * Controller for managing Night display settings.
@@ -116,8 +120,9 @@ public final class NightDisplayController {
     */
     */
    public boolean setActivated(boolean activated) {
    public boolean setActivated(boolean activated) {
        if (isActivated() != activated) {
        if (isActivated() != activated) {
            Secure.putLongForUser(mContext.getContentResolver(),
            Secure.putStringForUser(mContext.getContentResolver(),
                    Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, System.currentTimeMillis(),
                    Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
                    LocalDateTime.now().toString(),
                    mUserId);
                    mUserId);
        }
        }
        return Secure.putIntForUser(mContext.getContentResolver(),
        return Secure.putIntForUser(mContext.getContentResolver(),
@@ -128,17 +133,22 @@ public final class NightDisplayController {
     * Returns the time when Night display's activation state last changed, or {@code null} if it
     * Returns the time when Night display's activation state last changed, or {@code null} if it
     * has never been changed.
     * has never been changed.
     */
     */
    public Calendar getLastActivatedTime() {
    public LocalDateTime getLastActivatedTime() {
        final ContentResolver cr = mContext.getContentResolver();
        final ContentResolver cr = mContext.getContentResolver();
        final long lastActivatedTimeMillis = Secure.getLongForUser(
        final String lastActivatedTime = Secure.getStringForUser(
                cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, -1, mUserId);
                cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, mUserId);
        if (lastActivatedTimeMillis < 0) {
        if (lastActivatedTime != null) {
            return null;
            try {
                return LocalDateTime.parse(lastActivatedTime);
            } catch (DateTimeParseException ignored) {}
            // Uses the old epoch time.
            try {
                return LocalDateTime.ofInstant(
                    Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
                    ZoneId.systemDefault());
            } catch (DateTimeException|NumberFormatException ignored) {}
        }
        }

        return null;
        final Calendar lastActivatedTime = Calendar.getInstance();
        lastActivatedTime.setTimeInMillis(lastActivatedTimeMillis);
        return lastActivatedTime;
    }
    }


    /**
    /**
@@ -183,8 +193,10 @@ public final class NightDisplayController {
        }
        }


        if (getAutoMode() != autoMode) {
        if (getAutoMode() != autoMode) {
            Secure.putLongForUser(mContext.getContentResolver(),
            Secure.putStringForUser(mContext.getContentResolver(),
                    Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, -1L, mUserId);
                    Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
                    null,
                    mUserId);
        }
        }
        return Secure.putIntForUser(mContext.getContentResolver(),
        return Secure.putIntForUser(mContext.getContentResolver(),
                Secure.NIGHT_DISPLAY_AUTO_MODE, autoMode, mUserId);
                Secure.NIGHT_DISPLAY_AUTO_MODE, autoMode, mUserId);
@@ -206,7 +218,7 @@ public final class NightDisplayController {
                    R.integer.config_defaultNightDisplayCustomStartTime);
                    R.integer.config_defaultNightDisplayCustomStartTime);
        }
        }


        return LocalTime.valueOf(startTimeValue);
        return LocalTime.ofSecondOfDay(startTimeValue / 1000);
    }
    }


    /**
    /**
@@ -221,7 +233,7 @@ public final class NightDisplayController {
            throw new IllegalArgumentException("startTime cannot be null");
            throw new IllegalArgumentException("startTime cannot be null");
        }
        }
        return Secure.putIntForUser(mContext.getContentResolver(),
        return Secure.putIntForUser(mContext.getContentResolver(),
                Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, startTime.toMillis(), mUserId);
                Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, startTime.toSecondOfDay() * 1000, mUserId);
    }
    }


    /**
    /**
@@ -240,7 +252,7 @@ public final class NightDisplayController {
                    R.integer.config_defaultNightDisplayCustomEndTime);
                    R.integer.config_defaultNightDisplayCustomEndTime);
        }
        }


        return LocalTime.valueOf(endTimeValue);
        return LocalTime.ofSecondOfDay(endTimeValue / 1000);
    }
    }


    /**
    /**
@@ -255,7 +267,7 @@ public final class NightDisplayController {
            throw new IllegalArgumentException("endTime cannot be null");
            throw new IllegalArgumentException("endTime cannot be null");
        }
        }
        return Secure.putIntForUser(mContext.getContentResolver(),
        return Secure.putIntForUser(mContext.getContentResolver(),
                Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.toMillis(), mUserId);
                Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.toSecondOfDay() * 1000, mUserId);
    }
    }


    /**
    /**
@@ -378,106 +390,6 @@ public final class NightDisplayController {
        return context.getResources().getBoolean(R.bool.config_nightDisplayAvailable);
        return context.getResources().getBoolean(R.bool.config_nightDisplayAvailable);
    }
    }


    /**
     * A time without a time-zone or date.
     */
    public static class LocalTime {

        /**
         * The hour of the day from 0 - 23.
         */
        public final int hourOfDay;
        /**
         * The minute within the hour from 0 - 59.
         */
        public final int minute;

        public LocalTime(int hourOfDay, int minute) {
            if (hourOfDay < 0 || hourOfDay > 23) {
                throw new IllegalArgumentException("Invalid hourOfDay: " + hourOfDay);
            } else if (minute < 0 || minute > 59) {
                throw new IllegalArgumentException("Invalid minute: " + minute);
            }

            this.hourOfDay = hourOfDay;
            this.minute = minute;
        }

        /**
         * Returns the first date time corresponding to this local time that occurs before the
         * provided date time.
         *
         * @param time the date time to compare against
         * @return the prior date time corresponding to this local time
         */
        public Calendar getDateTimeBefore(Calendar time) {
            final Calendar c = Calendar.getInstance();
            c.set(Calendar.YEAR, time.get(Calendar.YEAR));
            c.set(Calendar.DAY_OF_YEAR, time.get(Calendar.DAY_OF_YEAR));

            c.set(Calendar.HOUR_OF_DAY, hourOfDay);
            c.set(Calendar.MINUTE, minute);
            c.set(Calendar.SECOND, 0);
            c.set(Calendar.MILLISECOND, 0);

            // Check if the local time has past, if so return the same time tomorrow.
            if (c.after(time)) {
                c.add(Calendar.DATE, -1);
            }

            return c;
        }

        /**
         * Returns the first date time corresponding to this local time that occurs after the
         * provided date time.
         *
         * @param time the date time to compare against
         * @return the next date time corresponding to this local time
         */
        public Calendar getDateTimeAfter(Calendar time) {
            final Calendar c = Calendar.getInstance();
            c.set(Calendar.YEAR, time.get(Calendar.YEAR));
            c.set(Calendar.DAY_OF_YEAR, time.get(Calendar.DAY_OF_YEAR));

            c.set(Calendar.HOUR_OF_DAY, hourOfDay);
            c.set(Calendar.MINUTE, minute);
            c.set(Calendar.SECOND, 0);
            c.set(Calendar.MILLISECOND, 0);

            // Check if the local time has past, if so return the same time tomorrow.
            if (c.before(time)) {
                c.add(Calendar.DATE, 1);
            }

            return c;
        }

        /**
         * Returns a local time corresponding the given number of milliseconds from midnight.
         *
         * @param millis the number of milliseconds from midnight
         * @return the corresponding local time
         */
        private static LocalTime valueOf(int millis) {
            final int hourOfDay = (millis / 3600000) % 24;
            final int minutes = (millis / 60000) % 60;
            return new LocalTime(hourOfDay, minutes);
        }

        /**
         * Returns the local time represented as milliseconds from midnight.
         */
        private int toMillis() {
            return hourOfDay * 3600000 + minute * 60000;
        }

        @Override
        public String toString() {
            return String.format(Locale.US, "%02d:%02d", hourOfDay, minute);
        }
    }

    /**
    /**
     * Callback invoked whenever the Night display settings are changed.
     * Callback invoked whenever the Night display settings are changed.
     */
     */
+59 −42
Original line number Original line Diff line number Diff line
@@ -48,8 +48,10 @@ import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
import com.android.server.twilight.TwilightState;


import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.Calendar;
import java.util.TimeZone;
import java.util.TimeZone;


import com.android.internal.R;
import com.android.internal.R;
@@ -308,7 +310,7 @@ public final class NightDisplayService extends SystemService
    }
    }


    @Override
    @Override
    public void onCustomStartTimeChanged(NightDisplayController.LocalTime startTime) {
    public void onCustomStartTimeChanged(LocalTime startTime) {
        Slog.d(TAG, "onCustomStartTimeChanged: startTime=" + startTime);
        Slog.d(TAG, "onCustomStartTimeChanged: startTime=" + startTime);


        if (mAutoMode != null) {
        if (mAutoMode != null) {
@@ -317,7 +319,7 @@ public final class NightDisplayService extends SystemService
    }
    }


    @Override
    @Override
    public void onCustomEndTimeChanged(NightDisplayController.LocalTime endTime) {
    public void onCustomEndTimeChanged(LocalTime endTime) {
        Slog.d(TAG, "onCustomEndTimeChanged: endTime=" + endTime);
        Slog.d(TAG, "onCustomEndTimeChanged: endTime=" + endTime);


        if (mAutoMode != null) {
        if (mAutoMode != null) {
@@ -416,6 +418,36 @@ public final class NightDisplayService extends SystemService
        outTemp[10] = blue;
        outTemp[10] = blue;
    }
    }


    /**
     * Returns the first date time corresponding to the local time that occurs before the
     * provided date time.
     *
     * @param compareTime the LocalDateTime to compare against
     * @return the prior LocalDateTime corresponding to this local time
     */
    public static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
        final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
                compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());

        // Check if the local time has passed, if so return the same time yesterday.
        return ldt.isAfter(compareTime) ? ldt.minusDays(1) : ldt;
    }

    /**
     * Returns the first date time corresponding to this local time that occurs after the
     * provided date time.
     *
     * @param compareTime the LocalDateTime to compare against
     * @return the next LocalDateTime corresponding to this local time
     */
    public static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
        final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
                compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());

        // Check if the local time has passed, if so return the same time tomorrow.
        return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
    }

    private abstract class AutoMode implements NightDisplayController.Callback {
    private abstract class AutoMode implements NightDisplayController.Callback {
        public abstract void onStart();
        public abstract void onStart();


@@ -427,10 +459,10 @@ public final class NightDisplayService extends SystemService
        private final AlarmManager mAlarmManager;
        private final AlarmManager mAlarmManager;
        private final BroadcastReceiver mTimeChangedReceiver;
        private final BroadcastReceiver mTimeChangedReceiver;


        private NightDisplayController.LocalTime mStartTime;
        private LocalTime mStartTime;
        private NightDisplayController.LocalTime mEndTime;
        private LocalTime mEndTime;


        private Calendar mLastActivatedTime;
        private LocalDateTime mLastActivatedTime;


        CustomAutoMode() {
        CustomAutoMode() {
            mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
            mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
@@ -443,31 +475,15 @@ public final class NightDisplayService extends SystemService
        }
        }


        private void updateActivated() {
        private void updateActivated() {
            final Calendar now = Calendar.getInstance();
            final LocalDateTime now = LocalDateTime.now();
            final Calendar startTime = mStartTime.getDateTimeBefore(now);
            final LocalDateTime start = getDateTimeBefore(mStartTime, now);
            final Calendar endTime = mEndTime.getDateTimeAfter(startTime);
            final LocalDateTime end = getDateTimeAfter(mEndTime, start);
            boolean activate = now.isBefore(end);


            boolean activate = now.before(endTime);
            if (mLastActivatedTime != null) {
            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);
                    final int dayOfYear = mLastActivatedTime.get(Calendar.DAY_OF_YEAR);
                    final int hourOfDay = mLastActivatedTime.get(Calendar.HOUR_OF_DAY);
                    final int minute = mLastActivatedTime.get(Calendar.MINUTE);

                    mLastActivatedTime.setTimeZone(currentTimeZone);
                    mLastActivatedTime.set(Calendar.YEAR, year);
                    mLastActivatedTime.set(Calendar.DAY_OF_YEAR, dayOfYear);
                    mLastActivatedTime.set(Calendar.HOUR_OF_DAY, hourOfDay);
                    mLastActivatedTime.set(Calendar.MINUTE, minute);
                }

                // Maintain the existing activated state if within the current period.
                // Maintain the existing activated state if within the current period.
                if (mLastActivatedTime.before(now)
                if (mLastActivatedTime.isBefore(now) && mLastActivatedTime.isAfter(start)
                        && mLastActivatedTime.after(startTime)
                        && (mLastActivatedTime.isAfter(end) || now.isBefore(end))) {
                        && (mLastActivatedTime.after(endTime) || now.before(endTime))) {
                    activate = mController.isActivated();
                    activate = mController.isActivated();
                }
                }
            }
            }
@@ -475,14 +491,16 @@ public final class NightDisplayService extends SystemService
            if (mIsActivated == null || mIsActivated != activate) {
            if (mIsActivated == null || mIsActivated != activate) {
                mController.setActivated(activate);
                mController.setActivated(activate);
            }
            }

            updateNextAlarm(mIsActivated, now);
            updateNextAlarm(mIsActivated, now);
        }
        }


        private void updateNextAlarm(@Nullable Boolean activated, @NonNull Calendar now) {
        private void updateNextAlarm(@Nullable Boolean activated, @NonNull LocalDateTime now) {
            if (activated != null) {
            if (activated != null) {
                final Calendar next = activated ? mEndTime.getDateTimeAfter(now)
                final LocalDateTime next = activated ? getDateTimeAfter(mEndTime, now)
                        : mStartTime.getDateTimeAfter(now);
                        : getDateTimeAfter(mStartTime, now);
                mAlarmManager.setExact(AlarmManager.RTC, next.getTimeInMillis(), TAG, this, null);
                final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                mAlarmManager.setExact(AlarmManager.RTC, millis, TAG, this, null);
            }
            }
        }
        }


@@ -512,18 +530,18 @@ public final class NightDisplayService extends SystemService
        @Override
        @Override
        public void onActivated(boolean activated) {
        public void onActivated(boolean activated) {
            mLastActivatedTime = mController.getLastActivatedTime();
            mLastActivatedTime = mController.getLastActivatedTime();
            updateNextAlarm(activated, Calendar.getInstance());
            updateNextAlarm(activated, LocalDateTime.now());
        }
        }


        @Override
        @Override
        public void onCustomStartTimeChanged(NightDisplayController.LocalTime startTime) {
        public void onCustomStartTimeChanged(LocalTime startTime) {
            mStartTime = startTime;
            mStartTime = startTime;
            mLastActivatedTime = null;
            mLastActivatedTime = null;
            updateActivated();
            updateActivated();
        }
        }


        @Override
        @Override
        public void onCustomEndTimeChanged(NightDisplayController.LocalTime endTime) {
        public void onCustomEndTimeChanged(LocalTime endTime) {
            mEndTime = endTime;
            mEndTime = endTime;
            mLastActivatedTime = null;
            mLastActivatedTime = null;
            updateActivated();
            updateActivated();
@@ -552,15 +570,14 @@ public final class NightDisplayService extends SystemService
            }
            }


            boolean activate = state.isNight();
            boolean activate = state.isNight();
            final Calendar lastActivatedTime = mController.getLastActivatedTime();
            final LocalDateTime lastActivatedTime = mController.getLastActivatedTime();
            if (lastActivatedTime != null) {
            if (lastActivatedTime != null) {
                final Calendar now = Calendar.getInstance();
                final LocalDateTime now = LocalDateTime.now();
                final Calendar sunrise = state.sunrise();
                final LocalDateTime sunrise = state.sunrise();
                final Calendar sunset = state.sunset();
                final LocalDateTime sunset = state.sunset();

                // Maintain the existing activated state if within the current period.
                // Maintain the existing activated state if within the current period.
                if (lastActivatedTime.before(now)
                if (lastActivatedTime.isBefore(now) && (lastActivatedTime.isBefore(sunrise)
                        && (lastActivatedTime.after(sunrise) ^ lastActivatedTime.after(sunset))) {
                        ^ lastActivatedTime.isBefore(sunset))) {
                    activate = mController.isActivated();
                    activate = mController.isActivated();
                }
                }
            }
            }
Loading