diff --git a/services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java b/services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java index 5f1400097a3a2..b4aa84e27b09b 100644 --- a/services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java +++ b/services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java @@ -15,214 +15,51 @@ */ package com.android.server.timedetector; -import static com.android.server.timedetector.ServerFlags.KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE; -import static com.android.server.timedetector.ServerFlags.KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE; -import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_NETWORK; -import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_TELEPHONY; - import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.os.Build; -import android.os.SystemProperties; -import com.android.internal.R; -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.Preconditions; import com.android.server.timedetector.TimeDetectorStrategy.Origin; import com.android.server.timezonedetector.ConfigurationChangeListener; import java.time.Instant; -import java.util.Arrays; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.function.Supplier; /** - * A singleton that provides access to service configuration for time detection. This hides how - * configuration is split between static, compile-time config and dynamic, server-pushed flags. It - * provides a rudimentary mechanism to signal when values have changed. + * An interface that provides access to service configuration for time detection. This hides + * how configuration is split between static, compile-time config, dynamic server-pushed flags and + * user settings. It provides listeners to signal when values that affect different components have + * changed. */ -final class ServiceConfigAccessor { - - private static final int SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT = 2 * 1000; +public interface ServiceConfigAccessor { /** - * By default telephony and network only suggestions are accepted and telephony takes - * precedence over network. - */ - 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 SERVER_FLAGS_KEYS_TO_WATCH = Set.of( - KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE, - KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE - ); - - private static final Object SLOCK = new Object(); - - /** The singleton instance. Initialized once in {@link #getInstance(Context)}. */ - @GuardedBy("SLOCK") - @Nullable - private static ServiceConfigAccessor sInstance; - - @NonNull private final Context mContext; - @NonNull private final ConfigOriginPrioritiesSupplier mConfigOriginPrioritiesSupplier; - @NonNull private final ServerFlagsOriginPrioritiesSupplier mServerFlagsOriginPrioritiesSupplier; - @NonNull private final ServerFlags mServerFlags; - - /** - * If a newly calculated system clock time and the current system clock time differs by this or - * more the system clock will actually be updated. Used to prevent the system clock being set - * for only minor differences. - */ - private final int mSystemClockUpdateThresholdMillis; - - private ServiceConfigAccessor(@NonNull Context context) { - mContext = Objects.requireNonNull(context); - mServerFlags = ServerFlags.getInstance(mContext); - mConfigOriginPrioritiesSupplier = new ConfigOriginPrioritiesSupplier(context); - mServerFlagsOriginPrioritiesSupplier = - new ServerFlagsOriginPrioritiesSupplier(mServerFlags); - mSystemClockUpdateThresholdMillis = - SystemProperties.getInt("ro.sys.time_detector_update_diff", - SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT); - } - - /** Returns the singleton instance. */ - static ServiceConfigAccessor getInstance(Context context) { - synchronized (SLOCK) { - if (sInstance == null) { - sInstance = new ServiceConfigAccessor(context); - } - return sInstance; - } - } - - /** - * Adds a listener that will be called when server flags related to this class change. The - * callbacks are delivered on the main looper thread. + * Adds a listener that will be invoked when {@link ConfigurationInternal} may have changed. + * The listener is invoked on the main thread. + * * *

Note: Only for use by long-lived objects. There is deliberately no associated remove * method. */ - void addListener(@NonNull ConfigurationChangeListener listener) { - mServerFlags.addListener(listener, SERVER_FLAGS_KEYS_TO_WATCH); - } - - @NonNull - @Origin int[] getOriginPriorities() { - int[] serverFlagsValue = mServerFlagsOriginPrioritiesSupplier.get(); - if (serverFlagsValue != null) { - return serverFlagsValue; - } - - int[] configValue = mConfigOriginPrioritiesSupplier.get(); - if (configValue != null) { - return configValue; - } - return DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES; - } - - int systemClockUpdateThresholdMillis() { - return mSystemClockUpdateThresholdMillis; - } - - @NonNull - Instant autoTimeLowerBound() { - return mServerFlags.getOptionalInstant(KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE) - .orElse(TIME_LOWER_BOUND_DEFAULT); - } + void addListener(@NonNull ConfigurationChangeListener listener); /** - * 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 - * changed. + * Returns the absolute threshold below which the system clock need not be updated. i.e. if + * setting the system clock would adjust it by less than this (either backwards or forwards) + * then it need not be set. */ - private abstract static class BaseOriginPrioritiesSupplier implements Supplier<@Origin int[]> { - @GuardedBy("this") @Nullable private String[] mLastPriorityStrings; - @GuardedBy("this") @Nullable private int[] mLastPriorityInts; - - /** Returns an array of {@code ORIGIN_*} values, or {@code null}. */ - @Override - @Nullable - public @Origin int[] get() { - String[] priorityStrings = lookupPriorityStrings(); - synchronized (this) { - if (Arrays.equals(mLastPriorityStrings, priorityStrings)) { - return mLastPriorityInts; - } - - int[] priorityInts = null; - if (priorityStrings != null && priorityStrings.length > 0) { - priorityInts = new int[priorityStrings.length]; - try { - for (int i = 0; i < priorityInts.length; i++) { - String priorityString = priorityStrings[i]; - Preconditions.checkArgument(priorityString != null); - - priorityString = priorityString.trim(); - priorityInts[i] = TimeDetectorStrategy.stringToOrigin(priorityString); - } - } catch (IllegalArgumentException e) { - // If any strings were bad and they were ignored then the semantics of the - // whole list could change, so return null. - priorityInts = null; - } - } - mLastPriorityStrings = priorityStrings; - mLastPriorityInts = priorityInts; - return priorityInts; - } - } - - @Nullable - protected abstract String[] lookupPriorityStrings(); - } - - /** Supplies origin priorities from config_autoTimeSourcesPriority. */ - private static class ConfigOriginPrioritiesSupplier extends BaseOriginPrioritiesSupplier { - - @NonNull private final Context mContext; - - private ConfigOriginPrioritiesSupplier(Context context) { - mContext = Objects.requireNonNull(context); - } - - @Override - @Nullable - protected String[] lookupPriorityStrings() { - return mContext.getResources().getStringArray(R.array.config_autoTimeSourcesPriority); - } - } + int systemClockUpdateThresholdMillis(); /** - * Supplies origin priorities from device_config (server flags), see - * {@link ServerFlags#KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE}. + * Returns a 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. + * 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. */ - private static class ServerFlagsOriginPrioritiesSupplier extends BaseOriginPrioritiesSupplier { + @NonNull + Instant autoTimeLowerBound(); - @NonNull private final ServerFlags mServerFlags; - - private ServerFlagsOriginPrioritiesSupplier(ServerFlags serverFlags) { - mServerFlags = Objects.requireNonNull(serverFlags); - } - - @Override - @Nullable - protected String[] lookupPriorityStrings() { - Optional priorityStrings = mServerFlags.getOptionalStringArray( - KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE); - return priorityStrings.orElse(null); - } - } + /** + * Returns the order to look at time suggestions when automatically detecting time. + * See {@code #ORIGIN_} constants + */ + @NonNull + @Origin int[] getOriginPriorities(); } diff --git a/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java b/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java new file mode 100644 index 0000000000000..fa654e2e3b605 --- /dev/null +++ b/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java @@ -0,0 +1,229 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.timedetector; + +import static com.android.server.timedetector.ServerFlags.KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE; +import static com.android.server.timedetector.ServerFlags.KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE; +import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_NETWORK; +import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_TELEPHONY; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.os.Build; +import android.os.SystemProperties; + +import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; +import com.android.server.timedetector.TimeDetectorStrategy.Origin; +import com.android.server.timezonedetector.ConfigurationChangeListener; + +import java.time.Instant; +import java.util.Arrays; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Supplier; + +/** + * A singleton implementation of {@link ServiceConfigAccessor}. + */ +final class ServiceConfigAccessorImpl implements ServiceConfigAccessor { + + private static final int SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT = 2 * 1000; + + /** + * By default telephony and network only suggestions are accepted and telephony takes + * precedence over network. + */ + 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 SERVER_FLAGS_KEYS_TO_WATCH = Set.of( + KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE, + KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE + ); + + private static final Object SLOCK = new Object(); + + /** The singleton instance. Initialized once in {@link #getInstance(Context)}. */ + @GuardedBy("SLOCK") + @Nullable + private static ServiceConfigAccessor sInstance; + + @NonNull private final Context mContext; + @NonNull private final ServerFlags mServerFlags; + @NonNull private final ConfigOriginPrioritiesSupplier mConfigOriginPrioritiesSupplier; + @NonNull private final ServerFlagsOriginPrioritiesSupplier mServerFlagsOriginPrioritiesSupplier; + + /** + * If a newly calculated system clock time and the current system clock time differs by this or + * more the system clock will actually be updated. Used to prevent the system clock being set + * for only minor differences. + */ + private final int mSystemClockUpdateThresholdMillis; + + private ServiceConfigAccessorImpl(@NonNull Context context) { + mContext = Objects.requireNonNull(context); + mServerFlags = ServerFlags.getInstance(mContext); + mConfigOriginPrioritiesSupplier = new ConfigOriginPrioritiesSupplier(context); + mServerFlagsOriginPrioritiesSupplier = + new ServerFlagsOriginPrioritiesSupplier(mServerFlags); + mSystemClockUpdateThresholdMillis = + SystemProperties.getInt("ro.sys.time_detector_update_diff", + SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT); + } + + /** Returns the singleton instance. */ + static ServiceConfigAccessor getInstance(Context context) { + synchronized (SLOCK) { + if (sInstance == null) { + sInstance = new ServiceConfigAccessorImpl(context); + } + return sInstance; + } + } + + /** + * Adds a listener that will be called when server flags related to this class change. The + * callbacks are delivered on the main looper thread. + * + *

Note: Only for use by long-lived objects. There is deliberately no associated remove + * method. + */ + public void addListener(@NonNull ConfigurationChangeListener listener) { + mServerFlags.addListener(listener, SERVER_FLAGS_KEYS_TO_WATCH); + } + + @Override + @NonNull + public @Origin int[] getOriginPriorities() { + int[] serverFlagsValue = mServerFlagsOriginPrioritiesSupplier.get(); + if (serverFlagsValue != null) { + return serverFlagsValue; + } + + int[] configValue = mConfigOriginPrioritiesSupplier.get(); + if (configValue != null) { + return configValue; + } + return DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES; + } + + @Override + public int systemClockUpdateThresholdMillis() { + return mSystemClockUpdateThresholdMillis; + } + + @Override + @NonNull + public Instant autoTimeLowerBound() { + return mServerFlags.getOptionalInstant(KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE) + .orElse(TIME_LOWER_BOUND_DEFAULT); + } + + /** + * 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 + * changed. + */ + private abstract static class BaseOriginPrioritiesSupplier implements Supplier<@Origin int[]> { + @GuardedBy("this") @Nullable private String[] mLastPriorityStrings; + @GuardedBy("this") @Nullable private int[] mLastPriorityInts; + + /** Returns an array of {@code ORIGIN_*} values, or {@code null}. */ + @Override + @Nullable + public @Origin int[] get() { + String[] priorityStrings = lookupPriorityStrings(); + synchronized (this) { + if (Arrays.equals(mLastPriorityStrings, priorityStrings)) { + return mLastPriorityInts; + } + + int[] priorityInts = null; + if (priorityStrings != null && priorityStrings.length > 0) { + priorityInts = new int[priorityStrings.length]; + try { + for (int i = 0; i < priorityInts.length; i++) { + String priorityString = priorityStrings[i]; + Preconditions.checkArgument(priorityString != null); + + priorityString = priorityString.trim(); + priorityInts[i] = TimeDetectorStrategy.stringToOrigin(priorityString); + } + } catch (IllegalArgumentException e) { + // If any strings were bad and they were ignored then the semantics of the + // whole list could change, so return null. + priorityInts = null; + } + } + mLastPriorityStrings = priorityStrings; + mLastPriorityInts = priorityInts; + return priorityInts; + } + } + + @Nullable + protected abstract String[] lookupPriorityStrings(); + } + + /** Supplies origin priorities from config_autoTimeSourcesPriority. */ + private static class ConfigOriginPrioritiesSupplier extends BaseOriginPrioritiesSupplier { + + @NonNull private final Context mContext; + + private ConfigOriginPrioritiesSupplier(Context context) { + mContext = Objects.requireNonNull(context); + } + + @Override + @Nullable + protected String[] lookupPriorityStrings() { + return mContext.getResources().getStringArray(R.array.config_autoTimeSourcesPriority); + } + } + + /** + * Supplies origin priorities from device_config (server flags), see + * {@link ServerFlags#KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE}. + */ + private static class ServerFlagsOriginPrioritiesSupplier extends BaseOriginPrioritiesSupplier { + + @NonNull private final ServerFlags mServerFlags; + + private ServerFlagsOriginPrioritiesSupplier(ServerFlags serverFlags) { + mServerFlags = Objects.requireNonNull(serverFlags); + } + + @Override + @Nullable + protected String[] lookupPriorityStrings() { + Optional priorityStrings = mServerFlags.getOptionalStringArray( + KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE); + return priorityStrings.orElse(null); + } + } +} diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java index 0f14af4b296b4..51a5f5636bb6c 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java @@ -67,7 +67,7 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { Handler handler = FgThread.getHandler(); ServiceConfigAccessor serviceConfigAccessor = - ServiceConfigAccessor.getInstance(context); + ServiceConfigAccessorImpl.getInstance(context); TimeDetectorStrategy timeDetectorStrategy = TimeDetectorStrategyImpl.create(context, handler, serviceConfigAccessor);