Loading core/java/android/app/timedetector/TimeDetectorHelper.java 0 → 100644 +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; } } services/core/java/com/android/server/timedetector/ConfigurationInternal.java +63 −39 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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. */ Loading Loading @@ -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; } Loading @@ -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 Loading @@ -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; Loading @@ -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; } Loading Loading @@ -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; } Loading @@ -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() { Loading services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java +7 −16 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -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(); } Loading Loading @@ -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 Loading @@ -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 Loading services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java +29 −12 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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; } Loading Loading @@ -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); Loading @@ -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 Loading @@ -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; Loading services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java +12 −7 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/app/timedetector/TimeDetectorHelper.java 0 → 100644 +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; } }
services/core/java/com/android/server/timedetector/ConfigurationInternal.java +63 −39 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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. */ Loading Loading @@ -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; } Loading @@ -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 Loading @@ -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; Loading @@ -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; } Loading Loading @@ -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; } Loading @@ -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() { Loading
services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java +7 −16 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -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(); } Loading Loading @@ -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 Loading @@ -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 Loading
services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java +29 −12 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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; } Loading Loading @@ -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); Loading @@ -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 Loading @@ -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; Loading
services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java +12 −7 File changed.Preview size limit exceeded, changes collapsed. Show changes