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

Commit 13415197 authored by Neil Fuller's avatar Neil Fuller
Browse files

Centralize upper/lower time bound logic

Move the upper / lower bound constants for valid time suggestions into a
single class TimeDetectorHelper, which can be accessed by both server
and client (SettingsUI) code. This reduces the number of places that
need to be touched when we change those values.

See also the associated change in packages/apps/Settings which removes
bounds checks from the client code and switches that code over to use
TimeDetectorHelper methods.

Test: services/tests/servicestests/src/com/android/server/timedetector/
Bug: 228967927
Change-Id: I952ecd4fc2e0d80ffeda4e288fb838253142953e
parent 422354c9
Loading
Loading
Loading
Loading
+158 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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 android.app.timedetector;

import android.annotation.NonNull;
import android.os.Build;

import java.time.Instant;

/**
 * A utility class for fundamental time detector-related logic that doesn't need to communicate with
 * the time detector service, i.e. because it can use SDK APIs or hard-coded facts, and doesn't need
 * permissions or singleton state. Putting logic here avoids the need to expose binder-based calls
 * or to duplicate code to share related logic (since android.app.timedetector classes are visible
 * to all processes).
 *
 * @hide
 */
// Not final for easy replacement / mocking during tests.
public class TimeDetectorHelper {

    /**
     * See {@link #getManualDateSelectionYearMin()}. Chosen to produce Unix epoch times be greater
     * than {@link #MANUAL_SUGGESTION_LOWER_BOUND}.
     */
    private static final int MANUAL_SUGGESTION_YEAR_MIN = 2008;

    /**
     * The maximum gregorian calendar year to allow for manual date selection on devices unlikely to
     * have Y2038 issues. This serves as a sensible UI-enforced limit though the system server may
     * support a larger upper bound. Users besides future archeologists are unlikely to need higher
     * values, for a few years at least.
     */
    private static final int MANUAL_SUGGESTION_YEAR_MAX_WITHOUT_Y2038_ISSUE = 2100;

    /**
     * The maximum gregorian calendar year to allow for manual date selection on devices that may
     * have Y2038 issues. This serves as a sensible UI-enforced limit though the system server may
     * support a larger upper bound. That is, the signed 32-bit milliseconds value is
     * 03:14:07 UTC on 19 January 2038, but this constant means users can only enter dates up to
     * 2037-12-31. See {@link #MANUAL_SUGGESTION_YEAR_MAX_WITH_Y2038_ISSUE}.
     *
     * <p>Note: This UI limit also doesn't prevent devices reaching the Y2038 roll-over time through
     * the natural passage of time, it just prevents users potentially causing issues in the years
     * leading up to it accidentally via the UI.
     */
    private static final int MANUAL_SUGGESTION_YEAR_MAX_WITH_Y2038_ISSUE = 2037;

    /**
     * The upper bound for valid suggestions when the Y2038 issue is a risk. This is the instant
     * when the Y2038 issue occurs.
     */
    private static final Instant SUGGESTION_UPPER_BOUND_WITH_Y2038_ISSUE =
            Instant.ofEpochMilli(1000L * Integer.MAX_VALUE);

    /**
     * The upper bound for valid suggestions when the Y2038 issue is not a risk. This values means
     * there is no practical upper bound.
     *
     * <p>Make sure this value remains in the value representable as a signed int64 Unix epoch
     * millis value as in various places {@link Instant#toEpochMilli()} is called, and that throws
     * an exception if the value is too large.
     */
    private static final Instant SUGGESTION_UPPER_BOUND_WIITHOUT_Y2038_ISSUE =
            Instant.ofEpochMilli(Long.MAX_VALUE);

    /** See {@link #getManualSuggestionLowerBound()}. */
    private static final Instant MANUAL_SUGGESTION_LOWER_BOUND =
            Instant.ofEpochMilli(1194220800000L); // Nov 5, 2007, 0:00 UTC

    /**
     * The lowest value in Unix epoch milliseconds that is considered a valid automatic suggestion.
     * See also {@link #MANUAL_SUGGESTION_LOWER_BOUND}.
     *
     * <p>Note that this is a default value. The lower value enforced can be overridden to be
     * lower in the system server with flags for testing.
     */
    private static final Instant AUTO_SUGGESTION_LOWER_BOUND_DEFAULT = Instant.ofEpochMilli(
            Long.max(android.os.Environment.getRootDirectory().lastModified(), Build.TIME));

    /** The singleton instance of this class. */
    public static final TimeDetectorHelper INSTANCE = new TimeDetectorHelper();

    /** Constructor present for subclassing in tests. Use {@link #INSTANCE} in production code. */
    protected TimeDetectorHelper() {}

    /**
     * Returns the minimum gregorian calendar year to offer for manual date selection. This serves
     * as a sensible UI-enforced lower limit, the system server may support a smaller lower bound.
     */
    public int getManualDateSelectionYearMin() {
        return MANUAL_SUGGESTION_YEAR_MIN;
    }

    /**
     * Returns the maximum gregorian calendar year to offer for manual date selection. This serves
     * as a sensible UI-enforced lower limit, the system server may support a larger upper bound.
     */
    public int getManualDateSelectionYearMax() {
        return getDeviceHasY2038Issue()
                ? MANUAL_SUGGESTION_YEAR_MAX_WITH_Y2038_ISSUE
                : MANUAL_SUGGESTION_YEAR_MAX_WITHOUT_Y2038_ISSUE;
    }

    /**
     * Returns the lowest value in Unix epoch milliseconds that is considered a valid manual
     * suggestion. For historical reasons Android has a different lower limit for manual input than
     * automatic. This may change in the future to align with automatic suggestions, but has been
     * kept initially to avoid breaking manual tests that are hard-coded with old dates real users
     * will never want to use.
     */
    @NonNull
    public Instant getManualSuggestionLowerBound() {
        return MANUAL_SUGGESTION_LOWER_BOUND;
    }

    /**
     * Returns the lowest value in Unix epoch milliseconds that is considered a valid automatic
     * suggestion. See also {@link #MANUAL_SUGGESTION_LOWER_BOUND}.
     *
     * <p>Note that this is a default value. The lower value enforced can be overridden to be
     * different in the system server with server flags.
     */
    @NonNull
    public Instant getAutoSuggestionLowerBoundDefault() {
        return AUTO_SUGGESTION_LOWER_BOUND_DEFAULT;
    }

    /** Returns the upper bound to enforce for all time suggestions (manual and automatic). */
    @NonNull
    public Instant getSuggestionUpperBound() {
        return getDeviceHasY2038Issue()
                ? SUGGESTION_UPPER_BOUND_WITH_Y2038_ISSUE
                : SUGGESTION_UPPER_BOUND_WIITHOUT_Y2038_ISSUE;
    }

    /**
     * Returns {@code true} if the device may be at risk of time_t overflow (because bionic
     * defines time_t as a 32-bit signed integer for 32-bit processes).
     */
    private boolean getDeviceHasY2038Issue() {
        return Build.SUPPORTED_32_BIT_ABIS.length > 0;
    }
}
+63 −39
Original line number Diff line number Diff line
@@ -46,9 +46,10 @@ public final class ConfigurationInternal {

    private final boolean mAutoDetectionSupported;
    private final int mSystemClockUpdateThresholdMillis;
    private final Instant mAutoTimeLowerBound;
    private final Instant mAutoSuggestionLowerBound;
    private final Instant mManualSuggestionLowerBound;
    private final Instant mSuggestionUpperBound;
    private final @Origin int[] mOriginPriorities;
    private final boolean mDeviceHasY2038Issue;
    private final boolean mAutoDetectionEnabledSetting;
    private final @UserIdInt int mUserId;
    private final boolean mUserConfigAllowed;
@@ -56,9 +57,10 @@ public final class ConfigurationInternal {
    private ConfigurationInternal(Builder builder) {
        mAutoDetectionSupported = builder.mAutoDetectionSupported;
        mSystemClockUpdateThresholdMillis = builder.mSystemClockUpdateThresholdMillis;
        mAutoTimeLowerBound = Objects.requireNonNull(builder.mAutoTimeLowerBound);
        mAutoSuggestionLowerBound = Objects.requireNonNull(builder.mAutoSuggestionLowerBound);
        mManualSuggestionLowerBound = Objects.requireNonNull(builder.mManualSuggestionLowerBound);
        mSuggestionUpperBound = Objects.requireNonNull(builder.mSuggestionUpperBound);
        mOriginPriorities = Objects.requireNonNull(builder.mOriginPriorities);
        mDeviceHasY2038Issue = builder.mDeviceHasY2038Issue;
        mAutoDetectionEnabledSetting = builder.mAutoDetectionEnabledSetting;

        mUserId = builder.mUserId;
@@ -80,30 +82,39 @@ public final class ConfigurationInternal {
    }

    /**
     * Returns the lower bound for valid automatic times. It is guaranteed to be in the past,
     * i.e. it is unrelated to the current system clock time.
     * Returns the lower bound for valid automatic time suggestions. It is guaranteed to be in the
     * past, i.e. it is unrelated to the current system clock time.
     * It holds no other meaning; it could be related to when the device system image was built,
     * or could be updated by a mainline module.
     */
    @NonNull
    public Instant getAutoTimeLowerBound() {
        return mAutoTimeLowerBound;
    public Instant getAutoSuggestionLowerBound() {
        return mAutoSuggestionLowerBound;
    }

    /**
     * Returns the order to look at time suggestions when automatically detecting time.
     * See {@code #ORIGIN_} constants
     * Returns the lower bound for valid manual time suggestions. It is guaranteed to be in the
     * past, i.e. it is unrelated to the current system clock time.
     */
    public @Origin int[] getAutoOriginPriorities() {
        return mOriginPriorities;
    @NonNull
    public Instant getManualSuggestionLowerBound() {
        return mManualSuggestionLowerBound;
    }

    /**
     * Returns {@code true} if the device may be at risk of time_t overflow (because bionic
     * defines time_t as a 32-bit signed integer for 32-bit processes).
     * Returns the upper bound for valid time suggestions (manual and automatic).
     */
    public boolean getDeviceHasY2038Issue() {
        return mDeviceHasY2038Issue;
    @NonNull
    public Instant getSuggestionUpperBound() {
        return mSuggestionUpperBound;
    }

    /**
     * Returns the order to look at time suggestions when automatically detecting time.
     * See {@code #ORIGIN_} constants
     */
    public @Origin int[] getAutoOriginPriorities() {
        return mOriginPriorities;
    }

    /** Returns the value of the auto time detection enabled setting. */
@@ -207,16 +218,17 @@ public final class ConfigurationInternal {
                && mAutoDetectionEnabledSetting == that.mAutoDetectionEnabledSetting
                && mUserId == that.mUserId && mUserConfigAllowed == that.mUserConfigAllowed
                && mSystemClockUpdateThresholdMillis == that.mSystemClockUpdateThresholdMillis
                && mAutoTimeLowerBound.equals(that.mAutoTimeLowerBound)
                && mDeviceHasY2038Issue == that.mDeviceHasY2038Issue
                && mAutoSuggestionLowerBound.equals(that.mAutoSuggestionLowerBound)
                && mManualSuggestionLowerBound.equals(that.mManualSuggestionLowerBound)
                && mSuggestionUpperBound.equals(that.mSuggestionUpperBound)
                && Arrays.equals(mOriginPriorities, that.mOriginPriorities);
    }

    @Override
    public int hashCode() {
        int result = Objects.hash(mAutoDetectionSupported, mAutoDetectionEnabledSetting, mUserId,
                mUserConfigAllowed, mSystemClockUpdateThresholdMillis, mAutoTimeLowerBound,
                mDeviceHasY2038Issue);
                mUserConfigAllowed, mSystemClockUpdateThresholdMillis, mAutoSuggestionLowerBound,
                mManualSuggestionLowerBound, mSuggestionUpperBound);
        result = 31 * result + Arrays.hashCode(mOriginPriorities);
        return result;
    }
@@ -230,10 +242,13 @@ public final class ConfigurationInternal {
        return "ConfigurationInternal{"
                + "mAutoDetectionSupported=" + mAutoDetectionSupported
                + ", mSystemClockUpdateThresholdMillis=" + mSystemClockUpdateThresholdMillis
                + ", mAutoTimeLowerBound=" + mAutoTimeLowerBound
                + "(" + mAutoTimeLowerBound.toEpochMilli() + ")"
                + ", mAutoSuggestionLowerBound=" + mAutoSuggestionLowerBound
                + "(" + mAutoSuggestionLowerBound.toEpochMilli() + ")"
                + ", mManualSuggestionLowerBound=" + mManualSuggestionLowerBound
                + "(" + mManualSuggestionLowerBound.toEpochMilli() + ")"
                + ", mSuggestionUpperBound=" + mSuggestionUpperBound
                + "(" + mSuggestionUpperBound.toEpochMilli() + ")"
                + ", mOriginPriorities=" + originPrioritiesString
                + ", mDeviceHasY2038Issue=" + mDeviceHasY2038Issue
                + ", mAutoDetectionEnabled=" + mAutoDetectionEnabledSetting
                + ", mUserId=" + mUserId
                + ", mUserConfigAllowed=" + mUserConfigAllowed
@@ -243,9 +258,10 @@ public final class ConfigurationInternal {
    static final class Builder {
        private boolean mAutoDetectionSupported;
        private int mSystemClockUpdateThresholdMillis;
        @NonNull private Instant mAutoTimeLowerBound;
        @NonNull private Instant mAutoSuggestionLowerBound;
        @NonNull private Instant mManualSuggestionLowerBound;
        @NonNull private Instant mSuggestionUpperBound;
        @NonNull private @Origin int[] mOriginPriorities;
        private boolean mDeviceHasY2038Issue;
        private boolean mAutoDetectionEnabledSetting;

        private final @UserIdInt int mUserId;
@@ -263,9 +279,10 @@ public final class ConfigurationInternal {
            this.mUserConfigAllowed = toCopy.mUserConfigAllowed;
            this.mAutoDetectionSupported = toCopy.mAutoDetectionSupported;
            this.mSystemClockUpdateThresholdMillis = toCopy.mSystemClockUpdateThresholdMillis;
            this.mAutoTimeLowerBound = toCopy.mAutoTimeLowerBound;
            this.mAutoSuggestionLowerBound = toCopy.mAutoSuggestionLowerBound;
            this.mManualSuggestionLowerBound = toCopy.mManualSuggestionLowerBound;
            this.mSuggestionUpperBound = toCopy.mSuggestionUpperBound;
            this.mOriginPriorities = toCopy.mOriginPriorities;
            this.mDeviceHasY2038Issue = toCopy.mDeviceHasY2038Issue;
            this.mAutoDetectionEnabledSetting = toCopy.mAutoDetectionEnabledSetting;
        }

@@ -296,10 +313,26 @@ public final class ConfigurationInternal {
        }

        /**
         * Sets the lower bound for valid automatic times.
         * Sets the lower bound for valid automatic time suggestions.
         */
        public Builder setAutoTimeLowerBound(@NonNull Instant autoTimeLowerBound) {
            mAutoTimeLowerBound = Objects.requireNonNull(autoTimeLowerBound);
        public Builder setAutoSuggestionLowerBound(@NonNull Instant autoSuggestionLowerBound) {
            mAutoSuggestionLowerBound = Objects.requireNonNull(autoSuggestionLowerBound);
            return this;
        }

        /**
         * Sets the lower bound for valid manual time suggestions.
         */
        public Builder setManualSuggestionLowerBound(@NonNull Instant manualSuggestionLowerBound) {
            mManualSuggestionLowerBound = Objects.requireNonNull(manualSuggestionLowerBound);
            return this;
        }

        /**
         * Sets the upper bound for valid time suggestions (manual and automatic).
         */
        public Builder setSuggestionUpperBound(@NonNull Instant suggestionUpperBound) {
            mSuggestionUpperBound = Objects.requireNonNull(suggestionUpperBound);
            return this;
        }

@@ -320,15 +353,6 @@ public final class ConfigurationInternal {
            return this;
        }

        /**
         * Returns {@code true} if the device may be at risk of time_t overflow (because bionic
         * defines time_t as a 32-bit signed integer for 32-bit processes).
         */
        Builder setDeviceHasY2038Issue(boolean deviceHasY2038Issue) {
            mDeviceHasY2038Issue = deviceHasY2038Issue;
            return this;
        }

        /** Returns a new {@link ConfigurationInternal}. */
        @NonNull
        ConfigurationInternal build() {
+7 −16
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import android.app.ActivityManagerInternal;
import android.app.time.TimeCapabilities;
import android.app.time.TimeCapabilitiesAndConfig;
import android.app.time.TimeConfiguration;
import android.app.timedetector.TimeDetectorHelper;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -38,7 +39,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.os.Build;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
@@ -74,13 +74,6 @@ final class ServiceConfigAccessorImpl implements ServiceConfigAccessor {
    private static final @Origin int[]
            DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES = { ORIGIN_TELEPHONY, ORIGIN_NETWORK };

    /**
     * Time in the past. If an automatic time suggestion is before this point, it is sure to be
     * incorrect.
     */
    private static final Instant TIME_LOWER_BOUND_DEFAULT = Instant.ofEpochMilli(
            Long.max(android.os.Environment.getRootDirectory().lastModified(), Build.TIME));

    /** Device config keys that affect the {@link TimeDetectorService}. */
    private static final Set<String> SERVER_FLAGS_KEYS_TO_WATCH = Set.of(
            KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE,
@@ -237,14 +230,16 @@ final class ServiceConfigAccessorImpl implements ServiceConfigAccessor {
    @Override
    @NonNull
    public synchronized ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) {
        TimeDetectorHelper timeDetectorHelper = TimeDetectorHelper.INSTANCE;
        return new ConfigurationInternal.Builder(userId)
                .setUserConfigAllowed(isUserConfigAllowed(userId))
                .setAutoDetectionSupported(isAutoDetectionSupported())
                .setAutoDetectionEnabledSetting(getAutoDetectionEnabledSetting())
                .setSystemClockUpdateThresholdMillis(getSystemClockUpdateThresholdMillis())
                .setAutoTimeLowerBound(getAutoTimeLowerBound())
                .setAutoSuggestionLowerBound(getAutoSuggestionLowerBound())
                .setManualSuggestionLowerBound(timeDetectorHelper.getManualSuggestionLowerBound())
                .setSuggestionUpperBound(timeDetectorHelper.getSuggestionUpperBound())
                .setOriginPriorities(getOriginPriorities())
                .setDeviceHasY2038Issue(getDeviceHasY2038Issue())
                .build();
    }

@@ -291,9 +286,9 @@ final class ServiceConfigAccessorImpl implements ServiceConfigAccessor {
    }

    @NonNull
    private Instant getAutoTimeLowerBound() {
    private Instant getAutoSuggestionLowerBound() {
        return mServerFlags.getOptionalInstant(KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE)
                .orElse(TIME_LOWER_BOUND_DEFAULT);
                .orElse(TimeDetectorHelper.INSTANCE.getAutoSuggestionLowerBoundDefault());
    }

    @NonNull
@@ -310,10 +305,6 @@ final class ServiceConfigAccessorImpl implements ServiceConfigAccessor {
        return DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES;
    }

    private boolean getDeviceHasY2038Issue() {
        return Build.SUPPORTED_32_BIT_ABIS.length > 0;
    }

    /**
     * A base supplier of an array of time origin integers in priority order.
     * It handles memoization of the result to avoid repeated string parsing when nothing has
+29 −12
Original line number Diff line number Diff line
@@ -84,9 +84,6 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
     */
    private static final int KEEP_SUGGESTION_HISTORY_SIZE = 10;

    /** The value in Unix epoch milliseconds of the Y2038 issue. */
    private static final long Y2038_LIMIT_IN_MILLIS = 1000L * Integer.MAX_VALUE;

    /**
     * A log that records the decisions / decision metadata that affected the device's system clock
     * time. This is logged in bug reports to assist with debugging issues with detection.
@@ -248,7 +245,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {

        final TimestampedValue<Long> newUnixEpochTime = suggestion.getUnixEpochTime();

        if (!validateSuggestionTime(newUnixEpochTime, suggestion)) {
        if (!validateManualSuggestionTime(newUnixEpochTime, suggestion)) {
            return false;
        }

@@ -424,7 +421,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
    }

    @GuardedBy("this")
    private boolean validateSuggestionTime(
    private boolean validateSuggestionCommon(
            @NonNull TimestampedValue<Long> newUnixEpochTime, @NonNull Object suggestion) {
        if (newUnixEpochTime.getValue() == null) {
            Slog.w(LOG_TAG, "Suggested time value is null. suggestion=" + suggestion);
@@ -441,8 +438,8 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
            return false;
        }

        if (newUnixEpochTime.getValue() > Y2038_LIMIT_IN_MILLIS
                && mCurrentConfigurationInternal.getDeviceHasY2038Issue()) {
        if (newUnixEpochTime.getValue()
                > mCurrentConfigurationInternal.getSuggestionUpperBound().toEpochMilli()) {
            // This check won't prevent a device's system clock exceeding Integer.MAX_VALUE Unix
            // seconds through the normal passage of time, but it will stop it jumping above 2038
            // because of a "bad" suggestion. b/204193177
@@ -453,20 +450,40 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
        return true;
    }

    /**
     * Returns {@code true} if an automatic time suggestion time is valid.
     * See also {@link #validateManualSuggestionTime(TimestampedValue, Object)}.
     */
    @GuardedBy("this")
    private boolean validateAutoSuggestionTime(
            @NonNull TimestampedValue<Long> newUnixEpochTime, @NonNull Object suggestion)  {
        return validateSuggestionTime(newUnixEpochTime, suggestion)
                && validateSuggestionAgainstLowerBound(newUnixEpochTime, suggestion);
        Instant lowerBound = mCurrentConfigurationInternal.getAutoSuggestionLowerBound();
        return validateSuggestionCommon(newUnixEpochTime, suggestion)
                && validateSuggestionAgainstLowerBound(newUnixEpochTime, suggestion,
                lowerBound);
    }

    /**
     * Returns {@code true} if a manual time suggestion time is valid.
     * See also {@link #validateAutoSuggestionTime(TimestampedValue, Object)}.
     */
    @GuardedBy("this")
    private boolean validateSuggestionAgainstLowerBound(
    private boolean validateManualSuggestionTime(
            @NonNull TimestampedValue<Long> newUnixEpochTime, @NonNull Object suggestion)  {
        Instant lowerBound = mCurrentConfigurationInternal.getAutoTimeLowerBound();
        Instant lowerBound = mCurrentConfigurationInternal.getManualSuggestionLowerBound();

        // Suggestion is definitely wrong if it comes before lower time bound.
        return validateSuggestionCommon(newUnixEpochTime, suggestion)
                && validateSuggestionAgainstLowerBound(newUnixEpochTime, suggestion, lowerBound);
    }

    @GuardedBy("this")
    private boolean validateSuggestionAgainstLowerBound(
            @NonNull TimestampedValue<Long> newUnixEpochTime, @NonNull Object suggestion,
            @NonNull Instant lowerBound) {

        // Suggestion is definitely wrong if it comes before lower time bound.
        if (lowerBound.isAfter(Instant.ofEpochMilli(newUnixEpochTime.getValue()))) {
        if (lowerBound.toEpochMilli() > newUnixEpochTime.getValue()) {
            Slog.w(LOG_TAG, "Suggestion points to time before lower bound, skipping it. "
                    + "suggestion=" + suggestion + ", lower bound=" + lowerBound);
            return false;
+12 −7

File changed.

Preview size limit exceeded, changes collapsed.

Loading