Merge "Add a new NetworkTimeHelper impl"
This commit is contained in:
@@ -31,6 +31,14 @@ import java.io.PrintWriter;
|
||||
*/
|
||||
abstract class NetworkTimeHelper {
|
||||
|
||||
/**
|
||||
* This compile-time value can be changed to switch between new and old ways to obtain network
|
||||
* time for GNSS. If you have to turn this from {@code true} to {@code false} then please create
|
||||
* a platform bug. This switch will be removed in a future release. If there are problems with
|
||||
* the new impl we'd like to hear about them.
|
||||
*/
|
||||
static final boolean USE_TIME_DETECTOR_IMPL = false;
|
||||
|
||||
/**
|
||||
* The callback interface used by {@link NetworkTimeHelper} to report the time to {@link
|
||||
* GnssLocationProvider}. The callback can happen at any time using the thread associated with
|
||||
@@ -47,7 +55,13 @@ abstract class NetworkTimeHelper {
|
||||
static NetworkTimeHelper create(
|
||||
@NonNull Context context, @NonNull Looper looper,
|
||||
@NonNull InjectTimeCallback injectTimeCallback) {
|
||||
return new NtpNetworkTimeHelper(context, looper, injectTimeCallback);
|
||||
if (USE_TIME_DETECTOR_IMPL) {
|
||||
TimeDetectorNetworkTimeHelper.Environment environment =
|
||||
new TimeDetectorNetworkTimeHelper.EnvironmentImpl(looper);
|
||||
return new TimeDetectorNetworkTimeHelper(environment, injectTimeCallback);
|
||||
} else {
|
||||
return new NtpNetworkTimeHelper(context, looper, injectTimeCallback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,7 +88,9 @@ abstract class NetworkTimeHelper {
|
||||
* Notifies that network connectivity has been established.
|
||||
*
|
||||
* <p>Called by {@link GnssLocationProvider} when the device establishes a data network
|
||||
* connection.
|
||||
* connection. This call should be removed eventually because it should be handled by the {@link
|
||||
* NetworkTimeHelper} implementation itself, but has been retained for compatibility while
|
||||
* switching implementations.
|
||||
*/
|
||||
abstract void onNetworkAvailable();
|
||||
|
||||
@@ -82,4 +98,5 @@ abstract class NetworkTimeHelper {
|
||||
* Dumps internal state during bugreports useful for debugging.
|
||||
*/
|
||||
abstract void dump(@NonNull PrintWriter pw);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
* 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 com.android.server.location.gnss;
|
||||
|
||||
import android.annotation.DurationMillisLong;
|
||||
import android.annotation.ElapsedRealtimeLong;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.time.UnixEpochTime;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.SystemClock;
|
||||
import android.util.IndentingPrintWriter;
|
||||
import android.util.LocalLog;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.timedetector.NetworkTimeSuggestion;
|
||||
import com.android.server.timedetector.TimeDetectorInternal;
|
||||
import com.android.server.timezonedetector.StateChangeListener;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Handles injecting network time to GNSS by using information from the platform time detector.
|
||||
*/
|
||||
public class TimeDetectorNetworkTimeHelper extends NetworkTimeHelper {
|
||||
|
||||
/** Returns {@code true} if the TimeDetectorNetworkTimeHelper is being used. */
|
||||
public static boolean isInUse() {
|
||||
return NetworkTimeHelper.USE_TIME_DETECTOR_IMPL;
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface exposed for easier testing that the surrounding class uses for interacting with
|
||||
* platform services, handlers, etc.
|
||||
*/
|
||||
interface Environment {
|
||||
|
||||
/**
|
||||
* Returns the current elapsed realtime value. The same as calling {@link
|
||||
* SystemClock#elapsedRealtime()} but easier to fake in tests.
|
||||
*/
|
||||
@ElapsedRealtimeLong long elapsedRealtimeMillis();
|
||||
|
||||
/**
|
||||
* Returns the latest / best network time available from the time detector service.
|
||||
*/
|
||||
@Nullable NetworkTimeSuggestion getLatestNetworkTime();
|
||||
|
||||
/**
|
||||
* Sets a listener that will receive a callback when the value returned by {@link
|
||||
* #getLatestNetworkTime()} has changed.
|
||||
*/
|
||||
void setNetworkTimeUpdateListener(StateChangeListener stateChangeListener);
|
||||
|
||||
/**
|
||||
* Requests asynchronous execution of {@link
|
||||
* TimeDetectorNetworkTimeHelper#queryAndInjectNetworkTime}, to execute as soon as possible.
|
||||
* The thread used is the same as used by {@link #requestDelayedTimeQueryCallback}.
|
||||
* Only one immediate callback can be requested at a time; requesting a new immediate
|
||||
* callback will clear any previously requested one.
|
||||
*/
|
||||
void requestImmediateTimeQueryCallback(TimeDetectorNetworkTimeHelper helper, String reason);
|
||||
|
||||
/**
|
||||
* Requests a delayed call to
|
||||
* {@link TimeDetectorNetworkTimeHelper#delayedQueryAndInjectNetworkTime()}.
|
||||
* The thread used is the same as used by {@link #requestImmediateTimeQueryCallback}.
|
||||
* Only one delayed callback can be scheduled at a time; requesting a new delayed callback
|
||||
* will clear any previously requested one.
|
||||
*/
|
||||
void requestDelayedTimeQueryCallback(
|
||||
TimeDetectorNetworkTimeHelper helper, @DurationMillisLong long delayMillis);
|
||||
|
||||
/**
|
||||
* Clear a delayed time query callback. This has no effect if no delayed callback is
|
||||
* currently set.
|
||||
*/
|
||||
void clearDelayedTimeQueryCallback();
|
||||
}
|
||||
|
||||
private static final String TAG = "TDNetworkTimeHelper";
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
|
||||
/** The maximum age of a network time signal that will be passed to GNSS. */
|
||||
@VisibleForTesting
|
||||
static final int MAX_NETWORK_TIME_AGE_MILLIS = 24 * 60 * 60 * 1000;
|
||||
|
||||
/**
|
||||
* The maximum time that is allowed to pass before a network time signal should be evaluated to
|
||||
* be passed to GNSS when mOnDemandTimeInjection == false.
|
||||
*/
|
||||
static final int NTP_REFRESH_INTERVAL_MILLIS = MAX_NETWORK_TIME_AGE_MILLIS;
|
||||
|
||||
private final LocalLog mDumpLog = new LocalLog(10, /*useLocalTimestamps=*/false);
|
||||
|
||||
/** The object the helper uses to interact with other components. */
|
||||
@NonNull private final Environment mEnvironment;
|
||||
@NonNull private final InjectTimeCallback mInjectTimeCallback;
|
||||
|
||||
/** Set to true if the GNSS engine requested on-demand NTP time injections. */
|
||||
@GuardedBy("this")
|
||||
private boolean mPeriodicTimeInjectionEnabled;
|
||||
|
||||
/**
|
||||
* Set to true when a network time has been injected. Used to ensure that a network time is
|
||||
* injected if this object wasn't listening when a network time signal first became available.
|
||||
*/
|
||||
@GuardedBy("this")
|
||||
private boolean mNetworkTimeInjected;
|
||||
|
||||
TimeDetectorNetworkTimeHelper(
|
||||
@NonNull Environment environment, @NonNull InjectTimeCallback injectTimeCallback) {
|
||||
mInjectTimeCallback = Objects.requireNonNull(injectTimeCallback);
|
||||
mEnvironment = Objects.requireNonNull(environment);
|
||||
|
||||
// Start listening for new network time updates immediately.
|
||||
mEnvironment.setNetworkTimeUpdateListener(this::onNetworkTimeAvailable);
|
||||
}
|
||||
|
||||
@Override
|
||||
synchronized void setPeriodicTimeInjectionMode(boolean periodicTimeInjectionEnabled) {
|
||||
// Periodic time injection has a complicated history. See b/73893222. When it is true, it
|
||||
// doesn't mean ONLY send it periodically.
|
||||
//
|
||||
// periodicTimeInjectionEnabled == true means the GNSS would like to be told the time
|
||||
// periodically in addition to all the other triggers (e.g. network available).
|
||||
|
||||
mPeriodicTimeInjectionEnabled = periodicTimeInjectionEnabled;
|
||||
if (!periodicTimeInjectionEnabled) {
|
||||
// Cancel any previously scheduled periodic query.
|
||||
removePeriodicNetworkTimeQuery();
|
||||
}
|
||||
|
||||
// Inject the latest network time in all cases if it is available.
|
||||
// Calling queryAndInjectNetworkTime() will cause a time signal to be injected if one is
|
||||
// available AND will cause the next periodic query to be scheduled.
|
||||
String reason = "setPeriodicTimeInjectionMode(" + periodicTimeInjectionEnabled + ")";
|
||||
mEnvironment.requestImmediateTimeQueryCallback(this, reason);
|
||||
}
|
||||
|
||||
void onNetworkTimeAvailable() {
|
||||
// A new network time could become available at any time. Make sure it is passed to GNSS.
|
||||
mEnvironment.requestImmediateTimeQueryCallback(this, "onNetworkTimeAvailable");
|
||||
}
|
||||
|
||||
@Override
|
||||
void onNetworkAvailable() {
|
||||
// In the original NetworkTimeHelper implementation, onNetworkAvailable() would cause an NTP
|
||||
// refresh to be made if it had previously been blocked by network issues. This
|
||||
// implementation generally relies on components associated with the time detector to
|
||||
// monitor the network and call onNetworkTimeAvailable() when a time is available. However,
|
||||
// it also checks mNetworkTimeInjected in case this component wasn't listening for
|
||||
// onNetworkTimeAvailable() when the last one became available.
|
||||
synchronized (this) {
|
||||
if (!mNetworkTimeInjected) {
|
||||
// Guard against ordering issues: This check should ensure that if a network time
|
||||
// became available before this class started listening then the initial network
|
||||
// time will still be injected.
|
||||
mEnvironment.requestImmediateTimeQueryCallback(this, "onNetworkAvailable");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void demandUtcTimeInjection() {
|
||||
mEnvironment.requestImmediateTimeQueryCallback(this, "demandUtcTimeInjection");
|
||||
}
|
||||
|
||||
// This method should always be invoked on the mEnvironment thread.
|
||||
void delayedQueryAndInjectNetworkTime() {
|
||||
queryAndInjectNetworkTime("delayedTimeQueryCallback");
|
||||
}
|
||||
|
||||
// This method should always be invoked on the mEnvironment thread.
|
||||
synchronized void queryAndInjectNetworkTime(@NonNull String reason) {
|
||||
NetworkTimeSuggestion latestNetworkTime = mEnvironment.getLatestNetworkTime();
|
||||
|
||||
maybeInjectNetworkTime(latestNetworkTime, reason);
|
||||
|
||||
// Deschedule (if needed) any previously scheduled periodic query.
|
||||
removePeriodicNetworkTimeQuery();
|
||||
|
||||
if (mPeriodicTimeInjectionEnabled) {
|
||||
int maxDelayMillis = NTP_REFRESH_INTERVAL_MILLIS;
|
||||
String debugMsg = "queryAndInjectNtpTime: Scheduling periodic query"
|
||||
+ " reason=" + reason
|
||||
+ " latestNetworkTime=" + latestNetworkTime
|
||||
+ " maxDelayMillis=" + maxDelayMillis;
|
||||
logToDumpLog(debugMsg);
|
||||
|
||||
// GNSS is expecting periodic injections, so schedule the next one.
|
||||
mEnvironment.requestDelayedTimeQueryCallback(this, maxDelayMillis);
|
||||
}
|
||||
}
|
||||
|
||||
private long calculateTimeSignalAgeMillis(
|
||||
@Nullable NetworkTimeSuggestion networkTimeSuggestion) {
|
||||
if (networkTimeSuggestion == null) {
|
||||
return Long.MAX_VALUE;
|
||||
}
|
||||
|
||||
long suggestionElapsedRealtimeMillis =
|
||||
networkTimeSuggestion.getUnixEpochTime().getElapsedRealtimeMillis();
|
||||
long currentElapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis();
|
||||
return currentElapsedRealtimeMillis - suggestionElapsedRealtimeMillis;
|
||||
}
|
||||
|
||||
@GuardedBy("this")
|
||||
private void maybeInjectNetworkTime(
|
||||
@Nullable NetworkTimeSuggestion latestNetworkTime, @NonNull String reason) {
|
||||
// Historically, time would only be injected if it was under a certain age. This has been
|
||||
// kept in case it is assumed by GNSS implementations.
|
||||
if (calculateTimeSignalAgeMillis(latestNetworkTime) > MAX_NETWORK_TIME_AGE_MILLIS) {
|
||||
String debugMsg = "maybeInjectNetworkTime: Not injecting latest network time"
|
||||
+ " latestNetworkTime=" + latestNetworkTime
|
||||
+ " reason=" + reason;
|
||||
logToDumpLog(debugMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
UnixEpochTime unixEpochTime = latestNetworkTime.getUnixEpochTime();
|
||||
long unixEpochTimeMillis = unixEpochTime.getUnixEpochTimeMillis();
|
||||
long currentTimeMillis = System.currentTimeMillis();
|
||||
String debugMsg = "maybeInjectNetworkTime: Injecting latest network time"
|
||||
+ " latestNetworkTime=" + latestNetworkTime
|
||||
+ " reason=" + reason
|
||||
+ " System time offset millis=" + (unixEpochTimeMillis - currentTimeMillis);
|
||||
logToDumpLog(debugMsg);
|
||||
|
||||
long timeReferenceMillis = unixEpochTime.getElapsedRealtimeMillis();
|
||||
int uncertaintyMillis = latestNetworkTime.getUncertaintyMillis();
|
||||
mInjectTimeCallback.injectTime(unixEpochTimeMillis, timeReferenceMillis, uncertaintyMillis);
|
||||
mNetworkTimeInjected = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
void dump(@NonNull PrintWriter pw) {
|
||||
pw.println("TimeDetectorNetworkTimeHelper:");
|
||||
|
||||
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
|
||||
ipw.increaseIndent();
|
||||
synchronized (this) {
|
||||
ipw.println("mPeriodicTimeInjectionEnabled=" + mPeriodicTimeInjectionEnabled);
|
||||
}
|
||||
|
||||
ipw.println("Debug log:");
|
||||
mDumpLog.dump(ipw);
|
||||
}
|
||||
|
||||
private void logToDumpLog(@NonNull String message) {
|
||||
mDumpLog.log(message);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, message);
|
||||
}
|
||||
}
|
||||
|
||||
private void removePeriodicNetworkTimeQuery() {
|
||||
// De-schedule any previously scheduled refresh. This is idempotent and has no effect if
|
||||
// there isn't one.
|
||||
mEnvironment.clearDelayedTimeQueryCallback();
|
||||
}
|
||||
|
||||
/** The real implementation of {@link Environment} used outside of tests. */
|
||||
static class EnvironmentImpl implements Environment {
|
||||
|
||||
/** Used to ensure one scheduled runnable is queued at a time. */
|
||||
private final Object mScheduledRunnableToken = new Object();
|
||||
/** Used to ensure one immediate runnable is queued at a time. */
|
||||
private final Object mImmediateRunnableToken = new Object();
|
||||
private final Handler mHandler;
|
||||
private final TimeDetectorInternal mTimeDetectorInternal;
|
||||
|
||||
EnvironmentImpl(Looper looper) {
|
||||
mHandler = new Handler(looper);
|
||||
mTimeDetectorInternal = LocalServices.getService(TimeDetectorInternal.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long elapsedRealtimeMillis() {
|
||||
return SystemClock.elapsedRealtime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NetworkTimeSuggestion getLatestNetworkTime() {
|
||||
return mTimeDetectorInternal.getLatestNetworkSuggestion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNetworkTimeUpdateListener(StateChangeListener stateChangeListener) {
|
||||
mTimeDetectorInternal.addNetworkTimeUpdateListener(stateChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestImmediateTimeQueryCallback(TimeDetectorNetworkTimeHelper helper,
|
||||
String reason) {
|
||||
// Ensure only one immediate callback is scheduled at a time. There's no
|
||||
// post(Runnable, Object), so we postDelayed() with a zero wait.
|
||||
synchronized (this) {
|
||||
mHandler.removeCallbacksAndMessages(mImmediateRunnableToken);
|
||||
mHandler.postDelayed(() -> helper.queryAndInjectNetworkTime(reason),
|
||||
mImmediateRunnableToken, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestDelayedTimeQueryCallback(TimeDetectorNetworkTimeHelper helper,
|
||||
long delayMillis) {
|
||||
synchronized (this) {
|
||||
clearDelayedTimeQueryCallback();
|
||||
mHandler.postDelayed(helper::delayedQueryAndInjectNetworkTime,
|
||||
mScheduledRunnableToken, delayMillis);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void clearDelayedTimeQueryCallback() {
|
||||
mHandler.removeCallbacksAndMessages(mScheduledRunnableToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,4 +129,9 @@ final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environment {
|
||||
public void dumpDebugLog(@NonNull PrintWriter printWriter) {
|
||||
SystemClockTime.dump(printWriter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runAsync(@NonNull Runnable runnable) {
|
||||
mHandler.post(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,13 @@
|
||||
package com.android.server.timedetector;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.time.TimeCapabilitiesAndConfig;
|
||||
import android.app.time.TimeConfiguration;
|
||||
import android.app.timedetector.ManualTimeSuggestion;
|
||||
|
||||
import com.android.server.timezonedetector.StateChangeListener;
|
||||
|
||||
/**
|
||||
* The internal (in-process) system server API for the time detector service.
|
||||
*
|
||||
@@ -61,10 +64,25 @@ public interface TimeDetectorInternal {
|
||||
/**
|
||||
* Suggests a network time to the time detector. The suggestion may not be used by the time
|
||||
* detector to set the device's time depending on device configuration and user settings, but
|
||||
* can replace previous network suggestions received.
|
||||
* can replace previous network suggestions received. See also
|
||||
* {@link #addNetworkTimeUpdateListener(StateChangeListener)} and
|
||||
* {@link #getLatestNetworkSuggestion()}.
|
||||
*/
|
||||
void suggestNetworkTime(@NonNull NetworkTimeSuggestion suggestion);
|
||||
|
||||
/**
|
||||
* Adds a listener that will be notified when a new network time is available. See {@link
|
||||
* #getLatestNetworkSuggestion()}.
|
||||
*/
|
||||
void addNetworkTimeUpdateListener(
|
||||
@NonNull StateChangeListener networkSuggestionUpdateListener);
|
||||
|
||||
/**
|
||||
* Returns the latest / best network time received by the time detector.
|
||||
*/
|
||||
@Nullable
|
||||
NetworkTimeSuggestion getLatestNetworkSuggestion();
|
||||
|
||||
/**
|
||||
* Suggests a GNSS-derived time to the time detector. The suggestion may not be used by the time
|
||||
* detector to set the device's time depending on device configuration and user settings, but
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.content.Context;
|
||||
import android.os.Handler;
|
||||
|
||||
import com.android.server.timezonedetector.CurrentUserIdentityInjector;
|
||||
import com.android.server.timezonedetector.StateChangeListener;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -86,6 +87,19 @@ public class TimeDetectorInternalImpl implements TimeDetectorInternal {
|
||||
mHandler.post(() -> mTimeDetectorStrategy.suggestNetworkTime(suggestion));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNetworkTimeUpdateListener(
|
||||
@NonNull StateChangeListener networkTimeUpdateListener) {
|
||||
Objects.requireNonNull(networkTimeUpdateListener);
|
||||
mTimeDetectorStrategy.addNetworkTimeUpdateListener(networkTimeUpdateListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public NetworkTimeSuggestion getLatestNetworkSuggestion() {
|
||||
return mTimeDetectorStrategy.getLatestNetworkSuggestion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestGnssTime(@NonNull GnssTimeSuggestion suggestion) {
|
||||
Objects.requireNonNull(suggestion);
|
||||
|
||||
@@ -47,6 +47,7 @@ import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.DumpUtils;
|
||||
import com.android.server.FgThread;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.location.gnss.TimeDetectorNetworkTimeHelper;
|
||||
import com.android.server.timezonedetector.CallerIdentityInjector;
|
||||
import com.android.server.timezonedetector.CurrentUserIdentityInjector;
|
||||
|
||||
@@ -405,13 +406,19 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub
|
||||
// TODO(b/222295093): Return the latest network time from mTimeDetectorStrategy once we can
|
||||
// be sure that all uses of NtpTrustedTime results in a suggestion being made to the time
|
||||
// detector. mNtpTrustedTime can be removed once this happens.
|
||||
NtpTrustedTime.TimeResult ntpResult = mNtpTrustedTime.getCachedTimeResult();
|
||||
if (ntpResult != null) {
|
||||
UnixEpochTime unixEpochTime = new UnixEpochTime(
|
||||
ntpResult.getElapsedRealtimeMillis(), ntpResult.getTimeMillis());
|
||||
return new NetworkTimeSuggestion(unixEpochTime, ntpResult.getUncertaintyMillis());
|
||||
if (TimeDetectorNetworkTimeHelper.isInUse()) {
|
||||
// The new implementation.
|
||||
return mTimeDetectorStrategy.getLatestNetworkSuggestion();
|
||||
} else {
|
||||
return null;
|
||||
// The old implementation.
|
||||
NtpTrustedTime.TimeResult ntpResult = mNtpTrustedTime.getCachedTimeResult();
|
||||
if (ntpResult != null) {
|
||||
UnixEpochTime unixEpochTime = new UnixEpochTime(
|
||||
ntpResult.getElapsedRealtimeMillis(), ntpResult.getTimeMillis());
|
||||
return new NetworkTimeSuggestion(unixEpochTime, ntpResult.getUncertaintyMillis());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.util.IndentingPrintWriter;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.server.timezonedetector.Dumpable;
|
||||
import com.android.server.timezonedetector.StateChangeListener;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -104,10 +105,18 @@ public interface TimeDetectorStrategy extends Dumpable {
|
||||
/**
|
||||
* Processes the suggested network time. The suggestion may not be used to set the device's time
|
||||
* depending on device configuration and user settings, but can replace previous network
|
||||
* suggestions received.
|
||||
* suggestions received. See also
|
||||
* {@link #addNetworkTimeUpdateListener(StateChangeListener)} and
|
||||
* {@link #getLatestNetworkSuggestion()}.
|
||||
*/
|
||||
void suggestNetworkTime(@NonNull NetworkTimeSuggestion suggestion);
|
||||
|
||||
/**
|
||||
* Adds a listener that will be notified when a new network time is available. See {@link
|
||||
* #getLatestNetworkSuggestion()}.
|
||||
*/
|
||||
void addNetworkTimeUpdateListener(@NonNull StateChangeListener networkSuggestionUpdateListener);
|
||||
|
||||
/**
|
||||
* Returns the latest (accepted) network time suggestion. Returns {@code null} if there isn't
|
||||
* one.
|
||||
|
||||
@@ -36,6 +36,7 @@ import android.app.timedetector.ManualTimeSuggestion;
|
||||
import android.app.timedetector.TelephonyTimeSuggestion;
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.util.ArraySet;
|
||||
import android.util.IndentingPrintWriter;
|
||||
import android.util.Slog;
|
||||
|
||||
@@ -125,6 +126,9 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
|
||||
private final ReferenceWithHistory<ExternalTimeSuggestion> mLastExternalSuggestion =
|
||||
new ReferenceWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
|
||||
|
||||
@GuardedBy("this")
|
||||
private final ArraySet<StateChangeListener> mNetworkTimeUpdateListeners = new ArraySet<>();
|
||||
|
||||
/**
|
||||
* Used by {@link TimeDetectorStrategyImpl} to interact with device configuration / settings
|
||||
* / system properties. It can be faked for testing.
|
||||
@@ -180,6 +184,11 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
|
||||
* Dumps the time debug log to the supplied {@link PrintWriter}.
|
||||
*/
|
||||
void dumpDebugLog(PrintWriter printWriter);
|
||||
|
||||
/**
|
||||
* Requests that the supplied runnable is invoked asynchronously.
|
||||
*/
|
||||
void runAsync(@NonNull Runnable runnable);
|
||||
}
|
||||
|
||||
static TimeDetectorStrategy create(
|
||||
@@ -307,6 +316,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
|
||||
NetworkTimeSuggestion lastNetworkSuggestion = mLastNetworkSuggestion.get();
|
||||
if (lastNetworkSuggestion == null || !lastNetworkSuggestion.equals(suggestion)) {
|
||||
mLastNetworkSuggestion.set(suggestion);
|
||||
notifyNetworkTimeUpdateListenersAsynchronously();
|
||||
}
|
||||
|
||||
// Now perform auto time detection. The new suggestion may be used to modify the system
|
||||
@@ -315,6 +325,20 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
|
||||
doAutoTimeDetection(reason);
|
||||
}
|
||||
|
||||
@GuardedBy("this")
|
||||
private void notifyNetworkTimeUpdateListenersAsynchronously() {
|
||||
for (StateChangeListener listener : mNetworkTimeUpdateListeners) {
|
||||
// This is queuing asynchronous notification, so no need to surrender the "this" lock.
|
||||
mEnvironment.runAsync(listener::onChange);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void addNetworkTimeUpdateListener(
|
||||
@NonNull StateChangeListener networkSuggestionUpdateListener) {
|
||||
mNetworkTimeUpdateListeners.add(networkSuggestionUpdateListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public synchronized NetworkTimeSuggestion getLatestNetworkSuggestion() {
|
||||
@@ -325,6 +349,8 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
|
||||
public synchronized void clearLatestNetworkSuggestion() {
|
||||
mLastNetworkSuggestion.set(null);
|
||||
|
||||
notifyNetworkTimeUpdateListenersAsynchronously();
|
||||
|
||||
// The loss of network time may change the time signal to use to set the system clock.
|
||||
String reason = "Network time cleared";
|
||||
doAutoTimeDetection(reason);
|
||||
|
||||
@@ -2095,6 +2095,14 @@ public final class SystemServer implements Dumpable {
|
||||
mSystemServiceManager.startService(DeviceStorageMonitorService.class);
|
||||
t.traceEnd();
|
||||
|
||||
t.traceBegin("StartTimeDetectorService");
|
||||
try {
|
||||
mSystemServiceManager.startService(TIME_DETECTOR_SERVICE_CLASS);
|
||||
} catch (Throwable e) {
|
||||
reportWtf("starting TimeDetectorService service", e);
|
||||
}
|
||||
t.traceEnd();
|
||||
|
||||
t.traceBegin("StartLocationManagerService");
|
||||
mSystemServiceManager.startService(LocationManagerService.Lifecycle.class);
|
||||
t.traceEnd();
|
||||
@@ -2108,14 +2116,6 @@ public final class SystemServer implements Dumpable {
|
||||
}
|
||||
t.traceEnd();
|
||||
|
||||
t.traceBegin("StartTimeDetectorService");
|
||||
try {
|
||||
mSystemServiceManager.startService(TIME_DETECTOR_SERVICE_CLASS);
|
||||
} catch (Throwable e) {
|
||||
reportWtf("starting TimeDetectorService service", e);
|
||||
}
|
||||
t.traceEnd();
|
||||
|
||||
t.traceBegin("StartTimeZoneDetectorService");
|
||||
try {
|
||||
mSystemServiceManager.startService(TIME_ZONE_DETECTOR_SERVICE_CLASS);
|
||||
|
||||
@@ -0,0 +1,399 @@
|
||||
/*
|
||||
* 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 com.android.server.location.gnss;
|
||||
|
||||
import static com.android.server.location.gnss.TimeDetectorNetworkTimeHelper.MAX_NETWORK_TIME_AGE_MILLIS;
|
||||
import static com.android.server.location.gnss.TimeDetectorNetworkTimeHelper.NTP_REFRESH_INTERVAL_MILLIS;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import android.app.time.UnixEpochTime;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import com.android.server.location.gnss.NetworkTimeHelper.InjectTimeCallback;
|
||||
import com.android.server.location.gnss.TimeDetectorNetworkTimeHelper.Environment;
|
||||
import com.android.server.timedetector.NetworkTimeSuggestion;
|
||||
import com.android.server.timezonedetector.StateChangeListener;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link TimeDetectorNetworkTimeHelper}.
|
||||
*/
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Presubmit
|
||||
public class TimeDetectorNetworkTimeHelperTest {
|
||||
|
||||
private static final NetworkTimeSuggestion ARBITRARY_NETWORK_TIME =
|
||||
new NetworkTimeSuggestion(new UnixEpochTime(1234L, 7777L), 123);
|
||||
|
||||
private FakeEnvironment mFakeEnvironment;
|
||||
@Mock private InjectTimeCallback mMockInjectTimeCallback;
|
||||
private TimeDetectorNetworkTimeHelper mTimeDetectorNetworkTimeHelper;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mFakeEnvironment = new FakeEnvironment();
|
||||
mTimeDetectorNetworkTimeHelper = new TimeDetectorNetworkTimeHelper(
|
||||
mFakeEnvironment, mMockInjectTimeCallback);
|
||||
|
||||
// TimeDetectorNetworkTimeHelper should register for network time updates during
|
||||
// construction.
|
||||
mFakeEnvironment.assertHasNetworkTimeChangeListener();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setPeriodicTimeInjectionMode_true() {
|
||||
testSetPeriodicTimeInjectionMode(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setPeriodicTimeInjectionMode_false() {
|
||||
testSetPeriodicTimeInjectionMode(false);
|
||||
}
|
||||
|
||||
private void testSetPeriodicTimeInjectionMode(boolean periodicTimeInjectionMode) {
|
||||
NetworkTimeSuggestion networkTime = ARBITRARY_NETWORK_TIME;
|
||||
int millisElapsedSinceNetworkTimeReceived = 1000;
|
||||
mFakeEnvironment.pokeLatestNetworkTime(networkTime);
|
||||
|
||||
long currentElapsedRealtimeMillis =
|
||||
networkTime.getUnixEpochTime().getElapsedRealtimeMillis()
|
||||
+ millisElapsedSinceNetworkTimeReceived;
|
||||
mFakeEnvironment.pokeElapsedRealtimeMillis(currentElapsedRealtimeMillis);
|
||||
|
||||
mTimeDetectorNetworkTimeHelper.setPeriodicTimeInjectionMode(periodicTimeInjectionMode);
|
||||
|
||||
// All injections are async, so we have to simulate the async work taking place.
|
||||
mFakeEnvironment.assertHasNoScheduledAsyncCallback();
|
||||
mFakeEnvironment.assertHasImmediateCallback();
|
||||
mFakeEnvironment.simulateTimeAdvancing(1);
|
||||
|
||||
// Any call to setPeriodicTimeInjectionMode() should result in an (async) injected time
|
||||
verify(mMockInjectTimeCallback).injectTime(
|
||||
networkTime.getUnixEpochTime().getUnixEpochTimeMillis(),
|
||||
networkTime.getUnixEpochTime().getElapsedRealtimeMillis(),
|
||||
networkTime.getUncertaintyMillis());
|
||||
|
||||
// Check whether the scheduled async is set up / not set up for the periodic request.
|
||||
if (periodicTimeInjectionMode) {
|
||||
mFakeEnvironment.assertHasScheduledAsyncCallback(
|
||||
mFakeEnvironment.elapsedRealtimeMillis() + NTP_REFRESH_INTERVAL_MILLIS);
|
||||
} else {
|
||||
mFakeEnvironment.assertHasNoScheduledAsyncCallback();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void periodicInjectionBehavior() {
|
||||
// Set the elapsed realtime clock to an arbitrary start value.
|
||||
mFakeEnvironment.pokeElapsedRealtimeMillis(12345L);
|
||||
|
||||
// Configure periodic time injections. Doing so should cause a time query, but no time is
|
||||
// available.
|
||||
mTimeDetectorNetworkTimeHelper.setPeriodicTimeInjectionMode(true);
|
||||
|
||||
// All query/injections are async, so we have to simulate the async work taking place.
|
||||
mFakeEnvironment.assertHasImmediateCallback();
|
||||
mFakeEnvironment.simulateTimeAdvancing(1);
|
||||
|
||||
// No time available, so no injection.
|
||||
verifyNoMoreInteractions(mMockInjectTimeCallback);
|
||||
|
||||
// A periodic check should be scheduled.
|
||||
mFakeEnvironment.assertHasScheduledAsyncCallback(
|
||||
mFakeEnvironment.elapsedRealtimeMillis() + NTP_REFRESH_INTERVAL_MILLIS);
|
||||
|
||||
// Time passes...
|
||||
mFakeEnvironment.simulateTimeAdvancing(NTP_REFRESH_INTERVAL_MILLIS / 2);
|
||||
|
||||
// A network time becomes available: This should cause the registered listener to trigger.
|
||||
NetworkTimeSuggestion networkTime = ARBITRARY_NETWORK_TIME;
|
||||
mFakeEnvironment.simulateLatestNetworkTimeChange(networkTime);
|
||||
|
||||
// All query/injections are async, so we have to simulate the async work taking place,
|
||||
// causing a query, time injection and a re-schedule.
|
||||
mFakeEnvironment.simulateTimeAdvancing(1);
|
||||
verify(mMockInjectTimeCallback).injectTime(
|
||||
networkTime.getUnixEpochTime().getUnixEpochTimeMillis(),
|
||||
networkTime.getUnixEpochTime().getElapsedRealtimeMillis(),
|
||||
networkTime.getUncertaintyMillis());
|
||||
|
||||
// A new periodic check should be scheduled.
|
||||
mFakeEnvironment.assertHasNoImmediateCallback();
|
||||
|
||||
mFakeEnvironment.assertHasScheduledAsyncCallback(
|
||||
mFakeEnvironment.elapsedRealtimeMillis() + NTP_REFRESH_INTERVAL_MILLIS);
|
||||
|
||||
int arbitraryIterationCount = 3;
|
||||
for (int i = 0; i < arbitraryIterationCount; i++) {
|
||||
// Advance by the amount needed for the scheduled work to run. That work should query
|
||||
// and inject.
|
||||
mFakeEnvironment.simulateTimeAdvancing(NTP_REFRESH_INTERVAL_MILLIS);
|
||||
|
||||
// All query/injections are async, so we have to simulate the async work taking place,
|
||||
// causing a query, time injection and a re-schedule.
|
||||
verify(mMockInjectTimeCallback).injectTime(
|
||||
networkTime.getUnixEpochTime().getUnixEpochTimeMillis(),
|
||||
networkTime.getUnixEpochTime().getElapsedRealtimeMillis(),
|
||||
networkTime.getUncertaintyMillis());
|
||||
|
||||
// A new periodic check should be scheduled.
|
||||
mFakeEnvironment.assertHasScheduledAsyncCallback(
|
||||
mFakeEnvironment.elapsedRealtimeMillis() + NTP_REFRESH_INTERVAL_MILLIS);
|
||||
mFakeEnvironment.assertHasNoImmediateCallback();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void networkTimeAvailableBehavior() {
|
||||
// Set the elapsed realtime clock to an arbitrary start value.
|
||||
mFakeEnvironment.pokeElapsedRealtimeMillis(12345L);
|
||||
|
||||
// No periodic time injections. This call causes a time query, but no time is available yet.
|
||||
mTimeDetectorNetworkTimeHelper.setPeriodicTimeInjectionMode(false);
|
||||
|
||||
// All query/injections are async, so we have to simulate the async work taking place.
|
||||
mFakeEnvironment.assertHasNoScheduledAsyncCallback();
|
||||
mFakeEnvironment.assertHasImmediateCallback();
|
||||
mFakeEnvironment.simulateTimeAdvancing(1);
|
||||
|
||||
// No time available, so no injection.
|
||||
verifyNoMoreInteractions(mMockInjectTimeCallback);
|
||||
|
||||
// No periodic check should be scheduled.
|
||||
mFakeEnvironment.assertHasNoScheduledAsyncCallback();
|
||||
|
||||
// Time passes...
|
||||
mFakeEnvironment.simulateTimeAdvancing(NTP_REFRESH_INTERVAL_MILLIS / 2);
|
||||
|
||||
// A network time becomes available: This should cause the registered listener to trigger
|
||||
// and cause time to be injected.
|
||||
NetworkTimeSuggestion networkTime = ARBITRARY_NETWORK_TIME;
|
||||
mFakeEnvironment.simulateLatestNetworkTimeChange(networkTime);
|
||||
|
||||
// All query/injections are async, so we have to simulate the async work taking place,
|
||||
// causing a query, time injection and a re-schedule.
|
||||
mFakeEnvironment.assertHasNoScheduledAsyncCallback();
|
||||
mFakeEnvironment.assertHasImmediateCallback();
|
||||
mFakeEnvironment.simulateTimeAdvancing(1);
|
||||
verify(mMockInjectTimeCallback).injectTime(
|
||||
networkTime.getUnixEpochTime().getUnixEpochTimeMillis(),
|
||||
networkTime.getUnixEpochTime().getElapsedRealtimeMillis(),
|
||||
networkTime.getUncertaintyMillis());
|
||||
|
||||
// No periodic check should be scheduled.
|
||||
mFakeEnvironment.assertHasNoScheduledAsyncCallback();
|
||||
mFakeEnvironment.assertHasNoImmediateCallback();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void networkConnectivityAvailableBehavior() {
|
||||
// Set the elapsed realtime clock to an arbitrary start value.
|
||||
mFakeEnvironment.pokeElapsedRealtimeMillis(12345L);
|
||||
|
||||
// No periodic time injections. This call causes a time query, but no time is available yet.
|
||||
mTimeDetectorNetworkTimeHelper.setPeriodicTimeInjectionMode(false);
|
||||
|
||||
// All query/injections are async, so we have to simulate the async work taking place.
|
||||
mFakeEnvironment.assertHasNoScheduledAsyncCallback();
|
||||
mFakeEnvironment.assertHasImmediateCallback();
|
||||
mFakeEnvironment.simulateTimeAdvancing(1);
|
||||
|
||||
// No time available, so no injection.
|
||||
verifyNoMoreInteractions(mMockInjectTimeCallback);
|
||||
|
||||
// No periodic check should be scheduled.
|
||||
mFakeEnvironment.assertHasNoScheduledAsyncCallback();
|
||||
|
||||
// Time passes...
|
||||
mFakeEnvironment.simulateTimeAdvancing(NTP_REFRESH_INTERVAL_MILLIS / 2);
|
||||
|
||||
NetworkTimeSuggestion networkTime = ARBITRARY_NETWORK_TIME;
|
||||
mFakeEnvironment.pokeLatestNetworkTime(networkTime);
|
||||
|
||||
// Simulate location code noticing that connectivity has changed and notifying the helper.
|
||||
mTimeDetectorNetworkTimeHelper.onNetworkAvailable();
|
||||
|
||||
// All query/injections are async, so we have to simulate the async work taking place,
|
||||
// causing a query, time injection and a re-schedule.
|
||||
mFakeEnvironment.assertHasNoScheduledAsyncCallback();
|
||||
mFakeEnvironment.assertHasImmediateCallback();
|
||||
mFakeEnvironment.simulateTimeAdvancing(1);
|
||||
verify(mMockInjectTimeCallback).injectTime(
|
||||
networkTime.getUnixEpochTime().getUnixEpochTimeMillis(),
|
||||
networkTime.getUnixEpochTime().getElapsedRealtimeMillis(),
|
||||
networkTime.getUncertaintyMillis());
|
||||
|
||||
// No periodic check should be scheduled.
|
||||
mFakeEnvironment.assertHasNoScheduledAsyncCallback();
|
||||
mFakeEnvironment.assertHasNoImmediateCallback();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void oldTimesNotInjected() {
|
||||
NetworkTimeSuggestion networkTime = ARBITRARY_NETWORK_TIME;
|
||||
mFakeEnvironment.pokeLatestNetworkTime(networkTime);
|
||||
|
||||
int millisElapsedSinceNetworkTimeReceived = MAX_NETWORK_TIME_AGE_MILLIS;
|
||||
long currentElapsedRealtimeMillis =
|
||||
networkTime.getUnixEpochTime().getElapsedRealtimeMillis()
|
||||
+ millisElapsedSinceNetworkTimeReceived;
|
||||
mFakeEnvironment.pokeElapsedRealtimeMillis(currentElapsedRealtimeMillis);
|
||||
|
||||
mTimeDetectorNetworkTimeHelper.setPeriodicTimeInjectionMode(true);
|
||||
|
||||
// All injections are async, so we have to simulate the async work taking place.
|
||||
mFakeEnvironment.assertHasNoScheduledAsyncCallback();
|
||||
mFakeEnvironment.assertHasImmediateCallback();
|
||||
|
||||
// The age of the network time will now be MAX_NETWORK_TIME_AGE_MILLIS + 1, which is too
|
||||
// old to inject.
|
||||
mFakeEnvironment.simulateTimeAdvancing(1);
|
||||
|
||||
// Old network times should not be injected.
|
||||
verify(mMockInjectTimeCallback, never()).injectTime(anyLong(), anyLong(), anyInt());
|
||||
|
||||
// Check whether the scheduled async is set up / not set up for the periodic request.
|
||||
mFakeEnvironment.assertHasScheduledAsyncCallback(
|
||||
mFakeEnvironment.elapsedRealtimeMillis() + NTP_REFRESH_INTERVAL_MILLIS);
|
||||
}
|
||||
|
||||
/** A fake implementation of {@link Environment} for use by this test. */
|
||||
private static class FakeEnvironment implements Environment {
|
||||
|
||||
private StateChangeListener mNetworkTimeUpdateListener;
|
||||
|
||||
private long mCurrentElapsedRealtimeMillis;
|
||||
private NetworkTimeSuggestion mLatestNetworkTime;
|
||||
|
||||
private TimeDetectorNetworkTimeHelper mImmediateAsyncCallback;
|
||||
private String mImmediateAsyncCallbackReason;
|
||||
|
||||
private TimeDetectorNetworkTimeHelper mScheduledAsyncCallback;
|
||||
private long mScheduledAsyncRunnableTimeMillis;
|
||||
|
||||
@Override
|
||||
public long elapsedRealtimeMillis() {
|
||||
return mCurrentElapsedRealtimeMillis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NetworkTimeSuggestion getLatestNetworkTime() {
|
||||
return mLatestNetworkTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNetworkTimeUpdateListener(StateChangeListener stateChangeListener) {
|
||||
mNetworkTimeUpdateListener = stateChangeListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestImmediateTimeQueryCallback(TimeDetectorNetworkTimeHelper helper,
|
||||
String reason) {
|
||||
if (mImmediateAsyncCallback != null) {
|
||||
fail("Only one immediate callback expected at a time, found reason: "
|
||||
+ mImmediateAsyncCallbackReason);
|
||||
}
|
||||
mImmediateAsyncCallback = helper;
|
||||
mImmediateAsyncCallbackReason = reason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestDelayedTimeQueryCallback(
|
||||
TimeDetectorNetworkTimeHelper instance, long delayMillis) {
|
||||
mScheduledAsyncCallback = instance;
|
||||
mScheduledAsyncRunnableTimeMillis = mCurrentElapsedRealtimeMillis + delayMillis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearDelayedTimeQueryCallback() {
|
||||
mScheduledAsyncCallback = null;
|
||||
mScheduledAsyncRunnableTimeMillis = -1;
|
||||
}
|
||||
|
||||
void pokeLatestNetworkTime(NetworkTimeSuggestion networkTime) {
|
||||
mLatestNetworkTime = networkTime;
|
||||
}
|
||||
|
||||
void pokeElapsedRealtimeMillis(long currentElapsedRealtimeMillis) {
|
||||
mCurrentElapsedRealtimeMillis = currentElapsedRealtimeMillis;
|
||||
}
|
||||
|
||||
void simulateLatestNetworkTimeChange(NetworkTimeSuggestion networkTime) {
|
||||
mLatestNetworkTime = networkTime;
|
||||
mNetworkTimeUpdateListener.onChange();
|
||||
}
|
||||
|
||||
void simulateTimeAdvancing(long durationMillis) {
|
||||
mCurrentElapsedRealtimeMillis += durationMillis;
|
||||
|
||||
if (mImmediateAsyncCallback != null) {
|
||||
TimeDetectorNetworkTimeHelper helper = mImmediateAsyncCallback;
|
||||
String reason = mImmediateAsyncCallbackReason;
|
||||
mImmediateAsyncCallback = null;
|
||||
mImmediateAsyncCallbackReason = null;
|
||||
helper.queryAndInjectNetworkTime(reason);
|
||||
}
|
||||
|
||||
if (mScheduledAsyncCallback != null
|
||||
&& mCurrentElapsedRealtimeMillis >= mScheduledAsyncRunnableTimeMillis) {
|
||||
TimeDetectorNetworkTimeHelper helper = mScheduledAsyncCallback;
|
||||
mScheduledAsyncCallback = null;
|
||||
mScheduledAsyncRunnableTimeMillis = -1;
|
||||
helper.delayedQueryAndInjectNetworkTime();
|
||||
}
|
||||
}
|
||||
|
||||
void assertHasNetworkTimeChangeListener() {
|
||||
assertNotNull(mNetworkTimeUpdateListener);
|
||||
}
|
||||
|
||||
void assertHasImmediateCallback() {
|
||||
assertNotNull(mImmediateAsyncCallback);
|
||||
}
|
||||
|
||||
void assertHasNoImmediateCallback() {
|
||||
assertNull(mImmediateAsyncCallback);
|
||||
}
|
||||
|
||||
void assertHasScheduledAsyncCallback(long expectedScheduledAsyncRunnableTimeMillis) {
|
||||
assertNotNull(mScheduledAsyncCallback);
|
||||
assertEquals(expectedScheduledAsyncRunnableTimeMillis,
|
||||
mScheduledAsyncRunnableTimeMillis);
|
||||
}
|
||||
|
||||
void assertHasNoScheduledAsyncCallback() {
|
||||
assertNull(mScheduledAsyncCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,8 @@ import android.app.timedetector.ManualTimeSuggestion;
|
||||
import android.app.timedetector.TelephonyTimeSuggestion;
|
||||
import android.util.IndentingPrintWriter;
|
||||
|
||||
import com.android.server.timezonedetector.StateChangeListener;
|
||||
|
||||
/**
|
||||
* A fake implementation of {@link com.android.server.timedetector.TimeDetectorStrategy} for use
|
||||
* in tests.
|
||||
@@ -31,6 +33,7 @@ import android.util.IndentingPrintWriter;
|
||||
public class FakeTimeDetectorStrategy implements TimeDetectorStrategy {
|
||||
// State
|
||||
private TimeState mTimeState;
|
||||
private NetworkTimeSuggestion mLatestNetworkTimeSuggestion;
|
||||
|
||||
@Override
|
||||
public TimeState getTimeState() {
|
||||
@@ -61,9 +64,13 @@ public class FakeTimeDetectorStrategy implements TimeDetectorStrategy {
|
||||
public void suggestNetworkTime(NetworkTimeSuggestion suggestion) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNetworkTimeUpdateListener(StateChangeListener networkSuggestionUpdateListener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public NetworkTimeSuggestion getLatestNetworkSuggestion() {
|
||||
return null;
|
||||
return mLatestNetworkTimeSuggestion;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -81,4 +88,8 @@ public class FakeTimeDetectorStrategy implements TimeDetectorStrategy {
|
||||
@Override
|
||||
public void dump(IndentingPrintWriter pw, String[] args) {
|
||||
}
|
||||
|
||||
void setLatestNetworkTime(NetworkTimeSuggestion networkTimeSuggestion) {
|
||||
mLatestNetworkTimeSuggestion = networkTimeSuggestion;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ import android.util.NtpTrustedTime;
|
||||
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.server.location.gnss.TimeDetectorNetworkTimeHelper;
|
||||
import com.android.server.timezonedetector.TestCallerIdentityInjector;
|
||||
import com.android.server.timezonedetector.TestHandler;
|
||||
|
||||
@@ -420,21 +421,35 @@ public class TimeDetectorServiceTest {
|
||||
|
||||
@Test
|
||||
public void testGetLatestNetworkSuggestion() {
|
||||
NtpTrustedTime.TimeResult latestNetworkTime = new NtpTrustedTime.TimeResult(
|
||||
1234L, 54321L, 999, InetSocketAddress.createUnresolved("test.timeserver", 123));
|
||||
when(mMockNtpTrustedTime.getCachedTimeResult())
|
||||
.thenReturn(latestNetworkTime);
|
||||
UnixEpochTime expectedUnixEpochTime = new UnixEpochTime(
|
||||
latestNetworkTime.getElapsedRealtimeMillis(), latestNetworkTime.getTimeMillis());
|
||||
NetworkTimeSuggestion expected = new NetworkTimeSuggestion(
|
||||
expectedUnixEpochTime, latestNetworkTime.getUncertaintyMillis());
|
||||
assertEquals(expected, mTimeDetectorService.getLatestNetworkSuggestion());
|
||||
if (TimeDetectorNetworkTimeHelper.isInUse()) {
|
||||
NetworkTimeSuggestion latestNetworkTime = createNetworkTimeSuggestion();
|
||||
mFakeTimeDetectorStrategySpy.setLatestNetworkTime(latestNetworkTime);
|
||||
|
||||
assertEquals(latestNetworkTime, mTimeDetectorService.getLatestNetworkSuggestion());
|
||||
} else {
|
||||
NtpTrustedTime.TimeResult latestNetworkTime = new NtpTrustedTime.TimeResult(
|
||||
1234L, 54321L, 999, InetSocketAddress.createUnresolved("test.timeserver", 123));
|
||||
when(mMockNtpTrustedTime.getCachedTimeResult())
|
||||
.thenReturn(latestNetworkTime);
|
||||
UnixEpochTime expectedUnixEpochTime = new UnixEpochTime(
|
||||
latestNetworkTime.getElapsedRealtimeMillis(),
|
||||
latestNetworkTime.getTimeMillis());
|
||||
NetworkTimeSuggestion expected = new NetworkTimeSuggestion(
|
||||
expectedUnixEpochTime, latestNetworkTime.getUncertaintyMillis());
|
||||
assertEquals(expected, mTimeDetectorService.getLatestNetworkSuggestion());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetLatestNetworkSuggestion_noTimeAvailable() {
|
||||
when(mMockNtpTrustedTime.getCachedTimeResult()).thenReturn(null);
|
||||
assertNull(mTimeDetectorService.getLatestNetworkSuggestion());
|
||||
if (TimeDetectorNetworkTimeHelper.isInUse()) {
|
||||
mFakeTimeDetectorStrategySpy.setLatestNetworkTime(null);
|
||||
|
||||
assertNull(mTimeDetectorService.getLatestNetworkSuggestion());
|
||||
} else {
|
||||
when(mMockNtpTrustedTime.getCachedTimeResult()).thenReturn(null);
|
||||
assertNull(mTimeDetectorService.getLatestNetworkSuggestion());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -41,6 +41,7 @@ import android.os.TimestampedValue;
|
||||
import com.android.server.SystemClockTime.TimeConfidence;
|
||||
import com.android.server.timedetector.TimeDetectorStrategy.Origin;
|
||||
import com.android.server.timezonedetector.StateChangeListener;
|
||||
import com.android.server.timezonedetector.TestStateChangeListener;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -51,6 +52,8 @@ import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import junitparams.JUnitParamsRunner;
|
||||
@@ -863,8 +866,11 @@ public class TimeDetectorStrategyImplTest {
|
||||
new ConfigurationInternal.Builder(CONFIG_AUTO_ENABLED)
|
||||
.setOriginPriorities(ORIGIN_NETWORK)
|
||||
.build();
|
||||
Script script = new Script().simulateConfigurationInternalChange(configInternal)
|
||||
.verifySystemClockConfidence(TIME_CONFIDENCE_LOW);
|
||||
TestStateChangeListener networkTimeUpdateListener = new TestStateChangeListener();
|
||||
Script script = new Script()
|
||||
.simulateConfigurationInternalChange(configInternal)
|
||||
.verifySystemClockConfidence(TIME_CONFIDENCE_LOW)
|
||||
.addNetworkTimeUpdateListener(networkTimeUpdateListener);
|
||||
|
||||
NetworkTimeSuggestion timeSuggestion =
|
||||
script.generateNetworkTimeSuggestion(ARBITRARY_TEST_TIME);
|
||||
@@ -877,6 +883,11 @@ public class TimeDetectorStrategyImplTest {
|
||||
.assertLatestNetworkSuggestion(timeSuggestion)
|
||||
.verifySystemClockConfidence(TIME_CONFIDENCE_HIGH)
|
||||
.verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis);
|
||||
|
||||
// Confirm the network time update listener is notified of a change.
|
||||
networkTimeUpdateListener.assertNotificationsReceivedAndReset(0);
|
||||
script.simulateAsyncRunnableExecution();
|
||||
networkTimeUpdateListener.assertNotificationsReceivedAndReset(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -885,7 +896,10 @@ public class TimeDetectorStrategyImplTest {
|
||||
new ConfigurationInternal.Builder(CONFIG_AUTO_DISABLED)
|
||||
.setOriginPriorities(ORIGIN_NETWORK)
|
||||
.build();
|
||||
Script script = new Script().simulateConfigurationInternalChange(configInternal);
|
||||
TestStateChangeListener networkTimeUpdateListener = new TestStateChangeListener();
|
||||
Script script = new Script()
|
||||
.simulateConfigurationInternalChange(configInternal)
|
||||
.addNetworkTimeUpdateListener(networkTimeUpdateListener);
|
||||
|
||||
NetworkTimeSuggestion timeSuggestion =
|
||||
script.generateNetworkTimeSuggestion(ARBITRARY_TEST_TIME);
|
||||
@@ -894,6 +908,11 @@ public class TimeDetectorStrategyImplTest {
|
||||
.simulateNetworkTimeSuggestion(timeSuggestion)
|
||||
.assertLatestNetworkSuggestion(timeSuggestion)
|
||||
.verifySystemClockWasNotSetAndResetCallTracking();
|
||||
|
||||
// Confirm the network time update listener is notified of a change.
|
||||
networkTimeUpdateListener.assertNotificationsReceivedAndReset(0);
|
||||
script.simulateAsyncRunnableExecution();
|
||||
networkTimeUpdateListener.assertNotificationsReceivedAndReset(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -902,7 +921,11 @@ public class TimeDetectorStrategyImplTest {
|
||||
new ConfigurationInternal.Builder(CONFIG_AUTO_ENABLED)
|
||||
.setOriginPriorities(ORIGIN_NETWORK, ORIGIN_EXTERNAL)
|
||||
.build();
|
||||
Script script = new Script().simulateConfigurationInternalChange(configInternal);
|
||||
|
||||
TestStateChangeListener networkTimeUpdateListener = new TestStateChangeListener();
|
||||
Script script = new Script()
|
||||
.simulateConfigurationInternalChange(configInternal)
|
||||
.addNetworkTimeUpdateListener(networkTimeUpdateListener);
|
||||
|
||||
// Create two different time suggestions for the current elapsedRealtimeMillis.
|
||||
ExternalTimeSuggestion externalTimeSuggestion =
|
||||
@@ -927,6 +950,11 @@ public class TimeDetectorStrategyImplTest {
|
||||
script.simulateNetworkTimeSuggestion(networkTimeSuggestion)
|
||||
.assertLatestNetworkSuggestion(networkTimeSuggestion)
|
||||
.verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis);
|
||||
|
||||
// Confirm the network time update listener is notified of a change.
|
||||
networkTimeUpdateListener.assertNotificationsReceivedAndReset(0);
|
||||
script.simulateAsyncRunnableExecution();
|
||||
networkTimeUpdateListener.assertNotificationsReceivedAndReset(1);
|
||||
}
|
||||
|
||||
// Clear the network time. This should cause the device to change back to the external time,
|
||||
@@ -937,6 +965,11 @@ public class TimeDetectorStrategyImplTest {
|
||||
script.simulateClearLatestNetworkSuggestion()
|
||||
.assertLatestNetworkSuggestion(null)
|
||||
.verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis);
|
||||
|
||||
// Confirm network time update listeners are asynchronously notified of a change.
|
||||
networkTimeUpdateListener.assertNotificationsReceivedAndReset(0);
|
||||
script.simulateAsyncRunnableExecution();
|
||||
networkTimeUpdateListener.assertNotificationsReceivedAndReset(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -947,8 +980,11 @@ public class TimeDetectorStrategyImplTest {
|
||||
.setOriginPriorities(ORIGIN_NETWORK)
|
||||
.setAutoSuggestionLowerBound(TEST_SUGGESTION_LOWER_BOUND)
|
||||
.build();
|
||||
Script script = new Script().simulateConfigurationInternalChange(configInternal)
|
||||
.verifySystemClockConfidence(TIME_CONFIDENCE_LOW);
|
||||
TestStateChangeListener networkTimeUpdateListener = new TestStateChangeListener();
|
||||
Script script = new Script()
|
||||
.simulateConfigurationInternalChange(configInternal)
|
||||
.verifySystemClockConfidence(TIME_CONFIDENCE_LOW)
|
||||
.addNetworkTimeUpdateListener(networkTimeUpdateListener);
|
||||
|
||||
Instant belowLowerBound = TEST_SUGGESTION_LOWER_BOUND.minusSeconds(1);
|
||||
NetworkTimeSuggestion timeSuggestion =
|
||||
@@ -957,6 +993,11 @@ public class TimeDetectorStrategyImplTest {
|
||||
.assertLatestNetworkSuggestion(null)
|
||||
.verifySystemClockConfidence(TIME_CONFIDENCE_LOW)
|
||||
.verifySystemClockWasNotSetAndResetCallTracking();
|
||||
|
||||
// Confirm the network time update listener is not notified of a change.
|
||||
networkTimeUpdateListener.assertNotificationsReceivedAndReset(0);
|
||||
script.simulateAsyncRunnableExecution();
|
||||
networkTimeUpdateListener.assertNotificationsReceivedAndReset(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -966,8 +1007,10 @@ public class TimeDetectorStrategyImplTest {
|
||||
.setOriginPriorities(ORIGIN_NETWORK)
|
||||
.setAutoSuggestionLowerBound(TEST_SUGGESTION_LOWER_BOUND)
|
||||
.build();
|
||||
TestStateChangeListener networkTimeUpdateListener = new TestStateChangeListener();
|
||||
Script script = new Script().simulateConfigurationInternalChange(configInternal)
|
||||
.verifySystemClockConfidence(TIME_CONFIDENCE_LOW);
|
||||
.verifySystemClockConfidence(TIME_CONFIDENCE_LOW)
|
||||
.addNetworkTimeUpdateListener(networkTimeUpdateListener);
|
||||
|
||||
Instant aboveLowerBound = TEST_SUGGESTION_LOWER_BOUND.plusSeconds(1);
|
||||
NetworkTimeSuggestion timeSuggestion =
|
||||
@@ -976,6 +1019,11 @@ public class TimeDetectorStrategyImplTest {
|
||||
.assertLatestNetworkSuggestion(timeSuggestion)
|
||||
.verifySystemClockConfidence(TIME_CONFIDENCE_HIGH)
|
||||
.verifySystemClockWasSetAndResetCallTracking(aboveLowerBound.toEpochMilli());
|
||||
|
||||
// Confirm the network time update listener is notified of a change.
|
||||
networkTimeUpdateListener.assertNotificationsReceivedAndReset(0);
|
||||
script.simulateAsyncRunnableExecution();
|
||||
networkTimeUpdateListener.assertNotificationsReceivedAndReset(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -985,8 +1033,10 @@ public class TimeDetectorStrategyImplTest {
|
||||
.setOriginPriorities(ORIGIN_NETWORK)
|
||||
.setSuggestionUpperBound(TEST_SUGGESTION_UPPER_BOUND)
|
||||
.build();
|
||||
TestStateChangeListener networkTimeUpdateListener = new TestStateChangeListener();
|
||||
Script script = new Script().simulateConfigurationInternalChange(configInternal)
|
||||
.verifySystemClockConfidence(TIME_CONFIDENCE_LOW);
|
||||
.verifySystemClockConfidence(TIME_CONFIDENCE_LOW)
|
||||
.addNetworkTimeUpdateListener(networkTimeUpdateListener);
|
||||
|
||||
Instant aboveUpperBound = TEST_SUGGESTION_UPPER_BOUND.plusSeconds(1);
|
||||
NetworkTimeSuggestion timeSuggestion =
|
||||
@@ -995,6 +1045,11 @@ public class TimeDetectorStrategyImplTest {
|
||||
.assertLatestNetworkSuggestion(null)
|
||||
.verifySystemClockConfidence(TIME_CONFIDENCE_LOW)
|
||||
.verifySystemClockWasNotSetAndResetCallTracking();
|
||||
|
||||
// Confirm the network time update listener is not notified of a change.
|
||||
networkTimeUpdateListener.assertNotificationsReceivedAndReset(0);
|
||||
script.simulateAsyncRunnableExecution();
|
||||
networkTimeUpdateListener.assertNotificationsReceivedAndReset(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1004,8 +1059,10 @@ public class TimeDetectorStrategyImplTest {
|
||||
.setOriginPriorities(ORIGIN_NETWORK)
|
||||
.setSuggestionUpperBound(TEST_SUGGESTION_UPPER_BOUND)
|
||||
.build();
|
||||
TestStateChangeListener networkTimeUpdateListener = new TestStateChangeListener();
|
||||
Script script = new Script().simulateConfigurationInternalChange(configInternal)
|
||||
.verifySystemClockConfidence(TIME_CONFIDENCE_LOW);
|
||||
.verifySystemClockConfidence(TIME_CONFIDENCE_LOW)
|
||||
.addNetworkTimeUpdateListener(networkTimeUpdateListener);
|
||||
|
||||
Instant belowUpperBound = TEST_SUGGESTION_UPPER_BOUND.minusSeconds(1);
|
||||
NetworkTimeSuggestion timeSuggestion =
|
||||
@@ -1014,6 +1071,11 @@ public class TimeDetectorStrategyImplTest {
|
||||
.assertLatestNetworkSuggestion(timeSuggestion)
|
||||
.verifySystemClockConfidence(TIME_CONFIDENCE_HIGH)
|
||||
.verifySystemClockWasSetAndResetCallTracking(belowUpperBound.toEpochMilli());
|
||||
|
||||
// Confirm the network time update listener is notified of a change.
|
||||
networkTimeUpdateListener.assertNotificationsReceivedAndReset(0);
|
||||
script.simulateAsyncRunnableExecution();
|
||||
networkTimeUpdateListener.assertNotificationsReceivedAndReset(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1800,7 +1862,10 @@ public class TimeDetectorStrategyImplTest {
|
||||
new ConfigurationInternal.Builder(CONFIG_AUTO_ENABLED)
|
||||
.setOriginPriorities(ORIGIN_TELEPHONY)
|
||||
.build();
|
||||
Script script = new Script().simulateConfigurationInternalChange(configInternal);
|
||||
TestStateChangeListener networkTimeUpdateListener = new TestStateChangeListener();
|
||||
Script script = new Script()
|
||||
.simulateConfigurationInternalChange(configInternal)
|
||||
.addNetworkTimeUpdateListener(networkTimeUpdateListener);
|
||||
|
||||
NetworkTimeSuggestion timeSuggestion = script.generateNetworkTimeSuggestion(
|
||||
ARBITRARY_TEST_TIME);
|
||||
@@ -1809,6 +1874,11 @@ public class TimeDetectorStrategyImplTest {
|
||||
.assertLatestNetworkSuggestion(timeSuggestion)
|
||||
.assertLatestNetworkSuggestion(timeSuggestion)
|
||||
.verifySystemClockWasNotSetAndResetCallTracking();
|
||||
|
||||
// Confirm the network time update listener is notified of a change.
|
||||
networkTimeUpdateListener.assertNotificationsReceivedAndReset(0);
|
||||
script.simulateAsyncRunnableExecution();
|
||||
networkTimeUpdateListener.assertNotificationsReceivedAndReset(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1867,6 +1937,8 @@ public class TimeDetectorStrategyImplTest {
|
||||
*/
|
||||
private static class FakeEnvironment implements TimeDetectorStrategyImpl.Environment {
|
||||
|
||||
private final List<Runnable> mAsyncRunnables = new ArrayList<>();
|
||||
|
||||
private ConfigurationInternal mConfigurationInternal;
|
||||
private boolean mWakeLockAcquired;
|
||||
private long mElapsedRealtimeMillis;
|
||||
@@ -1951,8 +2023,20 @@ public class TimeDetectorStrategyImplTest {
|
||||
// No-op for tests
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runAsync(Runnable runnable) {
|
||||
mAsyncRunnables.add(runnable);
|
||||
}
|
||||
|
||||
// Methods below are for managing the fake's behavior.
|
||||
|
||||
void runAsyncRunnables() {
|
||||
for (Runnable runnable : mAsyncRunnables) {
|
||||
runnable.run();
|
||||
}
|
||||
mAsyncRunnables.clear();
|
||||
}
|
||||
|
||||
void simulateConfigurationInternalChange(ConfigurationInternal configurationInternal) {
|
||||
mConfigurationInternal = configurationInternal;
|
||||
mConfigurationInternalChangeListener.onChange();
|
||||
@@ -1992,7 +2076,7 @@ public class TimeDetectorStrategyImplTest {
|
||||
assertEquals(expectedSystemClockMillis, mSystemClockMillis);
|
||||
}
|
||||
|
||||
public void verifySystemClockConfidenceLatest(@TimeConfidence int expectedConfidence) {
|
||||
void verifySystemClockConfidenceLatest(@TimeConfidence int expectedConfidence) {
|
||||
assertEquals(expectedConfidence, mSystemClockConfidence);
|
||||
}
|
||||
|
||||
@@ -2118,6 +2202,17 @@ public class TimeDetectorStrategyImplTest {
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Calls {@link TimeDetectorStrategy#addNetworkTimeUpdateListener(StateChangeListener)}. */
|
||||
Script addNetworkTimeUpdateListener(StateChangeListener listener) {
|
||||
mTimeDetectorStrategy.addNetworkTimeUpdateListener(listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
Script simulateAsyncRunnableExecution() {
|
||||
mFakeEnvironment.runAsyncRunnables();
|
||||
return this;
|
||||
}
|
||||
|
||||
Script verifySystemClockWasNotSetAndResetCallTracking() {
|
||||
mFakeEnvironment.verifySystemClockNotSet();
|
||||
mFakeEnvironment.resetCallTracking();
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2023 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 org.junit.Assert.assertEquals;
|
||||
|
||||
public class TestStateChangeListener implements StateChangeListener {
|
||||
|
||||
private int mNotificationsReceived;
|
||||
|
||||
@Override
|
||||
public void onChange() {
|
||||
mNotificationsReceived++;
|
||||
}
|
||||
|
||||
public void assertNotificationsReceivedAndReset(int expectedCount) {
|
||||
assertNotificationsReceived(expectedCount);
|
||||
resetNotificationsReceivedCount();
|
||||
}
|
||||
|
||||
private void resetNotificationsReceivedCount() {
|
||||
mNotificationsReceived = 0;
|
||||
}
|
||||
|
||||
private void assertNotificationsReceived(int expectedCount) {
|
||||
assertEquals(expectedCount, mNotificationsReceived);
|
||||
}
|
||||
}
|
||||
@@ -2001,26 +2001,4 @@ public class TimeZoneDetectorStrategyImplTest {
|
||||
return new TelephonyTestCase(matchType, quality, expectedScore);
|
||||
}
|
||||
|
||||
private static class TestStateChangeListener implements StateChangeListener {
|
||||
|
||||
private int mNotificationsReceived;
|
||||
|
||||
@Override
|
||||
public void onChange() {
|
||||
mNotificationsReceived++;
|
||||
}
|
||||
|
||||
public void assertNotificationsReceivedAndReset(int expectedCount) {
|
||||
assertNotificationsReceived(expectedCount);
|
||||
resetNotificationsReceivedCount();
|
||||
}
|
||||
|
||||
private void resetNotificationsReceivedCount() {
|
||||
mNotificationsReceived = 0;
|
||||
}
|
||||
|
||||
private void assertNotificationsReceived(int expectedCount) {
|
||||
assertEquals(expectedCount, mNotificationsReceived);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user