Loading services/core/java/com/android/server/timezonedetector/Environment.java 0 → 100644 +80 −0 Original line number Diff line number Diff line /* * Copyright 2025 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.timezonedetector; import android.annotation.CurrentTimeMillisLong; import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; import com.android.server.SystemTimeZone; import java.io.PrintWriter; /** * Used by the time zone detector code to interact with device state besides that available from * {@link ServiceConfigAccessor}. It can be faked for testing. */ public interface Environment { /** * Returns the device's currently configured time zone. May return an empty string. */ @NonNull String getDeviceTimeZone(); /** * Returns the confidence of the device's current time zone. */ @SystemTimeZone.TimeZoneConfidence int getDeviceTimeZoneConfidence(); /** * Sets the device's time zone, associated confidence, and records a debug log entry. */ void setDeviceTimeZoneAndConfidence( @NonNull String zoneId, @SystemTimeZone.TimeZoneConfidence int confidence, @NonNull String logInfo); /** * Returns the time according to the elapsed realtime clock, the same as {@link * android.os.SystemClock#elapsedRealtime()}. */ @ElapsedRealtimeLong long elapsedRealtimeMillis(); /** * Returns the current time in milliseconds, the same as * {@link java.lang.System#currentTimeMillis()}. */ @CurrentTimeMillisLong long currentTimeMillis(); /** * Adds a standalone entry to the time zone debug log. */ void addDebugLogEntry(@NonNull String logMsg); /** * Dumps the time zone debug log to the supplied {@link PrintWriter}. */ void dumpDebugLog(PrintWriter printWriter); /** * Requests that the supplied runnable be invoked asynchronously. */ void runAsync(@NonNull Runnable runnable); } services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java +8 −2 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.timezonedetector; import android.annotation.CurrentTimeMillisLong; import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; import android.os.Handler; Loading @@ -31,9 +32,9 @@ import java.io.PrintWriter; import java.util.Objects; /** * The real implementation of {@link TimeZoneDetectorStrategyImpl.Environment}. * The real implementation of {@link Environment}. */ final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Environment { final class EnvironmentImpl implements Environment { private static final String TIMEZONE_PROPERTY = "persist.sys.timezone"; Loading Loading @@ -68,6 +69,11 @@ final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Environment return SystemClock.elapsedRealtime(); } @Override public @CurrentTimeMillisLong long currentTimeMillis() { return System.currentTimeMillis(); } @Override public void addDebugLogEntry(@NonNull String logMsg) { SystemTimeZone.addDebugLogEntry(logMsg); Loading services/core/java/com/android/server/timezonedetector/NotifyingTimeZoneChangeListener.java +13 −7 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.ORIGI import android.annotation.DurationMillisLong; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.UserIdInt; import android.app.ActivityManagerInternal; Loading @@ -44,7 +45,6 @@ import android.icu.text.DateFormat; import android.icu.text.SimpleDateFormat; import android.icu.util.TimeZone; import android.os.Handler; import android.os.SystemClock; import android.os.UserHandle; import android.util.IndentingPrintWriter; import android.util.Log; Loading Loading @@ -153,6 +153,9 @@ public class NotifyingTimeZoneChangeListener implements TimeZoneChangeListener { } }; @NonNull private final Environment mEnvironment; private final Object mConfigurationLock = new Object(); @GuardedBy("mConfigurationLock") private ConfigurationInternal mConfigurationInternal; Loading @@ -170,12 +173,14 @@ public class NotifyingTimeZoneChangeListener implements TimeZoneChangeListener { /** Create and initialise a new {@code TimeZoneChangeTrackerImpl} */ @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL") public static NotifyingTimeZoneChangeListener create(Handler handler, Context context, ServiceConfigAccessor serviceConfigAccessor) { ServiceConfigAccessor serviceConfigAccessor, @NonNull Environment environment) { NotifyingTimeZoneChangeListener changeTracker = new NotifyingTimeZoneChangeListener(handler, context, serviceConfigAccessor, context.getSystemService(NotificationManager.class)); context.getSystemService(NotificationManager.class), environment); // Pretend there was an update to initialize configuration. changeTracker.handleConfigurationUpdate(); Loading @@ -184,9 +189,9 @@ public class NotifyingTimeZoneChangeListener implements TimeZoneChangeListener { } @VisibleForTesting NotifyingTimeZoneChangeListener( Handler handler, Context context, ServiceConfigAccessor serviceConfigAccessor, NotificationManager notificationManager) { NotifyingTimeZoneChangeListener(Handler handler, Context context, ServiceConfigAccessor serviceConfigAccessor, NotificationManager notificationManager, @NonNull Environment environment) { mHandler = Objects.requireNonNull(handler); mContext = Objects.requireNonNull(context); mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor); Loading @@ -194,6 +199,7 @@ public class NotifyingTimeZoneChangeListener implements TimeZoneChangeListener { this::handleConfigurationUpdate); mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); mNotificationManager = notificationManager; mEnvironment = Objects.requireNonNull(environment); } @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL") Loading Loading @@ -420,7 +426,7 @@ public class NotifyingTimeZoneChangeListener implements TimeZoneChangeListener { if (!changeEvent.getOldZoneId().equals(lastChangeEvent.getNewZoneId())) { int changeEventId = mNextChangeEventId.getAndIncrement(); TimeZoneChangeEvent syntheticChangeEvent = new TimeZoneChangeEvent( SystemClock.elapsedRealtime(), System.currentTimeMillis(), mEnvironment.elapsedRealtimeMillis(), mEnvironment.currentTimeMillis(), ORIGIN_UNKNOWN, UserHandle.USER_NULL, lastChangeEvent.getNewZoneId(), changeEvent.getOldZoneId(), 0, "Synthetic"); TimeZoneChangeRecord syntheticTrackedChangeEvent = Loading services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java +2 −53 Original line number Diff line number Diff line Loading @@ -25,7 +25,6 @@ import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_S import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH; import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_LOW; import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; Loading Loading @@ -54,7 +53,6 @@ import com.android.server.SystemTimeZone.TimeZoneConfidence; import com.android.server.flags.Flags; import com.android.server.timezonedetector.ConfigurationInternal.DetectionMode; import java.io.PrintWriter; import java.time.Duration; import java.util.ArrayList; import java.util.List; Loading @@ -67,55 +65,6 @@ import java.util.Objects; */ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrategy { /** * Used by {@link TimeZoneDetectorStrategyImpl} to interact with device state besides that * available from {@link #mServiceConfigAccessor}. It can be faked for testing. */ @VisibleForTesting public interface Environment { /** * Returns the device's currently configured time zone. May return an empty string. */ @NonNull String getDeviceTimeZone(); /** * Returns the confidence of the device's current time zone. */ @TimeZoneConfidence int getDeviceTimeZoneConfidence(); /** * Sets the device's time zone, associated confidence, and records a debug log entry. */ void setDeviceTimeZoneAndConfidence( @NonNull String zoneId, @TimeZoneConfidence int confidence, @NonNull String logInfo); /** * Returns the time according to the elapsed realtime clock, the same as {@link * android.os.SystemClock#elapsedRealtime()}. */ @ElapsedRealtimeLong long elapsedRealtimeMillis(); /** * Adds a standalone entry to the time zone debug log. */ void addDebugLogEntry(@NonNull String logMsg); /** * Dumps the time zone debug log to the supplied {@link PrintWriter}. */ void dumpDebugLog(PrintWriter printWriter); /** * Requests that the supplied runnable be invoked asynchronously. */ void runAsync(@NonNull Runnable runnable); } private static final String LOG_TAG = TimeZoneDetectorService.TAG; private static final boolean DBG = TimeZoneDetectorService.DBG; Loading Loading @@ -263,10 +212,10 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat public static TimeZoneDetectorStrategyImpl create( @NonNull Context context, @NonNull Handler handler, @NonNull ServiceConfigAccessor serviceConfigAccessor) { Environment environment = new EnvironmentImpl(handler); TimeZoneChangeListener changeEventTracker = NotifyingTimeZoneChangeListener.create(handler, context, serviceConfigAccessor); NotifyingTimeZoneChangeListener.create(handler, context, serviceConfigAccessor, environment); return new TimeZoneDetectorStrategyImpl( serviceConfigAccessor, environment, changeEventTracker); } Loading services/tests/timetests/src/com/android/server/timezonedetector/FakeEnvironment.java 0 → 100644 +141 −0 Original line number Diff line number Diff line /* * Copyright 2025 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.timezonedetector; import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_LOW; import android.annotation.CurrentTimeMillisLong; import android.annotation.ElapsedRealtimeLong; import com.android.server.SystemTimeZone; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; /** * A partially implemented, fake implementation of Environment for tests. */ public class FakeEnvironment implements Environment { private final TestState<String> mTimeZoneId = new TestState<>(); private final TestState<Integer> mTimeZoneConfidence = new TestState<>(); private final List<Runnable> mAsyncRunnables = new ArrayList<>(); private @ElapsedRealtimeLong long mElapsedRealtimeMillis; private @CurrentTimeMillisLong long mInitializationTimeMillis; FakeEnvironment() { // Ensure the fake environment starts with the defaults a fresh device would. initializeTimeZoneSetting("", TIME_ZONE_CONFIDENCE_LOW); } void initializeClock(@CurrentTimeMillisLong long currentTimeMillis, @ElapsedRealtimeLong long elapsedRealtimeMillis) { mInitializationTimeMillis = currentTimeMillis - elapsedRealtimeMillis; mElapsedRealtimeMillis = elapsedRealtimeMillis; } void initializeTimeZoneSetting(String zoneId, @SystemTimeZone.TimeZoneConfidence int timeZoneConfidence) { mTimeZoneId.init(zoneId); mTimeZoneConfidence.init(timeZoneConfidence); } void incrementClock() { mElapsedRealtimeMillis++; } @Override public String getDeviceTimeZone() { return mTimeZoneId.getLatest(); } @Override public int getDeviceTimeZoneConfidence() { return mTimeZoneConfidence.getLatest(); } @Override public void setDeviceTimeZoneAndConfidence( String zoneId, @SystemTimeZone.TimeZoneConfidence int confidence, String logInfo) { mTimeZoneId.set(zoneId); mTimeZoneConfidence.set(confidence); } void assertTimeZoneNotChanged() { mTimeZoneId.assertHasNotBeenSet(); mTimeZoneConfidence.assertHasNotBeenSet(); } void assertTimeZoneChangedTo(String timeZoneId, @SystemTimeZone.TimeZoneConfidence int confidence) { mTimeZoneId.assertHasBeenSet(); mTimeZoneId.assertChangeCount(1); mTimeZoneId.assertLatestEquals(timeZoneId); mTimeZoneConfidence.assertHasBeenSet(); mTimeZoneConfidence.assertChangeCount(1); mTimeZoneConfidence.assertLatestEquals(confidence); } void commitAllChanges() { mTimeZoneId.commitLatest(); mTimeZoneConfidence.commitLatest(); } @Override @ElapsedRealtimeLong public long elapsedRealtimeMillis() { return mElapsedRealtimeMillis; } @Override @CurrentTimeMillisLong public long currentTimeMillis() { return mInitializationTimeMillis + mElapsedRealtimeMillis; } @Override public void addDebugLogEntry(String logMsg) { // No-op for tests } @Override public void dumpDebugLog(PrintWriter printWriter) { // No-op for tests } /** * Adds the supplied runnable to a list but does not run them. To run all the runnables that * have been supplied, call {@code #runAsyncRunnables}. */ @Override public void runAsync(Runnable runnable) { mAsyncRunnables.add(runnable); } /** * Requests that the runnable that have been supplied to {@code #runAsync} are invoked * asynchronously and cleared. */ public void runAsyncRunnables() { for (Runnable runnable : mAsyncRunnables) { runnable.run(); } mAsyncRunnables.clear(); } } Loading
services/core/java/com/android/server/timezonedetector/Environment.java 0 → 100644 +80 −0 Original line number Diff line number Diff line /* * Copyright 2025 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.timezonedetector; import android.annotation.CurrentTimeMillisLong; import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; import com.android.server.SystemTimeZone; import java.io.PrintWriter; /** * Used by the time zone detector code to interact with device state besides that available from * {@link ServiceConfigAccessor}. It can be faked for testing. */ public interface Environment { /** * Returns the device's currently configured time zone. May return an empty string. */ @NonNull String getDeviceTimeZone(); /** * Returns the confidence of the device's current time zone. */ @SystemTimeZone.TimeZoneConfidence int getDeviceTimeZoneConfidence(); /** * Sets the device's time zone, associated confidence, and records a debug log entry. */ void setDeviceTimeZoneAndConfidence( @NonNull String zoneId, @SystemTimeZone.TimeZoneConfidence int confidence, @NonNull String logInfo); /** * Returns the time according to the elapsed realtime clock, the same as {@link * android.os.SystemClock#elapsedRealtime()}. */ @ElapsedRealtimeLong long elapsedRealtimeMillis(); /** * Returns the current time in milliseconds, the same as * {@link java.lang.System#currentTimeMillis()}. */ @CurrentTimeMillisLong long currentTimeMillis(); /** * Adds a standalone entry to the time zone debug log. */ void addDebugLogEntry(@NonNull String logMsg); /** * Dumps the time zone debug log to the supplied {@link PrintWriter}. */ void dumpDebugLog(PrintWriter printWriter); /** * Requests that the supplied runnable be invoked asynchronously. */ void runAsync(@NonNull Runnable runnable); }
services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java +8 −2 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.timezonedetector; import android.annotation.CurrentTimeMillisLong; import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; import android.os.Handler; Loading @@ -31,9 +32,9 @@ import java.io.PrintWriter; import java.util.Objects; /** * The real implementation of {@link TimeZoneDetectorStrategyImpl.Environment}. * The real implementation of {@link Environment}. */ final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Environment { final class EnvironmentImpl implements Environment { private static final String TIMEZONE_PROPERTY = "persist.sys.timezone"; Loading Loading @@ -68,6 +69,11 @@ final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Environment return SystemClock.elapsedRealtime(); } @Override public @CurrentTimeMillisLong long currentTimeMillis() { return System.currentTimeMillis(); } @Override public void addDebugLogEntry(@NonNull String logMsg) { SystemTimeZone.addDebugLogEntry(logMsg); Loading
services/core/java/com/android/server/timezonedetector/NotifyingTimeZoneChangeListener.java +13 −7 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.ORIGI import android.annotation.DurationMillisLong; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.UserIdInt; import android.app.ActivityManagerInternal; Loading @@ -44,7 +45,6 @@ import android.icu.text.DateFormat; import android.icu.text.SimpleDateFormat; import android.icu.util.TimeZone; import android.os.Handler; import android.os.SystemClock; import android.os.UserHandle; import android.util.IndentingPrintWriter; import android.util.Log; Loading Loading @@ -153,6 +153,9 @@ public class NotifyingTimeZoneChangeListener implements TimeZoneChangeListener { } }; @NonNull private final Environment mEnvironment; private final Object mConfigurationLock = new Object(); @GuardedBy("mConfigurationLock") private ConfigurationInternal mConfigurationInternal; Loading @@ -170,12 +173,14 @@ public class NotifyingTimeZoneChangeListener implements TimeZoneChangeListener { /** Create and initialise a new {@code TimeZoneChangeTrackerImpl} */ @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL") public static NotifyingTimeZoneChangeListener create(Handler handler, Context context, ServiceConfigAccessor serviceConfigAccessor) { ServiceConfigAccessor serviceConfigAccessor, @NonNull Environment environment) { NotifyingTimeZoneChangeListener changeTracker = new NotifyingTimeZoneChangeListener(handler, context, serviceConfigAccessor, context.getSystemService(NotificationManager.class)); context.getSystemService(NotificationManager.class), environment); // Pretend there was an update to initialize configuration. changeTracker.handleConfigurationUpdate(); Loading @@ -184,9 +189,9 @@ public class NotifyingTimeZoneChangeListener implements TimeZoneChangeListener { } @VisibleForTesting NotifyingTimeZoneChangeListener( Handler handler, Context context, ServiceConfigAccessor serviceConfigAccessor, NotificationManager notificationManager) { NotifyingTimeZoneChangeListener(Handler handler, Context context, ServiceConfigAccessor serviceConfigAccessor, NotificationManager notificationManager, @NonNull Environment environment) { mHandler = Objects.requireNonNull(handler); mContext = Objects.requireNonNull(context); mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor); Loading @@ -194,6 +199,7 @@ public class NotifyingTimeZoneChangeListener implements TimeZoneChangeListener { this::handleConfigurationUpdate); mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); mNotificationManager = notificationManager; mEnvironment = Objects.requireNonNull(environment); } @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL") Loading Loading @@ -420,7 +426,7 @@ public class NotifyingTimeZoneChangeListener implements TimeZoneChangeListener { if (!changeEvent.getOldZoneId().equals(lastChangeEvent.getNewZoneId())) { int changeEventId = mNextChangeEventId.getAndIncrement(); TimeZoneChangeEvent syntheticChangeEvent = new TimeZoneChangeEvent( SystemClock.elapsedRealtime(), System.currentTimeMillis(), mEnvironment.elapsedRealtimeMillis(), mEnvironment.currentTimeMillis(), ORIGIN_UNKNOWN, UserHandle.USER_NULL, lastChangeEvent.getNewZoneId(), changeEvent.getOldZoneId(), 0, "Synthetic"); TimeZoneChangeRecord syntheticTrackedChangeEvent = Loading
services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java +2 −53 Original line number Diff line number Diff line Loading @@ -25,7 +25,6 @@ import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_S import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH; import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_LOW; import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; Loading Loading @@ -54,7 +53,6 @@ import com.android.server.SystemTimeZone.TimeZoneConfidence; import com.android.server.flags.Flags; import com.android.server.timezonedetector.ConfigurationInternal.DetectionMode; import java.io.PrintWriter; import java.time.Duration; import java.util.ArrayList; import java.util.List; Loading @@ -67,55 +65,6 @@ import java.util.Objects; */ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrategy { /** * Used by {@link TimeZoneDetectorStrategyImpl} to interact with device state besides that * available from {@link #mServiceConfigAccessor}. It can be faked for testing. */ @VisibleForTesting public interface Environment { /** * Returns the device's currently configured time zone. May return an empty string. */ @NonNull String getDeviceTimeZone(); /** * Returns the confidence of the device's current time zone. */ @TimeZoneConfidence int getDeviceTimeZoneConfidence(); /** * Sets the device's time zone, associated confidence, and records a debug log entry. */ void setDeviceTimeZoneAndConfidence( @NonNull String zoneId, @TimeZoneConfidence int confidence, @NonNull String logInfo); /** * Returns the time according to the elapsed realtime clock, the same as {@link * android.os.SystemClock#elapsedRealtime()}. */ @ElapsedRealtimeLong long elapsedRealtimeMillis(); /** * Adds a standalone entry to the time zone debug log. */ void addDebugLogEntry(@NonNull String logMsg); /** * Dumps the time zone debug log to the supplied {@link PrintWriter}. */ void dumpDebugLog(PrintWriter printWriter); /** * Requests that the supplied runnable be invoked asynchronously. */ void runAsync(@NonNull Runnable runnable); } private static final String LOG_TAG = TimeZoneDetectorService.TAG; private static final boolean DBG = TimeZoneDetectorService.DBG; Loading Loading @@ -263,10 +212,10 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat public static TimeZoneDetectorStrategyImpl create( @NonNull Context context, @NonNull Handler handler, @NonNull ServiceConfigAccessor serviceConfigAccessor) { Environment environment = new EnvironmentImpl(handler); TimeZoneChangeListener changeEventTracker = NotifyingTimeZoneChangeListener.create(handler, context, serviceConfigAccessor); NotifyingTimeZoneChangeListener.create(handler, context, serviceConfigAccessor, environment); return new TimeZoneDetectorStrategyImpl( serviceConfigAccessor, environment, changeEventTracker); } Loading
services/tests/timetests/src/com/android/server/timezonedetector/FakeEnvironment.java 0 → 100644 +141 −0 Original line number Diff line number Diff line /* * Copyright 2025 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.timezonedetector; import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_LOW; import android.annotation.CurrentTimeMillisLong; import android.annotation.ElapsedRealtimeLong; import com.android.server.SystemTimeZone; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; /** * A partially implemented, fake implementation of Environment for tests. */ public class FakeEnvironment implements Environment { private final TestState<String> mTimeZoneId = new TestState<>(); private final TestState<Integer> mTimeZoneConfidence = new TestState<>(); private final List<Runnable> mAsyncRunnables = new ArrayList<>(); private @ElapsedRealtimeLong long mElapsedRealtimeMillis; private @CurrentTimeMillisLong long mInitializationTimeMillis; FakeEnvironment() { // Ensure the fake environment starts with the defaults a fresh device would. initializeTimeZoneSetting("", TIME_ZONE_CONFIDENCE_LOW); } void initializeClock(@CurrentTimeMillisLong long currentTimeMillis, @ElapsedRealtimeLong long elapsedRealtimeMillis) { mInitializationTimeMillis = currentTimeMillis - elapsedRealtimeMillis; mElapsedRealtimeMillis = elapsedRealtimeMillis; } void initializeTimeZoneSetting(String zoneId, @SystemTimeZone.TimeZoneConfidence int timeZoneConfidence) { mTimeZoneId.init(zoneId); mTimeZoneConfidence.init(timeZoneConfidence); } void incrementClock() { mElapsedRealtimeMillis++; } @Override public String getDeviceTimeZone() { return mTimeZoneId.getLatest(); } @Override public int getDeviceTimeZoneConfidence() { return mTimeZoneConfidence.getLatest(); } @Override public void setDeviceTimeZoneAndConfidence( String zoneId, @SystemTimeZone.TimeZoneConfidence int confidence, String logInfo) { mTimeZoneId.set(zoneId); mTimeZoneConfidence.set(confidence); } void assertTimeZoneNotChanged() { mTimeZoneId.assertHasNotBeenSet(); mTimeZoneConfidence.assertHasNotBeenSet(); } void assertTimeZoneChangedTo(String timeZoneId, @SystemTimeZone.TimeZoneConfidence int confidence) { mTimeZoneId.assertHasBeenSet(); mTimeZoneId.assertChangeCount(1); mTimeZoneId.assertLatestEquals(timeZoneId); mTimeZoneConfidence.assertHasBeenSet(); mTimeZoneConfidence.assertChangeCount(1); mTimeZoneConfidence.assertLatestEquals(confidence); } void commitAllChanges() { mTimeZoneId.commitLatest(); mTimeZoneConfidence.commitLatest(); } @Override @ElapsedRealtimeLong public long elapsedRealtimeMillis() { return mElapsedRealtimeMillis; } @Override @CurrentTimeMillisLong public long currentTimeMillis() { return mInitializationTimeMillis + mElapsedRealtimeMillis; } @Override public void addDebugLogEntry(String logMsg) { // No-op for tests } @Override public void dumpDebugLog(PrintWriter printWriter) { // No-op for tests } /** * Adds the supplied runnable to a list but does not run them. To run all the runnables that * have been supplied, call {@code #runAsyncRunnables}. */ @Override public void runAsync(Runnable runnable) { mAsyncRunnables.add(runnable); } /** * Requests that the runnable that have been supplied to {@code #runAsync} are invoked * asynchronously and cleared. */ public void runAsyncRunnables() { for (Runnable runnable : mAsyncRunnables) { runnable.run(); } mAsyncRunnables.clear(); } }