Loading services/core/java/com/android/server/timedetector/EnvironmentImpl.java +50 −61 Original line number Diff line number Diff line Loading @@ -16,26 +16,22 @@ package com.android.server.timedetector; import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_NETWORK; import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_TELEPHONY; import static com.android.server.timedetector.TimeDetectorStrategy.stringToOrigin; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.AlarmManager; import android.content.ContentResolver; import android.content.Context; import android.os.Build; import android.database.ContentObserver; import android.os.Handler; import android.os.PowerManager; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.util.Slog; import com.android.internal.R; import com.android.server.timedetector.TimeDetectorStrategy.Origin; import com.android.internal.annotations.GuardedBy; import com.android.server.timezonedetector.ConfigurationChangeListener; import java.time.Instant; import java.util.Objects; Loading @@ -43,62 +39,71 @@ import java.util.Objects; /** * The real implementation of {@link TimeDetectorStrategyImpl.Environment} used on device. */ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environment { private static final String TAG = TimeDetectorService.TAG; private static final int SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT = 2 * 1000; /** * Time in the past. If automatic time suggestion is before this point, it's * incorrect for sure. */ private static final Instant TIME_LOWER_BOUND = Instant.ofEpochMilli( Long.max(android.os.Environment.getRootDirectory().lastModified(), Build.TIME)); /** * 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 }; final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environment { /** * 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 static final String LOG_TAG = TimeDetectorService.TAG; @NonNull private final Context mContext; @NonNull private final Handler mHandler; @NonNull private final ServiceConfigAccessor mServiceConfigAccessor; @NonNull private final ContentResolver mContentResolver; @NonNull private final PowerManager.WakeLock mWakeLock; @NonNull private final AlarmManager mAlarmManager; @NonNull private final UserManager mUserManager; @NonNull private final int[] mOriginPriorities; public EnvironmentImpl(@NonNull Context context) { // @NonNull after setConfigChangeListener() is called. @GuardedBy("this") private ConfigurationChangeListener mConfigChangeListener; EnvironmentImpl(@NonNull Context context, @NonNull Handler handler, @NonNull ServiceConfigAccessor serviceConfigAccessor) { mContext = Objects.requireNonNull(context); mContentResolver = Objects.requireNonNull(context.getContentResolver()); mHandler = Objects.requireNonNull(handler); mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor); PowerManager powerManager = context.getSystemService(PowerManager.class); mWakeLock = Objects.requireNonNull( powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG)); powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG)); mAlarmManager = Objects.requireNonNull(context.getSystemService(AlarmManager.class)); mUserManager = Objects.requireNonNull(context.getSystemService(UserManager.class)); mSystemClockUpdateThresholdMillis = SystemProperties.getInt("ro.sys.time_detector_update_diff", SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT); // Wire up the config change listeners. All invocations are performed on the mHandler // thread. mOriginPriorities = getOriginPriorities(context); ContentResolver contentResolver = context.getContentResolver(); contentResolver.registerContentObserver( Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true, new ContentObserver(mHandler) { @Override public void onChange(boolean selfChange) { handleAutoTimeDetectionChangedOnHandlerThread(); } }); } /** Internal method for handling the auto time setting being changed. */ private void handleAutoTimeDetectionChangedOnHandlerThread() { synchronized (this) { if (mConfigChangeListener == null) { Slog.wtf(LOG_TAG, "mConfigChangeListener is unexpectedly null"); } mConfigChangeListener.onChange(); } } @Override public void setConfigChangeListener(@NonNull ConfigurationChangeListener listener) { synchronized (this) { mConfigChangeListener = Objects.requireNonNull(listener); } } @Override public int systemClockUpdateThresholdMillis() { return mSystemClockUpdateThresholdMillis; return mServiceConfigAccessor.systemClockUpdateThresholdMillis(); } @Override Loading @@ -112,12 +117,12 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme @Override public Instant autoTimeLowerBound() { return TIME_LOWER_BOUND; return mServiceConfigAccessor.autoTimeLowerBound(); } @Override public int[] autoOriginPriorities() { return mOriginPriorities; return mServiceConfigAccessor.getOriginPriorities(); } @Override Loading @@ -131,7 +136,7 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme @Override public void acquireWakeLock() { if (mWakeLock.isHeld()) { Slog.wtf(TAG, "WakeLock " + mWakeLock + " already held"); Slog.wtf(LOG_TAG, "WakeLock " + mWakeLock + " already held"); } mWakeLock.acquire(); } Loading Loading @@ -160,7 +165,7 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme private void checkWakeLockHeld() { if (!mWakeLock.isHeld()) { Slog.wtf(TAG, "WakeLock " + mWakeLock + " not held"); Slog.wtf(LOG_TAG, "WakeLock " + mWakeLock + " not held"); } } Loading @@ -168,20 +173,4 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme UserHandle userHandle = UserHandle.of(userId); return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_DATE_TIME, userHandle); } private static int[] getOriginPriorities(@NonNull Context context) { String[] originStrings = context.getResources().getStringArray(R.array.config_autoTimeSourcesPriority); if (originStrings.length == 0) { return DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES; } else { int[] origins = new int[originStrings.length]; for (int i = 0; i < originStrings.length; i++) { int origin = stringToOrigin(originStrings[i]); origins[i] = origin; } return origins; } } } services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java 0 → 100644 +141 −0 Original line number Diff line number Diff line /* * 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.TimeDetectorStrategy.ORIGIN_NETWORK; import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_TELEPHONY; import static com.android.server.timedetector.TimeDetectorStrategy.stringToOrigin; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.os.Build; import android.os.SystemProperties; import android.util.ArraySet; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.server.timezonedetector.ConfigurationChangeListener; import java.time.Instant; import java.util.Collections; import java.util.Objects; import java.util.Set; /** * 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. */ final class 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 @TimeDetectorStrategy.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)); private static final Set<String> SERVER_FLAGS_KEYS_TO_WATCH = Collections.unmodifiableSet( new ArraySet<>(new String[] { })); 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 int[] mOriginPriorities; /** * 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); mOriginPriorities = getOriginPrioritiesInternal(); 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. * * <p>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 int[] getOriginPriorities() { return mOriginPriorities; } int systemClockUpdateThresholdMillis() { return mSystemClockUpdateThresholdMillis; } Instant autoTimeLowerBound() { return TIME_LOWER_BOUND_DEFAULT; } private int[] getOriginPrioritiesInternal() { String[] originStrings = mContext.getResources().getStringArray(R.array.config_autoTimeSourcesPriority); if (originStrings.length == 0) { return DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES; } else { int[] origins = new int[originStrings.length]; for (int i = 0; i < originStrings.length; i++) { int origin = stringToOrigin(originStrings[i]); origins[i] = origin; } return origins; } } } services/core/java/com/android/server/timedetector/TimeDetectorService.java +10 −32 Original line number Diff line number Diff line Loading @@ -27,12 +27,9 @@ import android.app.timedetector.ITimeDetectorService; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.TelephonyTimeSuggestion; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; import android.os.Binder; import android.os.Handler; import android.provider.Settings; import android.util.IndentingPrintWriter; import com.android.internal.annotations.VisibleForTesting; Loading Loading @@ -64,7 +61,16 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { @Override public void onStart() { TimeDetectorService service = TimeDetectorService.create(getContext()); Context context = getContext(); Handler handler = FgThread.getHandler(); ServiceConfigAccessor serviceConfigAccessor = ServiceConfigAccessor.getInstance(context); TimeDetectorStrategy timeDetectorStrategy = TimeDetectorStrategyImpl.create(context, handler, serviceConfigAccessor); TimeDetectorService service = new TimeDetectorService(context, handler, timeDetectorStrategy); // Publish the binder service so it can be accessed from other (appropriately // permissioned) processes. Loading @@ -77,28 +83,6 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { @NonNull private final TimeDetectorStrategy mTimeDetectorStrategy; @NonNull private final CallerIdentityInjector mCallerIdentityInjector; private static TimeDetectorService create(@NonNull Context context) { TimeDetectorStrategyImpl.Environment environment = new EnvironmentImpl(context); TimeDetectorStrategy timeDetectorStrategy = new TimeDetectorStrategyImpl(environment); Handler handler = FgThread.getHandler(); TimeDetectorService timeDetectorService = new TimeDetectorService(context, handler, timeDetectorStrategy); // Wire up event listening. ContentResolver contentResolver = context.getContentResolver(); contentResolver.registerContentObserver( Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true, new ContentObserver(handler) { @Override public void onChange(boolean selfChange) { timeDetectorService.handleAutoTimeDetectionChanged(); } }); return timeDetectorService; } @VisibleForTesting public TimeDetectorService(@NonNull Context context, @NonNull Handler handler, @NonNull TimeDetectorStrategy timeDetectorStrategy) { Loading Loading @@ -185,12 +169,6 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { mHandler.post(() -> mTimeDetectorStrategy.suggestExternalTime(timeSignal)); } /** Internal method for handling the auto time setting being changed. */ @VisibleForTesting public void handleAutoTimeDetectionChanged() { mHandler.post(mTimeDetectorStrategy::handleAutoTimeConfigChanged); } @Override protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @Nullable String[] args) { Loading services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java +0 −6 Original line number Diff line number Diff line Loading @@ -92,12 +92,6 @@ public interface TimeDetectorStrategy extends Dumpable { /** Returns the configuration that controls time detector behaviour for specified user. */ ConfigurationInternal getConfigurationInternal(@UserIdInt int userId); /** * Handles the auto-time configuration changing For example, when the auto-time setting is * toggled on or off. */ void handleAutoTimeConfigChanged(); // Utility methods below are to be moved to a better home when one becomes more obvious. /** Loading services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java +21 −2 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ import android.app.timedetector.GnssTimeSuggestion; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.TelephonyTimeSuggestion; import android.content.Context; import android.os.Handler; import android.os.TimestampedValue; import android.util.IndentingPrintWriter; import android.util.LocalLog; Loading @@ -37,6 +39,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.timezonedetector.ArrayMapWithHistory; import com.android.server.timezonedetector.ConfigurationChangeListener; import com.android.server.timezonedetector.ReferenceWithHistory; import java.time.Instant; Loading Loading @@ -131,6 +134,12 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { */ public interface Environment { /** * Sets a {@link ConfigurationChangeListener} that will be invoked when there are any * changes that could affect time detection. This is invoked during system server setup. */ void setConfigChangeListener(@NonNull ConfigurationChangeListener listener); /** * 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 Loading Loading @@ -178,8 +187,19 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { void releaseWakeLock(); } static TimeDetectorStrategy create( @NonNull Context context, @NonNull Handler handler, @NonNull ServiceConfigAccessor serviceConfigAccessor) { TimeDetectorStrategyImpl.Environment environment = new EnvironmentImpl(context, handler, serviceConfigAccessor); return new TimeDetectorStrategyImpl(environment); } @VisibleForTesting TimeDetectorStrategyImpl(@NonNull Environment environment) { mEnvironment = Objects.requireNonNull(environment); mEnvironment.setConfigChangeListener(this::handleAutoTimeConfigChanged); } @Override Loading Loading @@ -279,8 +299,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { return mEnvironment.configurationInternal(userId); } @Override public synchronized void handleAutoTimeConfigChanged() { private synchronized void handleAutoTimeConfigChanged() { boolean enabled = mEnvironment.isAutoTimeDetectionEnabled(); // When automatic time detection is enabled we update the system clock instantly if we can. // Conversely, when automatic time detection is disabled we leave the clock as it is. Loading Loading
services/core/java/com/android/server/timedetector/EnvironmentImpl.java +50 −61 Original line number Diff line number Diff line Loading @@ -16,26 +16,22 @@ package com.android.server.timedetector; import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_NETWORK; import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_TELEPHONY; import static com.android.server.timedetector.TimeDetectorStrategy.stringToOrigin; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.AlarmManager; import android.content.ContentResolver; import android.content.Context; import android.os.Build; import android.database.ContentObserver; import android.os.Handler; import android.os.PowerManager; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.util.Slog; import com.android.internal.R; import com.android.server.timedetector.TimeDetectorStrategy.Origin; import com.android.internal.annotations.GuardedBy; import com.android.server.timezonedetector.ConfigurationChangeListener; import java.time.Instant; import java.util.Objects; Loading @@ -43,62 +39,71 @@ import java.util.Objects; /** * The real implementation of {@link TimeDetectorStrategyImpl.Environment} used on device. */ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environment { private static final String TAG = TimeDetectorService.TAG; private static final int SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT = 2 * 1000; /** * Time in the past. If automatic time suggestion is before this point, it's * incorrect for sure. */ private static final Instant TIME_LOWER_BOUND = Instant.ofEpochMilli( Long.max(android.os.Environment.getRootDirectory().lastModified(), Build.TIME)); /** * 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 }; final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environment { /** * 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 static final String LOG_TAG = TimeDetectorService.TAG; @NonNull private final Context mContext; @NonNull private final Handler mHandler; @NonNull private final ServiceConfigAccessor mServiceConfigAccessor; @NonNull private final ContentResolver mContentResolver; @NonNull private final PowerManager.WakeLock mWakeLock; @NonNull private final AlarmManager mAlarmManager; @NonNull private final UserManager mUserManager; @NonNull private final int[] mOriginPriorities; public EnvironmentImpl(@NonNull Context context) { // @NonNull after setConfigChangeListener() is called. @GuardedBy("this") private ConfigurationChangeListener mConfigChangeListener; EnvironmentImpl(@NonNull Context context, @NonNull Handler handler, @NonNull ServiceConfigAccessor serviceConfigAccessor) { mContext = Objects.requireNonNull(context); mContentResolver = Objects.requireNonNull(context.getContentResolver()); mHandler = Objects.requireNonNull(handler); mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor); PowerManager powerManager = context.getSystemService(PowerManager.class); mWakeLock = Objects.requireNonNull( powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG)); powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG)); mAlarmManager = Objects.requireNonNull(context.getSystemService(AlarmManager.class)); mUserManager = Objects.requireNonNull(context.getSystemService(UserManager.class)); mSystemClockUpdateThresholdMillis = SystemProperties.getInt("ro.sys.time_detector_update_diff", SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT); // Wire up the config change listeners. All invocations are performed on the mHandler // thread. mOriginPriorities = getOriginPriorities(context); ContentResolver contentResolver = context.getContentResolver(); contentResolver.registerContentObserver( Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true, new ContentObserver(mHandler) { @Override public void onChange(boolean selfChange) { handleAutoTimeDetectionChangedOnHandlerThread(); } }); } /** Internal method for handling the auto time setting being changed. */ private void handleAutoTimeDetectionChangedOnHandlerThread() { synchronized (this) { if (mConfigChangeListener == null) { Slog.wtf(LOG_TAG, "mConfigChangeListener is unexpectedly null"); } mConfigChangeListener.onChange(); } } @Override public void setConfigChangeListener(@NonNull ConfigurationChangeListener listener) { synchronized (this) { mConfigChangeListener = Objects.requireNonNull(listener); } } @Override public int systemClockUpdateThresholdMillis() { return mSystemClockUpdateThresholdMillis; return mServiceConfigAccessor.systemClockUpdateThresholdMillis(); } @Override Loading @@ -112,12 +117,12 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme @Override public Instant autoTimeLowerBound() { return TIME_LOWER_BOUND; return mServiceConfigAccessor.autoTimeLowerBound(); } @Override public int[] autoOriginPriorities() { return mOriginPriorities; return mServiceConfigAccessor.getOriginPriorities(); } @Override Loading @@ -131,7 +136,7 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme @Override public void acquireWakeLock() { if (mWakeLock.isHeld()) { Slog.wtf(TAG, "WakeLock " + mWakeLock + " already held"); Slog.wtf(LOG_TAG, "WakeLock " + mWakeLock + " already held"); } mWakeLock.acquire(); } Loading Loading @@ -160,7 +165,7 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme private void checkWakeLockHeld() { if (!mWakeLock.isHeld()) { Slog.wtf(TAG, "WakeLock " + mWakeLock + " not held"); Slog.wtf(LOG_TAG, "WakeLock " + mWakeLock + " not held"); } } Loading @@ -168,20 +173,4 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme UserHandle userHandle = UserHandle.of(userId); return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_DATE_TIME, userHandle); } private static int[] getOriginPriorities(@NonNull Context context) { String[] originStrings = context.getResources().getStringArray(R.array.config_autoTimeSourcesPriority); if (originStrings.length == 0) { return DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES; } else { int[] origins = new int[originStrings.length]; for (int i = 0; i < originStrings.length; i++) { int origin = stringToOrigin(originStrings[i]); origins[i] = origin; } return origins; } } }
services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java 0 → 100644 +141 −0 Original line number Diff line number Diff line /* * 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.TimeDetectorStrategy.ORIGIN_NETWORK; import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_TELEPHONY; import static com.android.server.timedetector.TimeDetectorStrategy.stringToOrigin; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.os.Build; import android.os.SystemProperties; import android.util.ArraySet; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.server.timezonedetector.ConfigurationChangeListener; import java.time.Instant; import java.util.Collections; import java.util.Objects; import java.util.Set; /** * 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. */ final class 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 @TimeDetectorStrategy.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)); private static final Set<String> SERVER_FLAGS_KEYS_TO_WATCH = Collections.unmodifiableSet( new ArraySet<>(new String[] { })); 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 int[] mOriginPriorities; /** * 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); mOriginPriorities = getOriginPrioritiesInternal(); 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. * * <p>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 int[] getOriginPriorities() { return mOriginPriorities; } int systemClockUpdateThresholdMillis() { return mSystemClockUpdateThresholdMillis; } Instant autoTimeLowerBound() { return TIME_LOWER_BOUND_DEFAULT; } private int[] getOriginPrioritiesInternal() { String[] originStrings = mContext.getResources().getStringArray(R.array.config_autoTimeSourcesPriority); if (originStrings.length == 0) { return DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES; } else { int[] origins = new int[originStrings.length]; for (int i = 0; i < originStrings.length; i++) { int origin = stringToOrigin(originStrings[i]); origins[i] = origin; } return origins; } } }
services/core/java/com/android/server/timedetector/TimeDetectorService.java +10 −32 Original line number Diff line number Diff line Loading @@ -27,12 +27,9 @@ import android.app.timedetector.ITimeDetectorService; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.TelephonyTimeSuggestion; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; import android.os.Binder; import android.os.Handler; import android.provider.Settings; import android.util.IndentingPrintWriter; import com.android.internal.annotations.VisibleForTesting; Loading Loading @@ -64,7 +61,16 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { @Override public void onStart() { TimeDetectorService service = TimeDetectorService.create(getContext()); Context context = getContext(); Handler handler = FgThread.getHandler(); ServiceConfigAccessor serviceConfigAccessor = ServiceConfigAccessor.getInstance(context); TimeDetectorStrategy timeDetectorStrategy = TimeDetectorStrategyImpl.create(context, handler, serviceConfigAccessor); TimeDetectorService service = new TimeDetectorService(context, handler, timeDetectorStrategy); // Publish the binder service so it can be accessed from other (appropriately // permissioned) processes. Loading @@ -77,28 +83,6 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { @NonNull private final TimeDetectorStrategy mTimeDetectorStrategy; @NonNull private final CallerIdentityInjector mCallerIdentityInjector; private static TimeDetectorService create(@NonNull Context context) { TimeDetectorStrategyImpl.Environment environment = new EnvironmentImpl(context); TimeDetectorStrategy timeDetectorStrategy = new TimeDetectorStrategyImpl(environment); Handler handler = FgThread.getHandler(); TimeDetectorService timeDetectorService = new TimeDetectorService(context, handler, timeDetectorStrategy); // Wire up event listening. ContentResolver contentResolver = context.getContentResolver(); contentResolver.registerContentObserver( Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true, new ContentObserver(handler) { @Override public void onChange(boolean selfChange) { timeDetectorService.handleAutoTimeDetectionChanged(); } }); return timeDetectorService; } @VisibleForTesting public TimeDetectorService(@NonNull Context context, @NonNull Handler handler, @NonNull TimeDetectorStrategy timeDetectorStrategy) { Loading Loading @@ -185,12 +169,6 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { mHandler.post(() -> mTimeDetectorStrategy.suggestExternalTime(timeSignal)); } /** Internal method for handling the auto time setting being changed. */ @VisibleForTesting public void handleAutoTimeDetectionChanged() { mHandler.post(mTimeDetectorStrategy::handleAutoTimeConfigChanged); } @Override protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @Nullable String[] args) { Loading
services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java +0 −6 Original line number Diff line number Diff line Loading @@ -92,12 +92,6 @@ public interface TimeDetectorStrategy extends Dumpable { /** Returns the configuration that controls time detector behaviour for specified user. */ ConfigurationInternal getConfigurationInternal(@UserIdInt int userId); /** * Handles the auto-time configuration changing For example, when the auto-time setting is * toggled on or off. */ void handleAutoTimeConfigChanged(); // Utility methods below are to be moved to a better home when one becomes more obvious. /** Loading
services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java +21 −2 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ import android.app.timedetector.GnssTimeSuggestion; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.TelephonyTimeSuggestion; import android.content.Context; import android.os.Handler; import android.os.TimestampedValue; import android.util.IndentingPrintWriter; import android.util.LocalLog; Loading @@ -37,6 +39,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.timezonedetector.ArrayMapWithHistory; import com.android.server.timezonedetector.ConfigurationChangeListener; import com.android.server.timezonedetector.ReferenceWithHistory; import java.time.Instant; Loading Loading @@ -131,6 +134,12 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { */ public interface Environment { /** * Sets a {@link ConfigurationChangeListener} that will be invoked when there are any * changes that could affect time detection. This is invoked during system server setup. */ void setConfigChangeListener(@NonNull ConfigurationChangeListener listener); /** * 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 Loading Loading @@ -178,8 +187,19 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { void releaseWakeLock(); } static TimeDetectorStrategy create( @NonNull Context context, @NonNull Handler handler, @NonNull ServiceConfigAccessor serviceConfigAccessor) { TimeDetectorStrategyImpl.Environment environment = new EnvironmentImpl(context, handler, serviceConfigAccessor); return new TimeDetectorStrategyImpl(environment); } @VisibleForTesting TimeDetectorStrategyImpl(@NonNull Environment environment) { mEnvironment = Objects.requireNonNull(environment); mEnvironment.setConfigChangeListener(this::handleAutoTimeConfigChanged); } @Override Loading Loading @@ -279,8 +299,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { return mEnvironment.configurationInternal(userId); } @Override public synchronized void handleAutoTimeConfigChanged() { private synchronized void handleAutoTimeConfigChanged() { boolean enabled = mEnvironment.isAutoTimeDetectionEnabled(); // When automatic time detection is enabled we update the system clock instantly if we can. // Conversely, when automatic time detection is disabled we leave the clock as it is. Loading