Merge "Change NetworkTimeUpdateServiceImpl behavior"
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
package android.app.timedetector;
|
||||
|
||||
import android.app.timedetector.ManualTimeSuggestion;
|
||||
import android.app.timedetector.NetworkTimeSuggestion;
|
||||
import android.app.timedetector.PhoneTimeSuggestion;
|
||||
|
||||
/**
|
||||
@@ -35,4 +36,5 @@ import android.app.timedetector.PhoneTimeSuggestion;
|
||||
interface ITimeDetectorService {
|
||||
void suggestPhoneTime(in PhoneTimeSuggestion timeSuggestion);
|
||||
void suggestManualTime(in ManualTimeSuggestion timeSuggestion);
|
||||
void suggestNetworkTime(in NetworkTimeSuggestion timeSuggestion);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.app.timedetector;
|
||||
|
||||
parcelable NetworkTimeSuggestion;
|
||||
129
core/java/android/app/timedetector/NetworkTimeSuggestion.java
Normal file
129
core/java/android/app/timedetector/NetworkTimeSuggestion.java
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.app.timedetector;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.TimestampedValue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A time signal from a network time source like NTP. The value consists of the number of
|
||||
* milliseconds elapsed since 1/1/1970 00:00:00 UTC and the time according to the elapsed realtime
|
||||
* clock when that number was established. The elapsed realtime clock is considered accurate but
|
||||
* volatile, so time signals must not be persisted across device resets.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class NetworkTimeSuggestion implements Parcelable {
|
||||
|
||||
public static final @NonNull Creator<NetworkTimeSuggestion> CREATOR =
|
||||
new Creator<NetworkTimeSuggestion>() {
|
||||
public NetworkTimeSuggestion createFromParcel(Parcel in) {
|
||||
return NetworkTimeSuggestion.createFromParcel(in);
|
||||
}
|
||||
|
||||
public NetworkTimeSuggestion[] newArray(int size) {
|
||||
return new NetworkTimeSuggestion[size];
|
||||
}
|
||||
};
|
||||
|
||||
@NonNull
|
||||
private final TimestampedValue<Long> mUtcTime;
|
||||
@Nullable
|
||||
private ArrayList<String> mDebugInfo;
|
||||
|
||||
public NetworkTimeSuggestion(@NonNull TimestampedValue<Long> utcTime) {
|
||||
mUtcTime = Objects.requireNonNull(utcTime);
|
||||
Objects.requireNonNull(utcTime.getValue());
|
||||
}
|
||||
|
||||
private static NetworkTimeSuggestion createFromParcel(Parcel in) {
|
||||
TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */);
|
||||
NetworkTimeSuggestion suggestion = new NetworkTimeSuggestion(utcTime);
|
||||
@SuppressWarnings("unchecked")
|
||||
ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
|
||||
suggestion.mDebugInfo = debugInfo;
|
||||
return suggestion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeParcelable(mUtcTime, 0);
|
||||
dest.writeList(mDebugInfo);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public TimestampedValue<Long> getUtcTime() {
|
||||
return mUtcTime;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<String> getDebugInfo() {
|
||||
return mDebugInfo == null
|
||||
? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates information with the instance that can be useful for debugging / logging. The
|
||||
* information is present in {@link #toString()} but is not considered for
|
||||
* {@link #equals(Object)} and {@link #hashCode()}.
|
||||
*/
|
||||
public void addDebugInfo(String... debugInfos) {
|
||||
if (mDebugInfo == null) {
|
||||
mDebugInfo = new ArrayList<>();
|
||||
}
|
||||
mDebugInfo.addAll(Arrays.asList(debugInfos));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
NetworkTimeSuggestion that = (NetworkTimeSuggestion) o;
|
||||
return Objects.equals(mUtcTime, that.mUtcTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mUtcTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NetworkTimeSuggestion{"
|
||||
+ "mUtcTime=" + mUtcTime
|
||||
+ ", mDebugInfo=" + mDebugInfo
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
@@ -85,4 +85,19 @@ public class TimeDetector {
|
||||
manualTimeSuggestion.addDebugInfo(why);
|
||||
return manualTimeSuggestion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Suggests the time according to a network time source like NTP.
|
||||
*/
|
||||
@RequiresPermission(android.Manifest.permission.SET_TIME)
|
||||
public void suggestNetworkTime(NetworkTimeSuggestion timeSuggestion) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "suggestNetworkTime called: " + timeSuggestion);
|
||||
}
|
||||
try {
|
||||
mITimeDetectorService.suggestNetworkTime(timeSuggestion);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,4 +175,22 @@ public class NtpTrustedTime implements TrustedTime {
|
||||
public long getCachedNtpTimeReference() {
|
||||
return mCachedNtpElapsedRealtime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the combination of {@link #getCachedNtpTime()} and {@link
|
||||
* #getCachedNtpTimeReference()} as a {@link TimestampedValue}. This method is useful when
|
||||
* passing the time to another component that will adjust for elapsed time.
|
||||
*
|
||||
* @throws IllegalStateException if there is no cached value
|
||||
*/
|
||||
@UnsupportedAppUsage
|
||||
public TimestampedValue<Long> getCachedNtpTimeSignal() {
|
||||
if (!mHasCache) {
|
||||
throw new IllegalStateException("Missing authoritative time source");
|
||||
}
|
||||
if (LOGD) Log.d(TAG, "currentTimeMillis() cache hit");
|
||||
|
||||
return new TimestampedValue<>(mCachedNtpElapsedRealtime, mCachedNtpTime);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2266,9 +2266,6 @@
|
||||
<!-- Number of times to try again with the shorter interval, before backing
|
||||
off until the normal polling interval. A value < 0 indicates infinite. -->
|
||||
<integer name="config_ntpRetry">3</integer>
|
||||
<!-- If the time difference is greater than this threshold in milliseconds,
|
||||
then update the time. -->
|
||||
<integer name="config_ntpThreshold">5000</integer>
|
||||
<!-- Timeout to wait for NTP server response in milliseconds. -->
|
||||
<integer name="config_ntpTimeout">5000</integer>
|
||||
|
||||
|
||||
@@ -489,7 +489,6 @@
|
||||
<java-symbol type="integer" name="config_ntpPollingInterval" />
|
||||
<java-symbol type="integer" name="config_ntpPollingIntervalShorter" />
|
||||
<java-symbol type="integer" name="config_ntpRetry" />
|
||||
<java-symbol type="integer" name="config_ntpThreshold" />
|
||||
<java-symbol type="integer" name="config_ntpTimeout" />
|
||||
<java-symbol type="integer" name="config_shortPressOnPowerBehavior" />
|
||||
<java-symbol type="integer" name="config_toastDefaultGravity" />
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.app.timedetector;
|
||||
|
||||
import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
|
||||
import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
|
||||
import android.util.TimestampedValue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class NetworkTimeSuggestionTest {
|
||||
|
||||
private static final TimestampedValue<Long> ARBITRARY_TIME =
|
||||
new TimestampedValue<>(1111L, 2222L);
|
||||
|
||||
@Test
|
||||
public void testEquals() {
|
||||
NetworkTimeSuggestion one = new NetworkTimeSuggestion(ARBITRARY_TIME);
|
||||
assertEquals(one, one);
|
||||
|
||||
NetworkTimeSuggestion two = new NetworkTimeSuggestion(ARBITRARY_TIME);
|
||||
assertEquals(one, two);
|
||||
assertEquals(two, one);
|
||||
|
||||
TimestampedValue<Long> differentTime = new TimestampedValue<>(
|
||||
ARBITRARY_TIME.getReferenceTimeMillis() + 1,
|
||||
ARBITRARY_TIME.getValue());
|
||||
NetworkTimeSuggestion three = new NetworkTimeSuggestion(differentTime);
|
||||
assertNotEquals(one, three);
|
||||
assertNotEquals(three, one);
|
||||
|
||||
// DebugInfo must not be considered in equals().
|
||||
one.addDebugInfo("Debug info 1");
|
||||
two.addDebugInfo("Debug info 2");
|
||||
assertEquals(one, two);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParcelable() {
|
||||
NetworkTimeSuggestion suggestion = new NetworkTimeSuggestion(ARBITRARY_TIME);
|
||||
assertRoundTripParcelable(suggestion);
|
||||
|
||||
// DebugInfo should also be stored (but is not checked by equals()
|
||||
suggestion.addDebugInfo("This is debug info");
|
||||
NetworkTimeSuggestion rtSuggestion = roundTripParcelable(suggestion);
|
||||
assertEquals(suggestion.getDebugInfo(), rtSuggestion.getDebugInfo());
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,8 @@ package com.android.server;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.timedetector.NetworkTimeSuggestion;
|
||||
import android.app.timedetector.TimeDetector;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
@@ -35,10 +37,10 @@ import android.os.Message;
|
||||
import android.os.PowerManager;
|
||||
import android.os.SystemClock;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.Log;
|
||||
import android.util.NtpTrustedTime;
|
||||
import android.util.TimeUtils;
|
||||
import android.util.TimestampedValue;
|
||||
|
||||
import com.android.internal.util.DumpUtils;
|
||||
|
||||
@@ -46,21 +48,19 @@ import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* Monitors the network time and updates the system time if it is out of sync
|
||||
* and there hasn't been any NITZ update from the carrier recently.
|
||||
* If looking up the network time fails for some reason, it tries a few times with a short
|
||||
* interval and then resets to checking on longer intervals.
|
||||
* <p>
|
||||
* If the user enables AUTO_TIME, it will check immediately for the network time, if NITZ wasn't
|
||||
* available.
|
||||
* </p>
|
||||
* Monitors the network time. If looking up the network time fails for some reason, it tries a few
|
||||
* times with a short interval and then resets to checking on longer intervals.
|
||||
*
|
||||
* <p>When available, the time is always suggested to the {@link
|
||||
* com.android.server.timedetector.TimeDetectorService} where it may be used to set the device
|
||||
* system clock, depending on user settings and what other signals are available.
|
||||
*/
|
||||
public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeUpdateService {
|
||||
|
||||
private static final String TAG = "NetworkTimeUpdateService";
|
||||
private static final boolean DBG = false;
|
||||
|
||||
private static final int EVENT_AUTO_TIME_CHANGED = 1;
|
||||
private static final int EVENT_AUTO_TIME_ENABLED = 1;
|
||||
private static final int EVENT_POLL_NETWORK_TIME = 2;
|
||||
private static final int EVENT_NETWORK_CHANGED = 3;
|
||||
|
||||
@@ -69,20 +69,19 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU
|
||||
|
||||
private static final int POLL_REQUEST = 0;
|
||||
|
||||
private static final long NOT_SET = -1;
|
||||
private long mNitzTimeSetTime = NOT_SET;
|
||||
private Network mDefaultNetwork = null;
|
||||
|
||||
private final Context mContext;
|
||||
private final NtpTrustedTime mTime;
|
||||
private final AlarmManager mAlarmManager;
|
||||
private final TimeDetector mTimeDetector;
|
||||
private final ConnectivityManager mCM;
|
||||
private final PendingIntent mPendingPollIntent;
|
||||
private final PowerManager.WakeLock mWakeLock;
|
||||
|
||||
// NTP lookup is done on this thread and handler
|
||||
private Handler mHandler;
|
||||
private SettingsObserver mSettingsObserver;
|
||||
private AutoTimeSettingObserver mAutoTimeSettingObserver;
|
||||
private NetworkTimeUpdateCallback mNetworkTimeUpdateCallback;
|
||||
|
||||
// Normal polling frequency
|
||||
@@ -91,8 +90,6 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU
|
||||
private final long mPollingIntervalShorterMs;
|
||||
// Number of times to try again
|
||||
private final int mTryAgainTimesMax;
|
||||
// If the time difference is greater than this threshold, then update the time.
|
||||
private final int mTimeErrorThresholdMs;
|
||||
// Keeps track of how many quick attempts were made to fetch NTP time.
|
||||
// During bootup, the network may not have been up yet, or it's taking time for the
|
||||
// connection to happen.
|
||||
@@ -102,6 +99,7 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU
|
||||
mContext = context;
|
||||
mTime = NtpTrustedTime.getInstance(context);
|
||||
mAlarmManager = mContext.getSystemService(AlarmManager.class);
|
||||
mTimeDetector = mContext.getSystemService(TimeDetector.class);
|
||||
mCM = mContext.getSystemService(ConnectivityManager.class);
|
||||
|
||||
Intent pollIntent = new Intent(ACTION_POLL, null);
|
||||
@@ -113,8 +111,6 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU
|
||||
com.android.internal.R.integer.config_ntpPollingIntervalShorter);
|
||||
mTryAgainTimesMax = mContext.getResources().getInteger(
|
||||
com.android.internal.R.integer.config_ntpRetry);
|
||||
mTimeErrorThresholdMs = mContext.getResources().getInteger(
|
||||
com.android.internal.R.integer.config_ntpThreshold);
|
||||
|
||||
mWakeLock = context.getSystemService(PowerManager.class).newWakeLock(
|
||||
PowerManager.PARTIAL_WAKE_LOCK, TAG);
|
||||
@@ -122,7 +118,6 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU
|
||||
|
||||
@Override
|
||||
public void systemRunning() {
|
||||
registerForTelephonyIntents();
|
||||
registerForAlarms();
|
||||
|
||||
HandlerThread thread = new HandlerThread(TAG);
|
||||
@@ -131,14 +126,9 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU
|
||||
mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
|
||||
mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
|
||||
|
||||
mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
|
||||
mSettingsObserver.observe(mContext);
|
||||
}
|
||||
|
||||
private void registerForTelephonyIntents() {
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(TelephonyManager.ACTION_NETWORK_SET_TIME);
|
||||
mContext.registerReceiver(mNitzReceiver, intentFilter);
|
||||
mAutoTimeSettingObserver = new AutoTimeSettingObserver(mContext, mHandler,
|
||||
EVENT_AUTO_TIME_ENABLED);
|
||||
mAutoTimeSettingObserver.observe();
|
||||
}
|
||||
|
||||
private void registerForAlarms() {
|
||||
@@ -152,8 +142,7 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU
|
||||
}
|
||||
|
||||
private void onPollNetworkTime(int event) {
|
||||
// If Automatic time is not set, don't bother. Similarly, if we don't
|
||||
// have any default network, don't bother.
|
||||
// If we don't have any default network, don't bother.
|
||||
if (mDefaultNetwork == null) return;
|
||||
mWakeLock.acquire();
|
||||
try {
|
||||
@@ -173,10 +162,12 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU
|
||||
if (mTime.getCacheAge() < mPollingIntervalMs) {
|
||||
// Obtained fresh fix; schedule next normal update
|
||||
resetAlarm(mPollingIntervalMs);
|
||||
if (isAutomaticTimeRequested()) {
|
||||
updateSystemClock(event);
|
||||
}
|
||||
|
||||
// Suggest the time to the time detector. It may choose use it to set the system clock.
|
||||
TimestampedValue<Long> timeSignal = mTime.getCachedNtpTimeSignal();
|
||||
NetworkTimeSuggestion timeSuggestion = new NetworkTimeSuggestion(timeSignal);
|
||||
timeSuggestion.addDebugInfo("Origin: NetworkTimeUpdateServiceImpl. event=" + event);
|
||||
mTimeDetector.suggestNetworkTime(timeSuggestion);
|
||||
} else {
|
||||
// No fresh fix; schedule retry
|
||||
mTryAgainCounter++;
|
||||
@@ -190,36 +181,6 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU
|
||||
}
|
||||
}
|
||||
|
||||
private long getNitzAge() {
|
||||
if (mNitzTimeSetTime == NOT_SET) {
|
||||
return Long.MAX_VALUE;
|
||||
} else {
|
||||
return SystemClock.elapsedRealtime() - mNitzTimeSetTime;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Consider updating system clock based on current NTP fix, if requested by
|
||||
* user, significant enough delta, and we don't have a recent NITZ.
|
||||
*/
|
||||
private void updateSystemClock(int event) {
|
||||
final boolean forceUpdate = (event == EVENT_AUTO_TIME_CHANGED);
|
||||
if (!forceUpdate) {
|
||||
if (getNitzAge() < mPollingIntervalMs) {
|
||||
if (DBG) Log.d(TAG, "Ignoring NTP update due to recent NITZ");
|
||||
return;
|
||||
}
|
||||
|
||||
final long skew = Math.abs(mTime.currentTimeMillis() - System.currentTimeMillis());
|
||||
if (skew < mTimeErrorThresholdMs) {
|
||||
if (DBG) Log.d(TAG, "Ignoring NTP update due to low skew");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SystemClock.setCurrentTimeMillis(mTime.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel old alarm and starts a new one for the specified interval.
|
||||
*
|
||||
@@ -232,27 +193,6 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU
|
||||
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the user prefers to automatically set the time.
|
||||
*/
|
||||
private boolean isAutomaticTimeRequested() {
|
||||
return Settings.Global.getInt(
|
||||
mContext.getContentResolver(), Settings.Global.AUTO_TIME, 0) != 0;
|
||||
}
|
||||
|
||||
/** Receiver for Nitz time events */
|
||||
private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
if (DBG) Log.d(TAG, "Received " + action);
|
||||
if (TelephonyManager.ACTION_NETWORK_SET_TIME.equals(action)) {
|
||||
mNitzTimeSetTime = SystemClock.elapsedRealtime();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** Handler to do the network accesses on */
|
||||
private class MyHandler extends Handler {
|
||||
|
||||
@@ -263,7 +203,7 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case EVENT_AUTO_TIME_CHANGED:
|
||||
case EVENT_AUTO_TIME_ENABLED:
|
||||
case EVENT_POLL_NETWORK_TIME:
|
||||
case EVENT_NETWORK_CHANGED:
|
||||
onPollNetworkTime(msg.what);
|
||||
@@ -287,27 +227,42 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU
|
||||
}
|
||||
}
|
||||
|
||||
/** Observer to watch for changes to the AUTO_TIME setting */
|
||||
private static class SettingsObserver extends ContentObserver {
|
||||
/**
|
||||
* Observer to watch for changes to the AUTO_TIME setting. It only triggers when the setting
|
||||
* is enabled.
|
||||
*/
|
||||
private static class AutoTimeSettingObserver extends ContentObserver {
|
||||
|
||||
private int mMsg;
|
||||
private Handler mHandler;
|
||||
private final Context mContext;
|
||||
private final int mMsg;
|
||||
private final Handler mHandler;
|
||||
|
||||
SettingsObserver(Handler handler, int msg) {
|
||||
AutoTimeSettingObserver(Context context, Handler handler, int msg) {
|
||||
super(handler);
|
||||
mContext = context;
|
||||
mHandler = handler;
|
||||
mMsg = msg;
|
||||
}
|
||||
|
||||
void observe(Context context) {
|
||||
ContentResolver resolver = context.getContentResolver();
|
||||
void observe() {
|
||||
ContentResolver resolver = mContext.getContentResolver();
|
||||
resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
|
||||
false, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
mHandler.obtainMessage(mMsg).sendToTarget();
|
||||
if (isAutomaticTimeEnabled()) {
|
||||
mHandler.obtainMessage(mMsg).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the user prefers to automatically set the time.
|
||||
*/
|
||||
private boolean isAutomaticTimeEnabled() {
|
||||
ContentResolver resolver = mContext.getContentResolver();
|
||||
return Settings.Global.getInt(resolver, Settings.Global.AUTO_TIME, 0) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,8 +274,6 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU
|
||||
pw.print("\nPollingIntervalShorterMs: ");
|
||||
TimeUtils.formatDuration(mPollingIntervalShorterMs, pw);
|
||||
pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax);
|
||||
pw.print("TimeErrorThresholdMs: ");
|
||||
TimeUtils.formatDuration(mTimeErrorThresholdMs, pw);
|
||||
pw.println("\nTryAgainCounter: " + mTryAgainCounter);
|
||||
pw.println("NTP cache age: " + mTime.getCacheAge());
|
||||
pw.println("NTP cache certainty: " + mTime.getCacheCertainty());
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.timedetector.ITimeDetectorService;
|
||||
import android.app.timedetector.ManualTimeSuggestion;
|
||||
import android.app.timedetector.NetworkTimeSuggestion;
|
||||
import android.app.timedetector.PhoneTimeSuggestion;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
@@ -105,6 +106,14 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub {
|
||||
mHandler.post(() -> mTimeDetectorStrategy.suggestManualTime(timeSignal));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSignal) {
|
||||
enforceSuggestNetworkTimePermission();
|
||||
Objects.requireNonNull(timeSignal);
|
||||
|
||||
mHandler.post(() -> mTimeDetectorStrategy.suggestNetworkTime(timeSignal));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void handleAutoTimeDetectionToggle() {
|
||||
mHandler.post(mTimeDetectorStrategy::handleAutoTimeDetectionChanged);
|
||||
@@ -129,4 +138,10 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub {
|
||||
android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE,
|
||||
"suggest manual time and time zone");
|
||||
}
|
||||
|
||||
private void enforceSuggestNetworkTimePermission() {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
android.Manifest.permission.SET_TIME,
|
||||
"set time");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.server.timedetector;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.timedetector.ManualTimeSuggestion;
|
||||
import android.app.timedetector.NetworkTimeSuggestion;
|
||||
import android.app.timedetector.PhoneTimeSuggestion;
|
||||
import android.content.Intent;
|
||||
import android.util.TimestampedValue;
|
||||
@@ -86,6 +87,9 @@ public interface TimeDetectorStrategy {
|
||||
/** Process the suggested manually entered time. */
|
||||
void suggestManualTime(@NonNull ManualTimeSuggestion timeSuggestion);
|
||||
|
||||
/** Process the suggested time from network sources. */
|
||||
void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSuggestion);
|
||||
|
||||
/** Handle the auto-time setting being toggled on or off. */
|
||||
void handleAutoTimeDetectionChanged();
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.timedetector.ManualTimeSuggestion;
|
||||
import android.app.timedetector.NetworkTimeSuggestion;
|
||||
import android.app.timedetector.PhoneTimeSuggestion;
|
||||
import android.content.Intent;
|
||||
import android.telephony.TelephonyManager;
|
||||
@@ -32,6 +33,7 @@ import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
import com.android.server.timezonedetector.ArrayMapWithHistory;
|
||||
import com.android.server.timezonedetector.ReferenceWithHistory;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -56,11 +58,11 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
|
||||
/** Each bucket is this size. All buckets are equally sized. */
|
||||
@VisibleForTesting
|
||||
static final int PHONE_BUCKET_SIZE_MILLIS = 60 * 60 * 1000;
|
||||
/** Phone suggestions older than this value are considered too old. */
|
||||
/** Phone and network suggestions older than this value are considered too old to be used. */
|
||||
@VisibleForTesting
|
||||
static final long PHONE_MAX_AGE_MILLIS = PHONE_BUCKET_COUNT * PHONE_BUCKET_SIZE_MILLIS;
|
||||
static final long MAX_UTC_TIME_AGE_MILLIS = PHONE_BUCKET_COUNT * PHONE_BUCKET_SIZE_MILLIS;
|
||||
|
||||
@IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL })
|
||||
@IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL, ORIGIN_NETWORK })
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Origin {}
|
||||
|
||||
@@ -72,6 +74,10 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
|
||||
@Origin
|
||||
private static final int ORIGIN_MANUAL = 2;
|
||||
|
||||
/** Used when a time value originated from a network signal. */
|
||||
@Origin
|
||||
private static final int ORIGIN_NETWORK = 3;
|
||||
|
||||
/**
|
||||
* CLOCK_PARANOIA: The maximum difference allowed between the expected system clock time and the
|
||||
* actual system clock time before a warning is logged. Used to help identify situations where
|
||||
@@ -101,9 +107,13 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
|
||||
* will have a small number of telephony devices and phoneIds are assumed to be stable.
|
||||
*/
|
||||
@GuardedBy("this")
|
||||
private ArrayMapWithHistory<Integer, PhoneTimeSuggestion> mSuggestionByPhoneId =
|
||||
private final ArrayMapWithHistory<Integer, PhoneTimeSuggestion> mSuggestionByPhoneId =
|
||||
new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
|
||||
|
||||
@GuardedBy("this")
|
||||
private final ReferenceWithHistory<NetworkTimeSuggestion> mLastNetworkSuggestion =
|
||||
new ReferenceWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
|
||||
|
||||
@Override
|
||||
public void initialize(@NonNull Callback callback) {
|
||||
mCallback = callback;
|
||||
@@ -121,6 +131,19 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
|
||||
setSystemClockIfRequired(ORIGIN_MANUAL, newUtcTime, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSuggestion) {
|
||||
if (!validateSuggestionTime(timeSuggestion.getUtcTime(), timeSuggestion)) {
|
||||
return;
|
||||
}
|
||||
mLastNetworkSuggestion.set(timeSuggestion);
|
||||
|
||||
// Now perform auto time detection. The new suggestion may be used to modify the system
|
||||
// clock.
|
||||
String reason = "New network time suggested. timeSuggestion=" + timeSuggestion;
|
||||
doAutoTimeDetection(reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) {
|
||||
// Empty time suggestion means that telephony network connectivity has been lost.
|
||||
@@ -184,6 +207,11 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
|
||||
mSuggestionByPhoneId.dump(ipw);
|
||||
ipw.decreaseIndent(); // level 2
|
||||
|
||||
ipw.println("Network suggestion history:");
|
||||
ipw.increaseIndent(); // level 2
|
||||
mLastNetworkSuggestion.dump(ipw);
|
||||
ipw.decreaseIndent(); // level 2
|
||||
|
||||
ipw.decreaseIndent(); // level 1
|
||||
ipw.flush();
|
||||
}
|
||||
@@ -253,23 +281,34 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
|
||||
return;
|
||||
}
|
||||
|
||||
// Android devices currently prioritize any telephony over network signals. There are
|
||||
// carrier compliance tests that would need to be changed before we could ignore NITZ or
|
||||
// prefer NTP generally. This check is cheap on devices without phone hardware.
|
||||
PhoneTimeSuggestion bestPhoneSuggestion = findBestPhoneSuggestion();
|
||||
|
||||
// Work out what to do with the best suggestion.
|
||||
if (bestPhoneSuggestion == null) {
|
||||
// There is no good phone suggestion.
|
||||
if (DBG) {
|
||||
Slog.d(LOG_TAG, "Could not determine time: No best phone suggestion."
|
||||
+ " detectionReason=" + detectionReason);
|
||||
}
|
||||
if (bestPhoneSuggestion != null) {
|
||||
final TimestampedValue<Long> newUtcTime = bestPhoneSuggestion.getUtcTime();
|
||||
String cause = "Found good phone suggestion."
|
||||
+ ", bestPhoneSuggestion=" + bestPhoneSuggestion
|
||||
+ ", detectionReason=" + detectionReason;
|
||||
setSystemClockIfRequired(ORIGIN_PHONE, newUtcTime, cause);
|
||||
return;
|
||||
}
|
||||
|
||||
final TimestampedValue<Long> newUtcTime = bestPhoneSuggestion.getUtcTime();
|
||||
String cause = "Found good suggestion."
|
||||
+ ", bestPhoneSuggestion=" + bestPhoneSuggestion
|
||||
+ ", detectionReason=" + detectionReason;
|
||||
setSystemClockIfRequired(ORIGIN_PHONE, newUtcTime, cause);
|
||||
// There is no good phone suggestion, try network.
|
||||
NetworkTimeSuggestion networkSuggestion = findLatestValidNetworkSuggestion();
|
||||
if (networkSuggestion != null) {
|
||||
final TimestampedValue<Long> newUtcTime = networkSuggestion.getUtcTime();
|
||||
String cause = "Found good network suggestion."
|
||||
+ ", networkSuggestion=" + networkSuggestion
|
||||
+ ", detectionReason=" + detectionReason;
|
||||
setSystemClockIfRequired(ORIGIN_NETWORK, newUtcTime, cause);
|
||||
return;
|
||||
}
|
||||
|
||||
if (DBG) {
|
||||
Slog.d(LOG_TAG, "Could not determine time: No best phone or network suggestion."
|
||||
+ " detectionReason=" + detectionReason);
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("this")
|
||||
@@ -348,37 +387,50 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
|
||||
|
||||
private static int scorePhoneSuggestion(
|
||||
long elapsedRealtimeMillis, @NonNull PhoneTimeSuggestion timeSuggestion) {
|
||||
// The score is based on the age since receipt. Suggestions are bucketed so two
|
||||
// suggestions in the same bucket from different phoneIds are scored the same.
|
||||
|
||||
// Validate first.
|
||||
TimestampedValue<Long> utcTime = timeSuggestion.getUtcTime();
|
||||
long referenceTimeMillis = utcTime.getReferenceTimeMillis();
|
||||
if (referenceTimeMillis > elapsedRealtimeMillis) {
|
||||
// Future times are ignored. They imply the reference time was wrong, or the elapsed
|
||||
// realtime clock has gone backwards, neither of which are supportable situations.
|
||||
Slog.w(LOG_TAG, "Existing suggestion found to be in the future. "
|
||||
if (!validateSuggestionUtcTime(elapsedRealtimeMillis, utcTime)) {
|
||||
Slog.w(LOG_TAG, "Existing suggestion found to be invalid "
|
||||
+ " elapsedRealtimeMillis=" + elapsedRealtimeMillis
|
||||
+ ", timeSuggestion=" + timeSuggestion);
|
||||
return PHONE_INVALID_SCORE;
|
||||
}
|
||||
|
||||
long ageMillis = elapsedRealtimeMillis - referenceTimeMillis;
|
||||
// The score is based on the age since receipt. Suggestions are bucketed so two
|
||||
// suggestions in the same bucket from different phoneIds are scored the same.
|
||||
long ageMillis = elapsedRealtimeMillis - utcTime.getReferenceTimeMillis();
|
||||
|
||||
// Any suggestion > MAX_AGE_MILLIS is treated as too old. Although time is relentless and
|
||||
// predictable, the accuracy of the reference time clock may be poor over long periods which
|
||||
// would lead to errors creeping in. Also, in edge cases where a bad suggestion has been
|
||||
// made and never replaced, it could also mean that the time detection code remains
|
||||
// opinionated using a bad invalid suggestion. This caps that edge case at MAX_AGE_MILLIS.
|
||||
if (ageMillis > PHONE_MAX_AGE_MILLIS) {
|
||||
// Turn the age into a discrete value: 0 <= bucketIndex < PHONE_BUCKET_COUNT.
|
||||
int bucketIndex = (int) (ageMillis / PHONE_BUCKET_SIZE_MILLIS);
|
||||
if (bucketIndex >= PHONE_BUCKET_COUNT) {
|
||||
return PHONE_INVALID_SCORE;
|
||||
}
|
||||
|
||||
// Turn the age into a discrete value: 0 <= bucketIndex < MAX_AGE_HOURS.
|
||||
int bucketIndex = (int) (ageMillis / PHONE_BUCKET_SIZE_MILLIS);
|
||||
|
||||
// We want the lowest bucket index to have the highest score. 0 > score >= BUCKET_COUNT.
|
||||
return PHONE_BUCKET_COUNT - bucketIndex;
|
||||
}
|
||||
|
||||
/** Returns the latest, valid, network suggestion. Returns {@code null} if there isn't one. */
|
||||
@GuardedBy("this")
|
||||
@Nullable
|
||||
private NetworkTimeSuggestion findLatestValidNetworkSuggestion() {
|
||||
NetworkTimeSuggestion networkSuggestion = mLastNetworkSuggestion.get();
|
||||
if (networkSuggestion == null) {
|
||||
// No network suggestions received. This is normal if there's no connectivity.
|
||||
return null;
|
||||
}
|
||||
|
||||
TimestampedValue<Long> utcTime = networkSuggestion.getUtcTime();
|
||||
long elapsedRealTimeMillis = mCallback.elapsedRealtimeMillis();
|
||||
if (!validateSuggestionUtcTime(elapsedRealTimeMillis, utcTime)) {
|
||||
// The latest suggestion is not valid, usually due to its age.
|
||||
return null;
|
||||
}
|
||||
|
||||
return networkSuggestion;
|
||||
}
|
||||
|
||||
@GuardedBy("this")
|
||||
private void setSystemClockIfRequired(
|
||||
@Origin int origin, @NonNull TimestampedValue<Long> time, @NonNull String cause) {
|
||||
@@ -415,7 +467,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
|
||||
}
|
||||
|
||||
private static boolean isOriginAutomatic(@Origin int origin) {
|
||||
return origin == ORIGIN_PHONE;
|
||||
return origin != ORIGIN_MANUAL;
|
||||
}
|
||||
|
||||
@GuardedBy("this")
|
||||
@@ -506,6 +558,16 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
|
||||
return findBestPhoneSuggestion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the latest valid network suggestion. Not intended for general use: it is used during
|
||||
* tests to check strategy behavior.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
public NetworkTimeSuggestion findLatestValidNetworkSuggestionForTests() {
|
||||
return findLatestValidNetworkSuggestion();
|
||||
}
|
||||
|
||||
/**
|
||||
* A method used to inspect state during tests. Not intended for general use.
|
||||
*/
|
||||
@@ -514,4 +576,32 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
|
||||
public synchronized PhoneTimeSuggestion getLatestPhoneSuggestion(int phoneId) {
|
||||
return mSuggestionByPhoneId.get(phoneId);
|
||||
}
|
||||
|
||||
/**
|
||||
* A method used to inspect state during tests. Not intended for general use.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
public NetworkTimeSuggestion getLatestNetworkSuggestion() {
|
||||
return mLastNetworkSuggestion.get();
|
||||
}
|
||||
|
||||
private static boolean validateSuggestionUtcTime(
|
||||
long elapsedRealtimeMillis, TimestampedValue<Long> utcTime) {
|
||||
long referenceTimeMillis = utcTime.getReferenceTimeMillis();
|
||||
if (referenceTimeMillis > elapsedRealtimeMillis) {
|
||||
// Future reference times are ignored. They imply the reference time was wrong, or the
|
||||
// elapsed realtime clock used to derive it has gone backwards, neither of which are
|
||||
// supportable situations.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Any suggestion > MAX_AGE_MILLIS is treated as too old. Although time is relentless and
|
||||
// predictable, the accuracy of the reference time clock may be poor over long periods which
|
||||
// would lead to errors creeping in. Also, in edge cases where a bad suggestion has been
|
||||
// made and never replaced, it could also mean that the time detection code remains
|
||||
// opinionated using a bad invalid suggestion. This caps that edge case at MAX_AGE_MILLIS.
|
||||
long ageMillis = elapsedRealtimeMillis - referenceTimeMillis;
|
||||
return ageMillis <= MAX_UTC_TIME_AGE_MILLIS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.timedetector.ManualTimeSuggestion;
|
||||
import android.app.timedetector.NetworkTimeSuggestion;
|
||||
import android.app.timedetector.PhoneTimeSuggestion;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
@@ -143,6 +144,36 @@ public class TimeDetectorServiceTest {
|
||||
mStubbedTimeDetectorStrategy.verifySuggestManualTimeCalled(manualTimeSuggestion);
|
||||
}
|
||||
|
||||
@Test(expected = SecurityException.class)
|
||||
public void testSuggestNetworkTime_withoutPermission() {
|
||||
doThrow(new SecurityException("Mock"))
|
||||
.when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
|
||||
NetworkTimeSuggestion NetworkTimeSuggestion = createNetworkTimeSuggestion();
|
||||
|
||||
try {
|
||||
mTimeDetectorService.suggestNetworkTime(NetworkTimeSuggestion);
|
||||
fail();
|
||||
} finally {
|
||||
verify(mMockContext).enforceCallingOrSelfPermission(
|
||||
eq(android.Manifest.permission.SET_TIME), anyString());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuggestNetworkTime() throws Exception {
|
||||
doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
|
||||
|
||||
NetworkTimeSuggestion NetworkTimeSuggestion = createNetworkTimeSuggestion();
|
||||
mTimeDetectorService.suggestNetworkTime(NetworkTimeSuggestion);
|
||||
mTestHandler.assertTotalMessagesEnqueued(1);
|
||||
|
||||
verify(mMockContext).enforceCallingOrSelfPermission(
|
||||
eq(android.Manifest.permission.SET_TIME), anyString());
|
||||
|
||||
mTestHandler.waitForEmptyQueue();
|
||||
mStubbedTimeDetectorStrategy.verifySuggestNetworkTimeCalled(NetworkTimeSuggestion);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDump() {
|
||||
when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP))
|
||||
@@ -180,11 +211,17 @@ public class TimeDetectorServiceTest {
|
||||
return new ManualTimeSuggestion(timeValue);
|
||||
}
|
||||
|
||||
private static NetworkTimeSuggestion createNetworkTimeSuggestion() {
|
||||
TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L);
|
||||
return new NetworkTimeSuggestion(timeValue);
|
||||
}
|
||||
|
||||
private static class StubbedTimeDetectorStrategy implements TimeDetectorStrategy {
|
||||
|
||||
// Call tracking.
|
||||
private PhoneTimeSuggestion mLastPhoneSuggestion;
|
||||
private ManualTimeSuggestion mLastManualSuggestion;
|
||||
private NetworkTimeSuggestion mLastNetworkSuggestion;
|
||||
private boolean mLastAutoTimeDetectionToggleCalled;
|
||||
private boolean mDumpCalled;
|
||||
|
||||
@@ -204,6 +241,12 @@ public class TimeDetectorServiceTest {
|
||||
mLastManualSuggestion = timeSuggestion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestNetworkTime(NetworkTimeSuggestion timeSuggestion) {
|
||||
resetCallTracking();
|
||||
mLastNetworkSuggestion = timeSuggestion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleAutoTimeDetectionChanged() {
|
||||
resetCallTracking();
|
||||
@@ -219,6 +262,7 @@ public class TimeDetectorServiceTest {
|
||||
void resetCallTracking() {
|
||||
mLastPhoneSuggestion = null;
|
||||
mLastManualSuggestion = null;
|
||||
mLastNetworkSuggestion = null;
|
||||
mLastAutoTimeDetectionToggleCalled = false;
|
||||
mDumpCalled = false;
|
||||
}
|
||||
@@ -231,6 +275,10 @@ public class TimeDetectorServiceTest {
|
||||
assertEquals(expectedSuggestion, mLastManualSuggestion);
|
||||
}
|
||||
|
||||
public void verifySuggestNetworkTimeCalled(NetworkTimeSuggestion expectedSuggestion) {
|
||||
assertEquals(expectedSuggestion, mLastNetworkSuggestion);
|
||||
}
|
||||
|
||||
void verifyHandleAutoTimeDetectionToggleCalled() {
|
||||
assertTrue(mLastAutoTimeDetectionToggleCalled);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.app.timedetector.ManualTimeSuggestion;
|
||||
import android.app.timedetector.NetworkTimeSuggestion;
|
||||
import android.app.timedetector.PhoneTimeSuggestion;
|
||||
import android.content.Intent;
|
||||
import android.icu.util.Calendar;
|
||||
@@ -45,14 +46,16 @@ public class TimeDetectorStrategyImplTest {
|
||||
private static final TimestampedValue<Long> ARBITRARY_CLOCK_INITIALIZATION_INFO =
|
||||
new TimestampedValue<>(
|
||||
123456789L /* realtimeClockMillis */,
|
||||
createUtcTime(1977, 1, 1, 12, 0, 0));
|
||||
createUtcTime(2008, 5, 23, 12, 0, 0));
|
||||
|
||||
/**
|
||||
* An arbitrary time, very different from the {@link #ARBITRARY_CLOCK_INITIALIZATION_INFO}
|
||||
* time. Can be used as the basis for time suggestions.
|
||||
*/
|
||||
private static final long ARBITRARY_TEST_TIME_MILLIS = createUtcTime(2018, 1, 1, 12, 0, 0);
|
||||
|
||||
private static final int ARBITRARY_PHONE_ID = 123456;
|
||||
|
||||
private static final long ONE_DAY_MILLIS = Duration.ofDays(1).toMillis();
|
||||
|
||||
private Script mScript;
|
||||
|
||||
@Before
|
||||
@@ -67,15 +70,16 @@ public class TimeDetectorStrategyImplTest {
|
||||
|
||||
int phoneId = ARBITRARY_PHONE_ID;
|
||||
long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
|
||||
|
||||
PhoneTimeSuggestion timeSuggestion =
|
||||
mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
|
||||
int clockIncrement = 1000;
|
||||
long expectedSystemClockMillis = testTimeMillis + clockIncrement;
|
||||
mScript.simulateTimePassing()
|
||||
.simulatePhoneTimeSuggestion(timeSuggestion);
|
||||
|
||||
mScript.simulateTimePassing(clockIncrement)
|
||||
.simulatePhoneTimeSuggestion(timeSuggestion)
|
||||
.verifySystemClockWasSetAndResetCallTracking(
|
||||
expectedSystemClockMillis, true /* expectNetworkBroadcast */)
|
||||
long expectedSystemClockMillis =
|
||||
mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime());
|
||||
mScript.verifySystemClockWasSetAndResetCallTracking(
|
||||
expectedSystemClockMillis, true /* expectNetworkBroadcast */)
|
||||
.assertLatestPhoneSuggestion(phoneId, timeSuggestion);
|
||||
}
|
||||
|
||||
@@ -94,26 +98,24 @@ public class TimeDetectorStrategyImplTest {
|
||||
|
||||
@Test
|
||||
public void testSuggestPhoneTime_systemClockThreshold() {
|
||||
int systemClockUpdateThresholdMillis = 1000;
|
||||
final int systemClockUpdateThresholdMillis = 1000;
|
||||
final int clockIncrementMillis = 100;
|
||||
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
|
||||
.pokeThresholds(systemClockUpdateThresholdMillis)
|
||||
.pokeAutoTimeDetectionEnabled(true);
|
||||
|
||||
final int clockIncrement = 100;
|
||||
int phoneId = ARBITRARY_PHONE_ID;
|
||||
|
||||
// Send the first time signal. It should be used.
|
||||
{
|
||||
long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
|
||||
PhoneTimeSuggestion timeSuggestion1 =
|
||||
mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
|
||||
TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
|
||||
mScript.generatePhoneTimeSuggestion(phoneId, ARBITRARY_TEST_TIME_MILLIS);
|
||||
|
||||
// Increment the the device clocks to simulate the passage of time.
|
||||
mScript.simulateTimePassing(clockIncrement);
|
||||
mScript.simulateTimePassing(clockIncrementMillis);
|
||||
|
||||
long expectedSystemClockMillis1 =
|
||||
TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
|
||||
mScript.calculateTimeInMillisForNow(timeSuggestion1.getUtcTime());
|
||||
|
||||
mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
|
||||
.verifySystemClockWasSetAndResetCallTracking(
|
||||
@@ -127,7 +129,7 @@ public class TimeDetectorStrategyImplTest {
|
||||
int underThresholdMillis = systemClockUpdateThresholdMillis - 1;
|
||||
PhoneTimeSuggestion timeSuggestion2 = mScript.generatePhoneTimeSuggestion(
|
||||
phoneId, mScript.peekSystemClockMillis() + underThresholdMillis);
|
||||
mScript.simulateTimePassing(clockIncrement)
|
||||
mScript.simulateTimePassing(clockIncrementMillis)
|
||||
.simulatePhoneTimeSuggestion(timeSuggestion2)
|
||||
.verifySystemClockWasNotSetAndResetCallTracking()
|
||||
.assertLatestPhoneSuggestion(phoneId, timeSuggestion2);
|
||||
@@ -138,11 +140,10 @@ public class TimeDetectorStrategyImplTest {
|
||||
PhoneTimeSuggestion timeSuggestion3 = mScript.generatePhoneTimeSuggestion(
|
||||
phoneId,
|
||||
mScript.peekSystemClockMillis() + systemClockUpdateThresholdMillis);
|
||||
mScript.simulateTimePassing(clockIncrement);
|
||||
mScript.simulateTimePassing(clockIncrementMillis);
|
||||
|
||||
long expectedSystemClockMillis3 =
|
||||
TimeDetectorStrategy.getTimeAt(timeSuggestion3.getUtcTime(),
|
||||
mScript.peekElapsedRealtimeMillis());
|
||||
mScript.calculateTimeInMillisForNow(timeSuggestion3.getUtcTime());
|
||||
|
||||
mScript.simulatePhoneTimeSuggestion(timeSuggestion3)
|
||||
.verifySystemClockWasSetAndResetCallTracking(
|
||||
@@ -162,17 +163,16 @@ public class TimeDetectorStrategyImplTest {
|
||||
int phone1Id = ARBITRARY_PHONE_ID;
|
||||
int phone2Id = ARBITRARY_PHONE_ID + 1;
|
||||
long phone1TimeMillis = ARBITRARY_TEST_TIME_MILLIS;
|
||||
long phone2TimeMillis = phone1TimeMillis + 60000;
|
||||
|
||||
final int clockIncrement = 999;
|
||||
long phone2TimeMillis = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(1).toMillis();
|
||||
|
||||
// Make a suggestion with phone2Id.
|
||||
{
|
||||
PhoneTimeSuggestion phone2TimeSuggestion =
|
||||
mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis);
|
||||
mScript.simulateTimePassing(clockIncrement);
|
||||
mScript.simulateTimePassing();
|
||||
|
||||
long expectedSystemClockMillis = phone2TimeMillis + clockIncrement;
|
||||
long expectedSystemClockMillis =
|
||||
mScript.calculateTimeInMillisForNow(phone2TimeSuggestion.getUtcTime());
|
||||
|
||||
mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
|
||||
.verifySystemClockWasSetAndResetCallTracking(
|
||||
@@ -181,15 +181,16 @@ public class TimeDetectorStrategyImplTest {
|
||||
.assertLatestPhoneSuggestion(phone2Id, phone2TimeSuggestion);
|
||||
}
|
||||
|
||||
mScript.simulateTimePassing(clockIncrement);
|
||||
mScript.simulateTimePassing();
|
||||
|
||||
// Now make a different suggestion with phone1Id.
|
||||
{
|
||||
PhoneTimeSuggestion phone1TimeSuggestion =
|
||||
mScript.generatePhoneTimeSuggestion(phone1Id, phone1TimeMillis);
|
||||
mScript.simulateTimePassing(clockIncrement);
|
||||
mScript.simulateTimePassing();
|
||||
|
||||
long expectedSystemClockMillis = phone1TimeMillis + clockIncrement;
|
||||
long expectedSystemClockMillis =
|
||||
mScript.calculateTimeInMillisForNow(phone1TimeSuggestion.getUtcTime());
|
||||
|
||||
mScript.simulatePhoneTimeSuggestion(phone1TimeSuggestion)
|
||||
.verifySystemClockWasSetAndResetCallTracking(
|
||||
@@ -198,14 +199,14 @@ public class TimeDetectorStrategyImplTest {
|
||||
|
||||
}
|
||||
|
||||
mScript.simulateTimePassing(clockIncrement);
|
||||
mScript.simulateTimePassing();
|
||||
|
||||
// Make another suggestion with phone2Id. It should be stored but not used because the
|
||||
// phone1Id suggestion will still "win".
|
||||
{
|
||||
PhoneTimeSuggestion phone2TimeSuggestion =
|
||||
mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis);
|
||||
mScript.simulateTimePassing(clockIncrement);
|
||||
mScript.simulateTimePassing();
|
||||
|
||||
mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
|
||||
.verifySystemClockWasNotSetAndResetCallTracking()
|
||||
@@ -220,9 +221,10 @@ public class TimeDetectorStrategyImplTest {
|
||||
{
|
||||
PhoneTimeSuggestion phone2TimeSuggestion =
|
||||
mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis);
|
||||
mScript.simulateTimePassing(clockIncrement);
|
||||
mScript.simulateTimePassing();
|
||||
|
||||
long expectedSystemClockMillis = phone2TimeMillis + clockIncrement;
|
||||
long expectedSystemClockMillis =
|
||||
mScript.calculateTimeInMillisForNow(phone2TimeSuggestion.getUtcTime());
|
||||
|
||||
mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
|
||||
.verifySystemClockWasSetAndResetCallTracking(
|
||||
@@ -239,7 +241,7 @@ public class TimeDetectorStrategyImplTest {
|
||||
int phoneId = ARBITRARY_PHONE_ID;
|
||||
PhoneTimeSuggestion timeSuggestion =
|
||||
mScript.generatePhoneTimeSuggestion(phoneId, ARBITRARY_TEST_TIME_MILLIS);
|
||||
mScript.simulateTimePassing(1000)
|
||||
mScript.simulateTimePassing()
|
||||
.simulatePhoneTimeSuggestion(timeSuggestion)
|
||||
.verifySystemClockWasNotSetAndResetCallTracking()
|
||||
.assertLatestPhoneSuggestion(phoneId, timeSuggestion);
|
||||
@@ -260,9 +262,8 @@ public class TimeDetectorStrategyImplTest {
|
||||
TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
|
||||
|
||||
// Initialize the strategy / device with a time set from a phone suggestion.
|
||||
mScript.simulateTimePassing(100);
|
||||
long expectedSystemClockMillis1 =
|
||||
TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
|
||||
mScript.simulateTimePassing();
|
||||
long expectedSystemClockMillis1 = mScript.calculateTimeInMillisForNow(utcTime1);
|
||||
mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
|
||||
.verifySystemClockWasSetAndResetCallTracking(
|
||||
expectedSystemClockMillis1, true /* expectNetworkBroadcast */)
|
||||
@@ -299,8 +300,7 @@ public class TimeDetectorStrategyImplTest {
|
||||
long validReferenceTimeMillis = utcTime1.getReferenceTimeMillis() + 100;
|
||||
TimestampedValue<Long> utcTime4 = new TimestampedValue<>(
|
||||
validReferenceTimeMillis, validUtcTimeMillis);
|
||||
long expectedSystemClockMillis4 =
|
||||
TimeDetectorStrategy.getTimeAt(utcTime4, mScript.peekElapsedRealtimeMillis());
|
||||
long expectedSystemClockMillis4 = mScript.calculateTimeInMillisForNow(utcTime4);
|
||||
PhoneTimeSuggestion timeSuggestion4 =
|
||||
createPhoneTimeSuggestion(phoneId, utcTime4);
|
||||
mScript.simulatePhoneTimeSuggestion(timeSuggestion4)
|
||||
@@ -335,8 +335,7 @@ public class TimeDetectorStrategyImplTest {
|
||||
// Simulate more time passing.
|
||||
mScript.simulateTimePassing(clockIncrementMillis);
|
||||
|
||||
long expectedSystemClockMillis1 =
|
||||
TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
|
||||
long expectedSystemClockMillis1 = mScript.calculateTimeInMillisForNow(utcTime1);
|
||||
|
||||
// Turn on auto time detection.
|
||||
mScript.simulateAutoTimeDetectionToggle()
|
||||
@@ -357,8 +356,8 @@ public class TimeDetectorStrategyImplTest {
|
||||
// Simulate more time passing.
|
||||
mScript.simulateTimePassing(clockIncrementMillis);
|
||||
|
||||
long expectedSystemClockMillis2 = TimeDetectorStrategy.getTimeAt(
|
||||
timeSuggestion2.getUtcTime(), mScript.peekElapsedRealtimeMillis());
|
||||
long expectedSystemClockMillis2 =
|
||||
mScript.calculateTimeInMillisForNow(timeSuggestion2.getUtcTime());
|
||||
|
||||
// The new time, though valid, should not be set in the system clock because auto time is
|
||||
// disabled.
|
||||
@@ -382,19 +381,21 @@ public class TimeDetectorStrategyImplTest {
|
||||
long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
|
||||
PhoneTimeSuggestion phoneSuggestion =
|
||||
mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
|
||||
int clockIncrementMillis = 1000;
|
||||
|
||||
mScript.simulateTimePassing(clockIncrementMillis)
|
||||
.simulatePhoneTimeSuggestion(phoneSuggestion)
|
||||
mScript.simulateTimePassing();
|
||||
|
||||
long expectedSystemClockMillis =
|
||||
mScript.calculateTimeInMillisForNow(phoneSuggestion.getUtcTime());
|
||||
mScript.simulatePhoneTimeSuggestion(phoneSuggestion)
|
||||
.verifySystemClockWasSetAndResetCallTracking(
|
||||
testTimeMillis + clockIncrementMillis, true /* expectedNetworkBroadcast */)
|
||||
expectedSystemClockMillis, true /* expectedNetworkBroadcast */)
|
||||
.assertLatestPhoneSuggestion(phoneId, phoneSuggestion);
|
||||
|
||||
// Look inside and check what the strategy considers the current best phone suggestion.
|
||||
assertEquals(phoneSuggestion, mScript.peekBestPhoneSuggestion());
|
||||
|
||||
// Simulate time passing, long enough that phoneSuggestion is now too old.
|
||||
mScript.simulateTimePassing(TimeDetectorStrategyImpl.PHONE_MAX_AGE_MILLIS);
|
||||
mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS);
|
||||
|
||||
// Look inside and check what the strategy considers the current best phone suggestion. It
|
||||
// should still be the, it's just no longer used.
|
||||
@@ -407,13 +408,14 @@ public class TimeDetectorStrategyImplTest {
|
||||
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
|
||||
.pokeAutoTimeDetectionEnabled(false);
|
||||
|
||||
long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
|
||||
ManualTimeSuggestion timeSuggestion = mScript.generateManualTimeSuggestion(testTimeMillis);
|
||||
final int clockIncrement = 1000;
|
||||
long expectedSystemClockMillis = testTimeMillis + clockIncrement;
|
||||
ManualTimeSuggestion timeSuggestion =
|
||||
mScript.generateManualTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS);
|
||||
|
||||
mScript.simulateTimePassing(clockIncrement)
|
||||
.simulateManualTimeSuggestion(timeSuggestion)
|
||||
mScript.simulateTimePassing();
|
||||
|
||||
long expectedSystemClockMillis =
|
||||
mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime());
|
||||
mScript.simulateManualTimeSuggestion(timeSuggestion)
|
||||
.verifySystemClockWasSetAndResetCallTracking(
|
||||
expectedSystemClockMillis, false /* expectNetworkBroadcast */);
|
||||
}
|
||||
@@ -430,21 +432,19 @@ public class TimeDetectorStrategyImplTest {
|
||||
long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
|
||||
PhoneTimeSuggestion phoneTimeSuggestion =
|
||||
mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
|
||||
long expectedAutoClockMillis = phoneTimeSuggestion.getUtcTime().getValue();
|
||||
final int clockIncrement = 1000;
|
||||
|
||||
// Simulate the passage of time.
|
||||
mScript.simulateTimePassing(clockIncrement);
|
||||
expectedAutoClockMillis += clockIncrement;
|
||||
mScript.simulateTimePassing();
|
||||
|
||||
long expectedAutoClockMillis =
|
||||
mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime());
|
||||
mScript.simulatePhoneTimeSuggestion(phoneTimeSuggestion)
|
||||
.verifySystemClockWasSetAndResetCallTracking(
|
||||
expectedAutoClockMillis, true /* expectNetworkBroadcast */)
|
||||
.assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
|
||||
|
||||
// Simulate the passage of time.
|
||||
mScript.simulateTimePassing(clockIncrement);
|
||||
expectedAutoClockMillis += clockIncrement;
|
||||
mScript.simulateTimePassing();
|
||||
|
||||
// Switch to manual.
|
||||
mScript.simulateAutoTimeDetectionToggle()
|
||||
@@ -452,26 +452,29 @@ public class TimeDetectorStrategyImplTest {
|
||||
.assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
|
||||
|
||||
// Simulate the passage of time.
|
||||
mScript.simulateTimePassing(clockIncrement);
|
||||
expectedAutoClockMillis += clockIncrement;
|
||||
mScript.simulateTimePassing();
|
||||
|
||||
// Simulate a manual suggestion 1 day different from the auto suggestion.
|
||||
long manualTimeMillis = testTimeMillis + ONE_DAY_MILLIS;
|
||||
long expectedManualClockMillis = manualTimeMillis;
|
||||
long manualTimeMillis = testTimeMillis + Duration.ofDays(1).toMillis();
|
||||
ManualTimeSuggestion manualTimeSuggestion =
|
||||
mScript.generateManualTimeSuggestion(manualTimeMillis);
|
||||
mScript.simulateTimePassing();
|
||||
|
||||
long expectedManualClockMillis =
|
||||
mScript.calculateTimeInMillisForNow(manualTimeSuggestion.getUtcTime());
|
||||
mScript.simulateManualTimeSuggestion(manualTimeSuggestion)
|
||||
.verifySystemClockWasSetAndResetCallTracking(
|
||||
expectedManualClockMillis, false /* expectNetworkBroadcast */)
|
||||
.assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
|
||||
|
||||
// Simulate the passage of time.
|
||||
mScript.simulateTimePassing(clockIncrement);
|
||||
expectedAutoClockMillis += clockIncrement;
|
||||
mScript.simulateTimePassing();
|
||||
|
||||
// Switch back to auto.
|
||||
mScript.simulateAutoTimeDetectionToggle();
|
||||
|
||||
expectedAutoClockMillis =
|
||||
mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime());
|
||||
mScript.verifySystemClockWasSetAndResetCallTracking(
|
||||
expectedAutoClockMillis, true /* expectNetworkBroadcast */)
|
||||
.assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
|
||||
@@ -492,13 +495,143 @@ public class TimeDetectorStrategyImplTest {
|
||||
|
||||
ManualTimeSuggestion timeSuggestion =
|
||||
mScript.generateManualTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS);
|
||||
final int clockIncrement = 1000;
|
||||
|
||||
mScript.simulateTimePassing(clockIncrement)
|
||||
mScript.simulateTimePassing()
|
||||
.simulateManualTimeSuggestion(timeSuggestion)
|
||||
.verifySystemClockWasNotSetAndResetCallTracking();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuggestNetworkTime_autoTimeEnabled() {
|
||||
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
|
||||
.pokeAutoTimeDetectionEnabled(true);
|
||||
|
||||
NetworkTimeSuggestion timeSuggestion =
|
||||
mScript.generateNetworkTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS);
|
||||
|
||||
mScript.simulateTimePassing();
|
||||
|
||||
long expectedSystemClockMillis =
|
||||
mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime());
|
||||
mScript.simulateNetworkTimeSuggestion(timeSuggestion)
|
||||
.verifySystemClockWasSetAndResetCallTracking(
|
||||
expectedSystemClockMillis, false /* expectNetworkBroadcast */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuggestNetworkTime_autoTimeDisabled() {
|
||||
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
|
||||
.pokeAutoTimeDetectionEnabled(false);
|
||||
|
||||
NetworkTimeSuggestion timeSuggestion =
|
||||
mScript.generateNetworkTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS);
|
||||
|
||||
mScript.simulateTimePassing()
|
||||
.simulateNetworkTimeSuggestion(timeSuggestion)
|
||||
.verifySystemClockWasNotSetAndResetCallTracking();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuggestNetworkTime_phoneSuggestionsBeatNetworkSuggestions() {
|
||||
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
|
||||
.pokeAutoTimeDetectionEnabled(true);
|
||||
|
||||
// Three obviously different times that could not be mistaken for each other.
|
||||
long networkTimeMillis1 = ARBITRARY_TEST_TIME_MILLIS;
|
||||
long networkTimeMillis2 = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(30).toMillis();
|
||||
long phoneTimeMillis = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(60).toMillis();
|
||||
// A small increment used to simulate the passage of time, but not enough to interfere with
|
||||
// macro-level time changes associated with suggestion age.
|
||||
final long smallTimeIncrementMillis = 101;
|
||||
|
||||
// A network suggestion is made. It should be used because there is no phone suggestion.
|
||||
NetworkTimeSuggestion networkTimeSuggestion1 =
|
||||
mScript.generateNetworkTimeSuggestion(networkTimeMillis1);
|
||||
mScript.simulateTimePassing(smallTimeIncrementMillis)
|
||||
.simulateNetworkTimeSuggestion(networkTimeSuggestion1)
|
||||
.verifySystemClockWasSetAndResetCallTracking(
|
||||
mScript.calculateTimeInMillisForNow(networkTimeSuggestion1.getUtcTime()),
|
||||
false /* expectNetworkBroadcast */);
|
||||
|
||||
// Check internal state.
|
||||
mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, null)
|
||||
.assertLatestNetworkSuggestion(networkTimeSuggestion1);
|
||||
assertEquals(networkTimeSuggestion1, mScript.peekLatestValidNetworkSuggestion());
|
||||
assertNull(mScript.peekBestPhoneSuggestion());
|
||||
|
||||
// Simulate a little time passing.
|
||||
mScript.simulateTimePassing(smallTimeIncrementMillis)
|
||||
.verifySystemClockWasNotSetAndResetCallTracking();
|
||||
|
||||
// Now a phone suggestion is made. Phone suggestions are prioritized over network
|
||||
// suggestions so it should "win".
|
||||
PhoneTimeSuggestion phoneTimeSuggestion =
|
||||
mScript.generatePhoneTimeSuggestion(ARBITRARY_PHONE_ID, phoneTimeMillis);
|
||||
mScript.simulateTimePassing(smallTimeIncrementMillis)
|
||||
.simulatePhoneTimeSuggestion(phoneTimeSuggestion)
|
||||
.verifySystemClockWasSetAndResetCallTracking(
|
||||
mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime()),
|
||||
true /* expectNetworkBroadcast */);
|
||||
|
||||
// Check internal state.
|
||||
mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion)
|
||||
.assertLatestNetworkSuggestion(networkTimeSuggestion1);
|
||||
assertEquals(networkTimeSuggestion1, mScript.peekLatestValidNetworkSuggestion());
|
||||
assertEquals(phoneTimeSuggestion, mScript.peekBestPhoneSuggestion());
|
||||
|
||||
// Simulate some significant time passing: half the time allowed before a time signal
|
||||
// becomes "too old to use".
|
||||
mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS / 2)
|
||||
.verifySystemClockWasNotSetAndResetCallTracking();
|
||||
|
||||
// Now another network suggestion is made. Phone suggestions are prioritized over network
|
||||
// suggestions so the latest phone suggestion should still "win".
|
||||
NetworkTimeSuggestion networkTimeSuggestion2 =
|
||||
mScript.generateNetworkTimeSuggestion(networkTimeMillis2);
|
||||
mScript.simulateTimePassing(smallTimeIncrementMillis)
|
||||
.simulateNetworkTimeSuggestion(networkTimeSuggestion2)
|
||||
.verifySystemClockWasNotSetAndResetCallTracking();
|
||||
|
||||
// Check internal state.
|
||||
mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion)
|
||||
.assertLatestNetworkSuggestion(networkTimeSuggestion2);
|
||||
assertEquals(networkTimeSuggestion2, mScript.peekLatestValidNetworkSuggestion());
|
||||
assertEquals(phoneTimeSuggestion, mScript.peekBestPhoneSuggestion());
|
||||
|
||||
// Simulate some significant time passing: half the time allowed before a time signal
|
||||
// becomes "too old to use". This should mean that phoneTimeSuggestion is now too old to be
|
||||
// used but networkTimeSuggestion2 is not.
|
||||
mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS / 2);
|
||||
|
||||
// NOTE: The TimeDetectorStrategyImpl doesn't set an alarm for the point when the last
|
||||
// suggestion it used becomes too old: it requires a new suggestion or an auto-time toggle
|
||||
// to re-run the detection logic. This may change in future but until then we rely on a
|
||||
// steady stream of suggestions to re-evaluate.
|
||||
mScript.verifySystemClockWasNotSetAndResetCallTracking();
|
||||
|
||||
// Check internal state.
|
||||
mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion)
|
||||
.assertLatestNetworkSuggestion(networkTimeSuggestion2);
|
||||
assertEquals(networkTimeSuggestion2, mScript.peekLatestValidNetworkSuggestion());
|
||||
assertNull(mScript.peekBestPhoneSuggestion());
|
||||
|
||||
// Toggle auto-time off and on to force the detection logic to run.
|
||||
mScript.simulateAutoTimeDetectionToggle()
|
||||
.simulateTimePassing(smallTimeIncrementMillis)
|
||||
.simulateAutoTimeDetectionToggle();
|
||||
|
||||
// Verify the latest network time now wins.
|
||||
mScript.verifySystemClockWasSetAndResetCallTracking(
|
||||
mScript.calculateTimeInMillisForNow(networkTimeSuggestion2.getUtcTime()),
|
||||
false /* expectNetworkTimeBroadcast */);
|
||||
|
||||
// Check internal state.
|
||||
mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion)
|
||||
.assertLatestNetworkSuggestion(networkTimeSuggestion2);
|
||||
assertEquals(networkTimeSuggestion2, mScript.peekLatestValidNetworkSuggestion());
|
||||
assertNull(mScript.peekBestPhoneSuggestion());
|
||||
}
|
||||
|
||||
/**
|
||||
* A fake implementation of TimeDetectorStrategy.Callback. Besides tracking changes and behaving
|
||||
* like the real thing should, it also asserts preconditions.
|
||||
@@ -674,6 +807,11 @@ public class TimeDetectorStrategyImplTest {
|
||||
return this;
|
||||
}
|
||||
|
||||
Script simulateNetworkTimeSuggestion(NetworkTimeSuggestion timeSuggestion) {
|
||||
mTimeDetectorStrategy.suggestNetworkTime(timeSuggestion);
|
||||
return this;
|
||||
}
|
||||
|
||||
Script simulateAutoTimeDetectionToggle() {
|
||||
mFakeCallback.simulateAutoTimeZoneDetectionToggle();
|
||||
mTimeDetectorStrategy.handleAutoTimeDetectionChanged();
|
||||
@@ -685,6 +823,13 @@ public class TimeDetectorStrategyImplTest {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates time passing by an arbitrary (but relatively small) amount.
|
||||
*/
|
||||
Script simulateTimePassing() {
|
||||
return simulateTimePassing(999);
|
||||
}
|
||||
|
||||
Script verifySystemClockWasNotSetAndResetCallTracking() {
|
||||
mFakeCallback.verifySystemClockNotSet();
|
||||
mFakeCallback.verifyIntentWasNotBroadcast();
|
||||
@@ -710,14 +855,30 @@ public class TimeDetectorStrategyImplTest {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* White box test info: Asserts the latest network suggestion is as expected.
|
||||
*/
|
||||
Script assertLatestNetworkSuggestion(NetworkTimeSuggestion expected) {
|
||||
assertEquals(expected, mTimeDetectorStrategy.getLatestNetworkSuggestion());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* White box test info: Returns the phone suggestion that would be used, if any, given the
|
||||
* current elapsed real time clock.
|
||||
* current elapsed real time clock and regardless of origin prioritization.
|
||||
*/
|
||||
PhoneTimeSuggestion peekBestPhoneSuggestion() {
|
||||
return mTimeDetectorStrategy.findBestPhoneSuggestionForTests();
|
||||
}
|
||||
|
||||
/**
|
||||
* White box test info: Returns the network suggestion that would be used, if any, given the
|
||||
* current elapsed real time clock and regardless of origin prioritization.
|
||||
*/
|
||||
NetworkTimeSuggestion peekLatestValidNetworkSuggestion() {
|
||||
return mTimeDetectorStrategy.findLatestValidNetworkSuggestionForTests();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a ManualTimeSuggestion using the current elapsed realtime clock for the
|
||||
* reference time.
|
||||
@@ -739,6 +900,24 @@ public class TimeDetectorStrategyImplTest {
|
||||
}
|
||||
return createPhoneTimeSuggestion(phoneId, time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a NetworkTimeSuggestion using the current elapsed realtime clock for the
|
||||
* reference time.
|
||||
*/
|
||||
NetworkTimeSuggestion generateNetworkTimeSuggestion(long timeMillis) {
|
||||
TimestampedValue<Long> utcTime =
|
||||
new TimestampedValue<>(mFakeCallback.peekElapsedRealtimeMillis(), timeMillis);
|
||||
return new NetworkTimeSuggestion(utcTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates what the supplied time would be when adjusted for the movement of the fake
|
||||
* elapsed realtime clock.
|
||||
*/
|
||||
long calculateTimeInMillisForNow(TimestampedValue<Long> utcTime) {
|
||||
return TimeDetectorStrategy.getTimeAt(utcTime, peekElapsedRealtimeMillis());
|
||||
}
|
||||
}
|
||||
|
||||
private static PhoneTimeSuggestion createPhoneTimeSuggestion(int phoneId,
|
||||
|
||||
Reference in New Issue
Block a user