Merge "Change NetworkTimeUpdateServiceImpl behavior"
This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
package android.app.timedetector;
|
package android.app.timedetector;
|
||||||
|
|
||||||
import android.app.timedetector.ManualTimeSuggestion;
|
import android.app.timedetector.ManualTimeSuggestion;
|
||||||
|
import android.app.timedetector.NetworkTimeSuggestion;
|
||||||
import android.app.timedetector.PhoneTimeSuggestion;
|
import android.app.timedetector.PhoneTimeSuggestion;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,4 +36,5 @@ import android.app.timedetector.PhoneTimeSuggestion;
|
|||||||
interface ITimeDetectorService {
|
interface ITimeDetectorService {
|
||||||
void suggestPhoneTime(in PhoneTimeSuggestion timeSuggestion);
|
void suggestPhoneTime(in PhoneTimeSuggestion timeSuggestion);
|
||||||
void suggestManualTime(in ManualTimeSuggestion 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);
|
manualTimeSuggestion.addDebugInfo(why);
|
||||||
return manualTimeSuggestion;
|
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() {
|
public long getCachedNtpTimeReference() {
|
||||||
return mCachedNtpElapsedRealtime;
|
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
|
<!-- Number of times to try again with the shorter interval, before backing
|
||||||
off until the normal polling interval. A value < 0 indicates infinite. -->
|
off until the normal polling interval. A value < 0 indicates infinite. -->
|
||||||
<integer name="config_ntpRetry">3</integer>
|
<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. -->
|
<!-- Timeout to wait for NTP server response in milliseconds. -->
|
||||||
<integer name="config_ntpTimeout">5000</integer>
|
<integer name="config_ntpTimeout">5000</integer>
|
||||||
|
|
||||||
|
|||||||
@@ -489,7 +489,6 @@
|
|||||||
<java-symbol type="integer" name="config_ntpPollingInterval" />
|
<java-symbol type="integer" name="config_ntpPollingInterval" />
|
||||||
<java-symbol type="integer" name="config_ntpPollingIntervalShorter" />
|
<java-symbol type="integer" name="config_ntpPollingIntervalShorter" />
|
||||||
<java-symbol type="integer" name="config_ntpRetry" />
|
<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_ntpTimeout" />
|
||||||
<java-symbol type="integer" name="config_shortPressOnPowerBehavior" />
|
<java-symbol type="integer" name="config_shortPressOnPowerBehavior" />
|
||||||
<java-symbol type="integer" name="config_toastDefaultGravity" />
|
<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.AlarmManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
|
import android.app.timedetector.NetworkTimeSuggestion;
|
||||||
|
import android.app.timedetector.TimeDetector;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -35,10 +37,10 @@ import android.os.Message;
|
|||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.telephony.TelephonyManager;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.NtpTrustedTime;
|
import android.util.NtpTrustedTime;
|
||||||
import android.util.TimeUtils;
|
import android.util.TimeUtils;
|
||||||
|
import android.util.TimestampedValue;
|
||||||
|
|
||||||
import com.android.internal.util.DumpUtils;
|
import com.android.internal.util.DumpUtils;
|
||||||
|
|
||||||
@@ -46,21 +48,19 @@ import java.io.FileDescriptor;
|
|||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Monitors the network time and updates the system time if it is out of sync
|
* Monitors the network time. If looking up the network time fails for some reason, it tries a few
|
||||||
* and there hasn't been any NITZ update from the carrier recently.
|
* times with a short interval and then resets to checking on longer intervals.
|
||||||
* 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
|
||||||
* <p>
|
* com.android.server.timedetector.TimeDetectorService} where it may be used to set the device
|
||||||
* If the user enables AUTO_TIME, it will check immediately for the network time, if NITZ wasn't
|
* system clock, depending on user settings and what other signals are available.
|
||||||
* available.
|
|
||||||
* </p>
|
|
||||||
*/
|
*/
|
||||||
public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeUpdateService {
|
public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeUpdateService {
|
||||||
|
|
||||||
private static final String TAG = "NetworkTimeUpdateService";
|
private static final String TAG = "NetworkTimeUpdateService";
|
||||||
private static final boolean DBG = false;
|
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_POLL_NETWORK_TIME = 2;
|
||||||
private static final int EVENT_NETWORK_CHANGED = 3;
|
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 int POLL_REQUEST = 0;
|
||||||
|
|
||||||
private static final long NOT_SET = -1;
|
|
||||||
private long mNitzTimeSetTime = NOT_SET;
|
|
||||||
private Network mDefaultNetwork = null;
|
private Network mDefaultNetwork = null;
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final NtpTrustedTime mTime;
|
private final NtpTrustedTime mTime;
|
||||||
private final AlarmManager mAlarmManager;
|
private final AlarmManager mAlarmManager;
|
||||||
|
private final TimeDetector mTimeDetector;
|
||||||
private final ConnectivityManager mCM;
|
private final ConnectivityManager mCM;
|
||||||
private final PendingIntent mPendingPollIntent;
|
private final PendingIntent mPendingPollIntent;
|
||||||
private final PowerManager.WakeLock mWakeLock;
|
private final PowerManager.WakeLock mWakeLock;
|
||||||
|
|
||||||
// NTP lookup is done on this thread and handler
|
// NTP lookup is done on this thread and handler
|
||||||
private Handler mHandler;
|
private Handler mHandler;
|
||||||
private SettingsObserver mSettingsObserver;
|
private AutoTimeSettingObserver mAutoTimeSettingObserver;
|
||||||
private NetworkTimeUpdateCallback mNetworkTimeUpdateCallback;
|
private NetworkTimeUpdateCallback mNetworkTimeUpdateCallback;
|
||||||
|
|
||||||
// Normal polling frequency
|
// Normal polling frequency
|
||||||
@@ -91,8 +90,6 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU
|
|||||||
private final long mPollingIntervalShorterMs;
|
private final long mPollingIntervalShorterMs;
|
||||||
// Number of times to try again
|
// Number of times to try again
|
||||||
private final int mTryAgainTimesMax;
|
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.
|
// 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
|
// During bootup, the network may not have been up yet, or it's taking time for the
|
||||||
// connection to happen.
|
// connection to happen.
|
||||||
@@ -102,6 +99,7 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU
|
|||||||
mContext = context;
|
mContext = context;
|
||||||
mTime = NtpTrustedTime.getInstance(context);
|
mTime = NtpTrustedTime.getInstance(context);
|
||||||
mAlarmManager = mContext.getSystemService(AlarmManager.class);
|
mAlarmManager = mContext.getSystemService(AlarmManager.class);
|
||||||
|
mTimeDetector = mContext.getSystemService(TimeDetector.class);
|
||||||
mCM = mContext.getSystemService(ConnectivityManager.class);
|
mCM = mContext.getSystemService(ConnectivityManager.class);
|
||||||
|
|
||||||
Intent pollIntent = new Intent(ACTION_POLL, null);
|
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);
|
com.android.internal.R.integer.config_ntpPollingIntervalShorter);
|
||||||
mTryAgainTimesMax = mContext.getResources().getInteger(
|
mTryAgainTimesMax = mContext.getResources().getInteger(
|
||||||
com.android.internal.R.integer.config_ntpRetry);
|
com.android.internal.R.integer.config_ntpRetry);
|
||||||
mTimeErrorThresholdMs = mContext.getResources().getInteger(
|
|
||||||
com.android.internal.R.integer.config_ntpThreshold);
|
|
||||||
|
|
||||||
mWakeLock = context.getSystemService(PowerManager.class).newWakeLock(
|
mWakeLock = context.getSystemService(PowerManager.class).newWakeLock(
|
||||||
PowerManager.PARTIAL_WAKE_LOCK, TAG);
|
PowerManager.PARTIAL_WAKE_LOCK, TAG);
|
||||||
@@ -122,7 +118,6 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void systemRunning() {
|
public void systemRunning() {
|
||||||
registerForTelephonyIntents();
|
|
||||||
registerForAlarms();
|
registerForAlarms();
|
||||||
|
|
||||||
HandlerThread thread = new HandlerThread(TAG);
|
HandlerThread thread = new HandlerThread(TAG);
|
||||||
@@ -131,14 +126,9 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU
|
|||||||
mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
|
mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
|
||||||
mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
|
mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
|
||||||
|
|
||||||
mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
|
mAutoTimeSettingObserver = new AutoTimeSettingObserver(mContext, mHandler,
|
||||||
mSettingsObserver.observe(mContext);
|
EVENT_AUTO_TIME_ENABLED);
|
||||||
}
|
mAutoTimeSettingObserver.observe();
|
||||||
|
|
||||||
private void registerForTelephonyIntents() {
|
|
||||||
IntentFilter intentFilter = new IntentFilter();
|
|
||||||
intentFilter.addAction(TelephonyManager.ACTION_NETWORK_SET_TIME);
|
|
||||||
mContext.registerReceiver(mNitzReceiver, intentFilter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerForAlarms() {
|
private void registerForAlarms() {
|
||||||
@@ -152,8 +142,7 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onPollNetworkTime(int event) {
|
private void onPollNetworkTime(int event) {
|
||||||
// If Automatic time is not set, don't bother. Similarly, if we don't
|
// If we don't have any default network, don't bother.
|
||||||
// have any default network, don't bother.
|
|
||||||
if (mDefaultNetwork == null) return;
|
if (mDefaultNetwork == null) return;
|
||||||
mWakeLock.acquire();
|
mWakeLock.acquire();
|
||||||
try {
|
try {
|
||||||
@@ -173,10 +162,12 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU
|
|||||||
if (mTime.getCacheAge() < mPollingIntervalMs) {
|
if (mTime.getCacheAge() < mPollingIntervalMs) {
|
||||||
// Obtained fresh fix; schedule next normal update
|
// Obtained fresh fix; schedule next normal update
|
||||||
resetAlarm(mPollingIntervalMs);
|
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 {
|
} else {
|
||||||
// No fresh fix; schedule retry
|
// No fresh fix; schedule retry
|
||||||
mTryAgainCounter++;
|
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.
|
* 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);
|
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 */
|
/** Handler to do the network accesses on */
|
||||||
private class MyHandler extends Handler {
|
private class MyHandler extends Handler {
|
||||||
|
|
||||||
@@ -263,7 +203,7 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU
|
|||||||
@Override
|
@Override
|
||||||
public void handleMessage(Message msg) {
|
public void handleMessage(Message msg) {
|
||||||
switch (msg.what) {
|
switch (msg.what) {
|
||||||
case EVENT_AUTO_TIME_CHANGED:
|
case EVENT_AUTO_TIME_ENABLED:
|
||||||
case EVENT_POLL_NETWORK_TIME:
|
case EVENT_POLL_NETWORK_TIME:
|
||||||
case EVENT_NETWORK_CHANGED:
|
case EVENT_NETWORK_CHANGED:
|
||||||
onPollNetworkTime(msg.what);
|
onPollNetworkTime(msg.what);
|
||||||
@@ -287,30 +227,45 @@ 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 final Context mContext;
|
||||||
private Handler mHandler;
|
private final int mMsg;
|
||||||
|
private final Handler mHandler;
|
||||||
|
|
||||||
SettingsObserver(Handler handler, int msg) {
|
AutoTimeSettingObserver(Context context, Handler handler, int msg) {
|
||||||
super(handler);
|
super(handler);
|
||||||
|
mContext = context;
|
||||||
mHandler = handler;
|
mHandler = handler;
|
||||||
mMsg = msg;
|
mMsg = msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
void observe(Context context) {
|
void observe() {
|
||||||
ContentResolver resolver = context.getContentResolver();
|
ContentResolver resolver = mContext.getContentResolver();
|
||||||
resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
|
resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
|
||||||
false, this);
|
false, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onChange(boolean selfChange) {
|
public void onChange(boolean selfChange) {
|
||||||
|
if (isAutomaticTimeEnabled()) {
|
||||||
mHandler.obtainMessage(mMsg).sendToTarget();
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||||
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
|
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
|
||||||
@@ -319,8 +274,6 @@ public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeU
|
|||||||
pw.print("\nPollingIntervalShorterMs: ");
|
pw.print("\nPollingIntervalShorterMs: ");
|
||||||
TimeUtils.formatDuration(mPollingIntervalShorterMs, pw);
|
TimeUtils.formatDuration(mPollingIntervalShorterMs, pw);
|
||||||
pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax);
|
pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax);
|
||||||
pw.print("TimeErrorThresholdMs: ");
|
|
||||||
TimeUtils.formatDuration(mTimeErrorThresholdMs, pw);
|
|
||||||
pw.println("\nTryAgainCounter: " + mTryAgainCounter);
|
pw.println("\nTryAgainCounter: " + mTryAgainCounter);
|
||||||
pw.println("NTP cache age: " + mTime.getCacheAge());
|
pw.println("NTP cache age: " + mTime.getCacheAge());
|
||||||
pw.println("NTP cache certainty: " + mTime.getCacheCertainty());
|
pw.println("NTP cache certainty: " + mTime.getCacheCertainty());
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
|
|||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.app.timedetector.ITimeDetectorService;
|
import android.app.timedetector.ITimeDetectorService;
|
||||||
import android.app.timedetector.ManualTimeSuggestion;
|
import android.app.timedetector.ManualTimeSuggestion;
|
||||||
|
import android.app.timedetector.NetworkTimeSuggestion;
|
||||||
import android.app.timedetector.PhoneTimeSuggestion;
|
import android.app.timedetector.PhoneTimeSuggestion;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -105,6 +106,14 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub {
|
|||||||
mHandler.post(() -> mTimeDetectorStrategy.suggestManualTime(timeSignal));
|
mHandler.post(() -> mTimeDetectorStrategy.suggestManualTime(timeSignal));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSignal) {
|
||||||
|
enforceSuggestNetworkTimePermission();
|
||||||
|
Objects.requireNonNull(timeSignal);
|
||||||
|
|
||||||
|
mHandler.post(() -> mTimeDetectorStrategy.suggestNetworkTime(timeSignal));
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public void handleAutoTimeDetectionToggle() {
|
public void handleAutoTimeDetectionToggle() {
|
||||||
mHandler.post(mTimeDetectorStrategy::handleAutoTimeDetectionChanged);
|
mHandler.post(mTimeDetectorStrategy::handleAutoTimeDetectionChanged);
|
||||||
@@ -129,4 +138,10 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub {
|
|||||||
android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE,
|
android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE,
|
||||||
"suggest manual time and time 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.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.app.timedetector.ManualTimeSuggestion;
|
import android.app.timedetector.ManualTimeSuggestion;
|
||||||
|
import android.app.timedetector.NetworkTimeSuggestion;
|
||||||
import android.app.timedetector.PhoneTimeSuggestion;
|
import android.app.timedetector.PhoneTimeSuggestion;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.util.TimestampedValue;
|
import android.util.TimestampedValue;
|
||||||
@@ -86,6 +87,9 @@ public interface TimeDetectorStrategy {
|
|||||||
/** Process the suggested manually entered time. */
|
/** Process the suggested manually entered time. */
|
||||||
void suggestManualTime(@NonNull ManualTimeSuggestion timeSuggestion);
|
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. */
|
/** Handle the auto-time setting being toggled on or off. */
|
||||||
void handleAutoTimeDetectionChanged();
|
void handleAutoTimeDetectionChanged();
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
|
|||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.app.AlarmManager;
|
import android.app.AlarmManager;
|
||||||
import android.app.timedetector.ManualTimeSuggestion;
|
import android.app.timedetector.ManualTimeSuggestion;
|
||||||
|
import android.app.timedetector.NetworkTimeSuggestion;
|
||||||
import android.app.timedetector.PhoneTimeSuggestion;
|
import android.app.timedetector.PhoneTimeSuggestion;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.telephony.TelephonyManager;
|
import android.telephony.TelephonyManager;
|
||||||
@@ -32,6 +33,7 @@ import com.android.internal.annotations.GuardedBy;
|
|||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.util.IndentingPrintWriter;
|
import com.android.internal.util.IndentingPrintWriter;
|
||||||
import com.android.server.timezonedetector.ArrayMapWithHistory;
|
import com.android.server.timezonedetector.ArrayMapWithHistory;
|
||||||
|
import com.android.server.timezonedetector.ReferenceWithHistory;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.lang.annotation.Retention;
|
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. */
|
/** Each bucket is this size. All buckets are equally sized. */
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final int PHONE_BUCKET_SIZE_MILLIS = 60 * 60 * 1000;
|
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
|
@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)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
public @interface Origin {}
|
public @interface Origin {}
|
||||||
|
|
||||||
@@ -72,6 +74,10 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
|
|||||||
@Origin
|
@Origin
|
||||||
private static final int ORIGIN_MANUAL = 2;
|
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
|
* 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
|
* 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.
|
* will have a small number of telephony devices and phoneIds are assumed to be stable.
|
||||||
*/
|
*/
|
||||||
@GuardedBy("this")
|
@GuardedBy("this")
|
||||||
private ArrayMapWithHistory<Integer, PhoneTimeSuggestion> mSuggestionByPhoneId =
|
private final ArrayMapWithHistory<Integer, PhoneTimeSuggestion> mSuggestionByPhoneId =
|
||||||
new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
|
new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
|
||||||
|
|
||||||
|
@GuardedBy("this")
|
||||||
|
private final ReferenceWithHistory<NetworkTimeSuggestion> mLastNetworkSuggestion =
|
||||||
|
new ReferenceWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(@NonNull Callback callback) {
|
public void initialize(@NonNull Callback callback) {
|
||||||
mCallback = callback;
|
mCallback = callback;
|
||||||
@@ -121,6 +131,19 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
|
|||||||
setSystemClockIfRequired(ORIGIN_MANUAL, newUtcTime, cause);
|
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
|
@Override
|
||||||
public synchronized void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) {
|
public synchronized void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) {
|
||||||
// Empty time suggestion means that telephony network connectivity has been lost.
|
// Empty time suggestion means that telephony network connectivity has been lost.
|
||||||
@@ -184,6 +207,11 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
|
|||||||
mSuggestionByPhoneId.dump(ipw);
|
mSuggestionByPhoneId.dump(ipw);
|
||||||
ipw.decreaseIndent(); // level 2
|
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.decreaseIndent(); // level 1
|
||||||
ipw.flush();
|
ipw.flush();
|
||||||
}
|
}
|
||||||
@@ -253,23 +281,34 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
|
|||||||
return;
|
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();
|
PhoneTimeSuggestion bestPhoneSuggestion = findBestPhoneSuggestion();
|
||||||
|
if (bestPhoneSuggestion != null) {
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final TimestampedValue<Long> newUtcTime = bestPhoneSuggestion.getUtcTime();
|
final TimestampedValue<Long> newUtcTime = bestPhoneSuggestion.getUtcTime();
|
||||||
String cause = "Found good suggestion."
|
String cause = "Found good phone suggestion."
|
||||||
+ ", bestPhoneSuggestion=" + bestPhoneSuggestion
|
+ ", bestPhoneSuggestion=" + bestPhoneSuggestion
|
||||||
+ ", detectionReason=" + detectionReason;
|
+ ", detectionReason=" + detectionReason;
|
||||||
setSystemClockIfRequired(ORIGIN_PHONE, newUtcTime, cause);
|
setSystemClockIfRequired(ORIGIN_PHONE, newUtcTime, cause);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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")
|
@GuardedBy("this")
|
||||||
@@ -348,37 +387,50 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
|
|||||||
|
|
||||||
private static int scorePhoneSuggestion(
|
private static int scorePhoneSuggestion(
|
||||||
long elapsedRealtimeMillis, @NonNull PhoneTimeSuggestion timeSuggestion) {
|
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();
|
TimestampedValue<Long> utcTime = timeSuggestion.getUtcTime();
|
||||||
long referenceTimeMillis = utcTime.getReferenceTimeMillis();
|
if (!validateSuggestionUtcTime(elapsedRealtimeMillis, utcTime)) {
|
||||||
if (referenceTimeMillis > elapsedRealtimeMillis) {
|
Slog.w(LOG_TAG, "Existing suggestion found to be invalid "
|
||||||
// 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. "
|
|
||||||
+ " elapsedRealtimeMillis=" + elapsedRealtimeMillis
|
+ " elapsedRealtimeMillis=" + elapsedRealtimeMillis
|
||||||
+ ", timeSuggestion=" + timeSuggestion);
|
+ ", timeSuggestion=" + timeSuggestion);
|
||||||
return PHONE_INVALID_SCORE;
|
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
|
// Turn the age into a discrete value: 0 <= bucketIndex < PHONE_BUCKET_COUNT.
|
||||||
// predictable, the accuracy of the reference time clock may be poor over long periods which
|
int bucketIndex = (int) (ageMillis / PHONE_BUCKET_SIZE_MILLIS);
|
||||||
// would lead to errors creeping in. Also, in edge cases where a bad suggestion has been
|
if (bucketIndex >= PHONE_BUCKET_COUNT) {
|
||||||
// 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) {
|
|
||||||
return PHONE_INVALID_SCORE;
|
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.
|
// We want the lowest bucket index to have the highest score. 0 > score >= BUCKET_COUNT.
|
||||||
return PHONE_BUCKET_COUNT - bucketIndex;
|
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")
|
@GuardedBy("this")
|
||||||
private void setSystemClockIfRequired(
|
private void setSystemClockIfRequired(
|
||||||
@Origin int origin, @NonNull TimestampedValue<Long> time, @NonNull String cause) {
|
@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) {
|
private static boolean isOriginAutomatic(@Origin int origin) {
|
||||||
return origin == ORIGIN_PHONE;
|
return origin != ORIGIN_MANUAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GuardedBy("this")
|
@GuardedBy("this")
|
||||||
@@ -506,6 +558,16 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
|
|||||||
return findBestPhoneSuggestion();
|
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.
|
* 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) {
|
public synchronized PhoneTimeSuggestion getLatestPhoneSuggestion(int phoneId) {
|
||||||
return mSuggestionByPhoneId.get(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 static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.app.timedetector.ManualTimeSuggestion;
|
import android.app.timedetector.ManualTimeSuggestion;
|
||||||
|
import android.app.timedetector.NetworkTimeSuggestion;
|
||||||
import android.app.timedetector.PhoneTimeSuggestion;
|
import android.app.timedetector.PhoneTimeSuggestion;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
@@ -143,6 +144,36 @@ public class TimeDetectorServiceTest {
|
|||||||
mStubbedTimeDetectorStrategy.verifySuggestManualTimeCalled(manualTimeSuggestion);
|
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
|
@Test
|
||||||
public void testDump() {
|
public void testDump() {
|
||||||
when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP))
|
when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP))
|
||||||
@@ -180,11 +211,17 @@ public class TimeDetectorServiceTest {
|
|||||||
return new ManualTimeSuggestion(timeValue);
|
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 {
|
private static class StubbedTimeDetectorStrategy implements TimeDetectorStrategy {
|
||||||
|
|
||||||
// Call tracking.
|
// Call tracking.
|
||||||
private PhoneTimeSuggestion mLastPhoneSuggestion;
|
private PhoneTimeSuggestion mLastPhoneSuggestion;
|
||||||
private ManualTimeSuggestion mLastManualSuggestion;
|
private ManualTimeSuggestion mLastManualSuggestion;
|
||||||
|
private NetworkTimeSuggestion mLastNetworkSuggestion;
|
||||||
private boolean mLastAutoTimeDetectionToggleCalled;
|
private boolean mLastAutoTimeDetectionToggleCalled;
|
||||||
private boolean mDumpCalled;
|
private boolean mDumpCalled;
|
||||||
|
|
||||||
@@ -204,6 +241,12 @@ public class TimeDetectorServiceTest {
|
|||||||
mLastManualSuggestion = timeSuggestion;
|
mLastManualSuggestion = timeSuggestion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void suggestNetworkTime(NetworkTimeSuggestion timeSuggestion) {
|
||||||
|
resetCallTracking();
|
||||||
|
mLastNetworkSuggestion = timeSuggestion;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleAutoTimeDetectionChanged() {
|
public void handleAutoTimeDetectionChanged() {
|
||||||
resetCallTracking();
|
resetCallTracking();
|
||||||
@@ -219,6 +262,7 @@ public class TimeDetectorServiceTest {
|
|||||||
void resetCallTracking() {
|
void resetCallTracking() {
|
||||||
mLastPhoneSuggestion = null;
|
mLastPhoneSuggestion = null;
|
||||||
mLastManualSuggestion = null;
|
mLastManualSuggestion = null;
|
||||||
|
mLastNetworkSuggestion = null;
|
||||||
mLastAutoTimeDetectionToggleCalled = false;
|
mLastAutoTimeDetectionToggleCalled = false;
|
||||||
mDumpCalled = false;
|
mDumpCalled = false;
|
||||||
}
|
}
|
||||||
@@ -231,6 +275,10 @@ public class TimeDetectorServiceTest {
|
|||||||
assertEquals(expectedSuggestion, mLastManualSuggestion);
|
assertEquals(expectedSuggestion, mLastManualSuggestion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void verifySuggestNetworkTimeCalled(NetworkTimeSuggestion expectedSuggestion) {
|
||||||
|
assertEquals(expectedSuggestion, mLastNetworkSuggestion);
|
||||||
|
}
|
||||||
|
|
||||||
void verifyHandleAutoTimeDetectionToggleCalled() {
|
void verifyHandleAutoTimeDetectionToggleCalled() {
|
||||||
assertTrue(mLastAutoTimeDetectionToggleCalled);
|
assertTrue(mLastAutoTimeDetectionToggleCalled);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import static org.junit.Assert.assertTrue;
|
|||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import android.app.timedetector.ManualTimeSuggestion;
|
import android.app.timedetector.ManualTimeSuggestion;
|
||||||
|
import android.app.timedetector.NetworkTimeSuggestion;
|
||||||
import android.app.timedetector.PhoneTimeSuggestion;
|
import android.app.timedetector.PhoneTimeSuggestion;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.icu.util.Calendar;
|
import android.icu.util.Calendar;
|
||||||
@@ -45,14 +46,16 @@ public class TimeDetectorStrategyImplTest {
|
|||||||
private static final TimestampedValue<Long> ARBITRARY_CLOCK_INITIALIZATION_INFO =
|
private static final TimestampedValue<Long> ARBITRARY_CLOCK_INITIALIZATION_INFO =
|
||||||
new TimestampedValue<>(
|
new TimestampedValue<>(
|
||||||
123456789L /* realtimeClockMillis */,
|
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 long ARBITRARY_TEST_TIME_MILLIS = createUtcTime(2018, 1, 1, 12, 0, 0);
|
||||||
|
|
||||||
private static final int ARBITRARY_PHONE_ID = 123456;
|
private static final int ARBITRARY_PHONE_ID = 123456;
|
||||||
|
|
||||||
private static final long ONE_DAY_MILLIS = Duration.ofDays(1).toMillis();
|
|
||||||
|
|
||||||
private Script mScript;
|
private Script mScript;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@@ -67,14 +70,15 @@ public class TimeDetectorStrategyImplTest {
|
|||||||
|
|
||||||
int phoneId = ARBITRARY_PHONE_ID;
|
int phoneId = ARBITRARY_PHONE_ID;
|
||||||
long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
|
long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
|
||||||
|
|
||||||
PhoneTimeSuggestion timeSuggestion =
|
PhoneTimeSuggestion timeSuggestion =
|
||||||
mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
|
mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
|
||||||
int clockIncrement = 1000;
|
mScript.simulateTimePassing()
|
||||||
long expectedSystemClockMillis = testTimeMillis + clockIncrement;
|
.simulatePhoneTimeSuggestion(timeSuggestion);
|
||||||
|
|
||||||
mScript.simulateTimePassing(clockIncrement)
|
long expectedSystemClockMillis =
|
||||||
.simulatePhoneTimeSuggestion(timeSuggestion)
|
mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime());
|
||||||
.verifySystemClockWasSetAndResetCallTracking(
|
mScript.verifySystemClockWasSetAndResetCallTracking(
|
||||||
expectedSystemClockMillis, true /* expectNetworkBroadcast */)
|
expectedSystemClockMillis, true /* expectNetworkBroadcast */)
|
||||||
.assertLatestPhoneSuggestion(phoneId, timeSuggestion);
|
.assertLatestPhoneSuggestion(phoneId, timeSuggestion);
|
||||||
}
|
}
|
||||||
@@ -94,26 +98,24 @@ public class TimeDetectorStrategyImplTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuggestPhoneTime_systemClockThreshold() {
|
public void testSuggestPhoneTime_systemClockThreshold() {
|
||||||
int systemClockUpdateThresholdMillis = 1000;
|
final int systemClockUpdateThresholdMillis = 1000;
|
||||||
|
final int clockIncrementMillis = 100;
|
||||||
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
|
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
|
||||||
.pokeThresholds(systemClockUpdateThresholdMillis)
|
.pokeThresholds(systemClockUpdateThresholdMillis)
|
||||||
.pokeAutoTimeDetectionEnabled(true);
|
.pokeAutoTimeDetectionEnabled(true);
|
||||||
|
|
||||||
final int clockIncrement = 100;
|
|
||||||
int phoneId = ARBITRARY_PHONE_ID;
|
int phoneId = ARBITRARY_PHONE_ID;
|
||||||
|
|
||||||
// Send the first time signal. It should be used.
|
// Send the first time signal. It should be used.
|
||||||
{
|
{
|
||||||
long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
|
|
||||||
PhoneTimeSuggestion timeSuggestion1 =
|
PhoneTimeSuggestion timeSuggestion1 =
|
||||||
mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
|
mScript.generatePhoneTimeSuggestion(phoneId, ARBITRARY_TEST_TIME_MILLIS);
|
||||||
TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
|
|
||||||
|
|
||||||
// Increment the the device clocks to simulate the passage of time.
|
// Increment the the device clocks to simulate the passage of time.
|
||||||
mScript.simulateTimePassing(clockIncrement);
|
mScript.simulateTimePassing(clockIncrementMillis);
|
||||||
|
|
||||||
long expectedSystemClockMillis1 =
|
long expectedSystemClockMillis1 =
|
||||||
TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
|
mScript.calculateTimeInMillisForNow(timeSuggestion1.getUtcTime());
|
||||||
|
|
||||||
mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
|
mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
|
||||||
.verifySystemClockWasSetAndResetCallTracking(
|
.verifySystemClockWasSetAndResetCallTracking(
|
||||||
@@ -127,7 +129,7 @@ public class TimeDetectorStrategyImplTest {
|
|||||||
int underThresholdMillis = systemClockUpdateThresholdMillis - 1;
|
int underThresholdMillis = systemClockUpdateThresholdMillis - 1;
|
||||||
PhoneTimeSuggestion timeSuggestion2 = mScript.generatePhoneTimeSuggestion(
|
PhoneTimeSuggestion timeSuggestion2 = mScript.generatePhoneTimeSuggestion(
|
||||||
phoneId, mScript.peekSystemClockMillis() + underThresholdMillis);
|
phoneId, mScript.peekSystemClockMillis() + underThresholdMillis);
|
||||||
mScript.simulateTimePassing(clockIncrement)
|
mScript.simulateTimePassing(clockIncrementMillis)
|
||||||
.simulatePhoneTimeSuggestion(timeSuggestion2)
|
.simulatePhoneTimeSuggestion(timeSuggestion2)
|
||||||
.verifySystemClockWasNotSetAndResetCallTracking()
|
.verifySystemClockWasNotSetAndResetCallTracking()
|
||||||
.assertLatestPhoneSuggestion(phoneId, timeSuggestion2);
|
.assertLatestPhoneSuggestion(phoneId, timeSuggestion2);
|
||||||
@@ -138,11 +140,10 @@ public class TimeDetectorStrategyImplTest {
|
|||||||
PhoneTimeSuggestion timeSuggestion3 = mScript.generatePhoneTimeSuggestion(
|
PhoneTimeSuggestion timeSuggestion3 = mScript.generatePhoneTimeSuggestion(
|
||||||
phoneId,
|
phoneId,
|
||||||
mScript.peekSystemClockMillis() + systemClockUpdateThresholdMillis);
|
mScript.peekSystemClockMillis() + systemClockUpdateThresholdMillis);
|
||||||
mScript.simulateTimePassing(clockIncrement);
|
mScript.simulateTimePassing(clockIncrementMillis);
|
||||||
|
|
||||||
long expectedSystemClockMillis3 =
|
long expectedSystemClockMillis3 =
|
||||||
TimeDetectorStrategy.getTimeAt(timeSuggestion3.getUtcTime(),
|
mScript.calculateTimeInMillisForNow(timeSuggestion3.getUtcTime());
|
||||||
mScript.peekElapsedRealtimeMillis());
|
|
||||||
|
|
||||||
mScript.simulatePhoneTimeSuggestion(timeSuggestion3)
|
mScript.simulatePhoneTimeSuggestion(timeSuggestion3)
|
||||||
.verifySystemClockWasSetAndResetCallTracking(
|
.verifySystemClockWasSetAndResetCallTracking(
|
||||||
@@ -162,17 +163,16 @@ public class TimeDetectorStrategyImplTest {
|
|||||||
int phone1Id = ARBITRARY_PHONE_ID;
|
int phone1Id = ARBITRARY_PHONE_ID;
|
||||||
int phone2Id = ARBITRARY_PHONE_ID + 1;
|
int phone2Id = ARBITRARY_PHONE_ID + 1;
|
||||||
long phone1TimeMillis = ARBITRARY_TEST_TIME_MILLIS;
|
long phone1TimeMillis = ARBITRARY_TEST_TIME_MILLIS;
|
||||||
long phone2TimeMillis = phone1TimeMillis + 60000;
|
long phone2TimeMillis = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(1).toMillis();
|
||||||
|
|
||||||
final int clockIncrement = 999;
|
|
||||||
|
|
||||||
// Make a suggestion with phone2Id.
|
// Make a suggestion with phone2Id.
|
||||||
{
|
{
|
||||||
PhoneTimeSuggestion phone2TimeSuggestion =
|
PhoneTimeSuggestion phone2TimeSuggestion =
|
||||||
mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis);
|
mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis);
|
||||||
mScript.simulateTimePassing(clockIncrement);
|
mScript.simulateTimePassing();
|
||||||
|
|
||||||
long expectedSystemClockMillis = phone2TimeMillis + clockIncrement;
|
long expectedSystemClockMillis =
|
||||||
|
mScript.calculateTimeInMillisForNow(phone2TimeSuggestion.getUtcTime());
|
||||||
|
|
||||||
mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
|
mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
|
||||||
.verifySystemClockWasSetAndResetCallTracking(
|
.verifySystemClockWasSetAndResetCallTracking(
|
||||||
@@ -181,15 +181,16 @@ public class TimeDetectorStrategyImplTest {
|
|||||||
.assertLatestPhoneSuggestion(phone2Id, phone2TimeSuggestion);
|
.assertLatestPhoneSuggestion(phone2Id, phone2TimeSuggestion);
|
||||||
}
|
}
|
||||||
|
|
||||||
mScript.simulateTimePassing(clockIncrement);
|
mScript.simulateTimePassing();
|
||||||
|
|
||||||
// Now make a different suggestion with phone1Id.
|
// Now make a different suggestion with phone1Id.
|
||||||
{
|
{
|
||||||
PhoneTimeSuggestion phone1TimeSuggestion =
|
PhoneTimeSuggestion phone1TimeSuggestion =
|
||||||
mScript.generatePhoneTimeSuggestion(phone1Id, phone1TimeMillis);
|
mScript.generatePhoneTimeSuggestion(phone1Id, phone1TimeMillis);
|
||||||
mScript.simulateTimePassing(clockIncrement);
|
mScript.simulateTimePassing();
|
||||||
|
|
||||||
long expectedSystemClockMillis = phone1TimeMillis + clockIncrement;
|
long expectedSystemClockMillis =
|
||||||
|
mScript.calculateTimeInMillisForNow(phone1TimeSuggestion.getUtcTime());
|
||||||
|
|
||||||
mScript.simulatePhoneTimeSuggestion(phone1TimeSuggestion)
|
mScript.simulatePhoneTimeSuggestion(phone1TimeSuggestion)
|
||||||
.verifySystemClockWasSetAndResetCallTracking(
|
.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
|
// Make another suggestion with phone2Id. It should be stored but not used because the
|
||||||
// phone1Id suggestion will still "win".
|
// phone1Id suggestion will still "win".
|
||||||
{
|
{
|
||||||
PhoneTimeSuggestion phone2TimeSuggestion =
|
PhoneTimeSuggestion phone2TimeSuggestion =
|
||||||
mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis);
|
mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis);
|
||||||
mScript.simulateTimePassing(clockIncrement);
|
mScript.simulateTimePassing();
|
||||||
|
|
||||||
mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
|
mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
|
||||||
.verifySystemClockWasNotSetAndResetCallTracking()
|
.verifySystemClockWasNotSetAndResetCallTracking()
|
||||||
@@ -220,9 +221,10 @@ public class TimeDetectorStrategyImplTest {
|
|||||||
{
|
{
|
||||||
PhoneTimeSuggestion phone2TimeSuggestion =
|
PhoneTimeSuggestion phone2TimeSuggestion =
|
||||||
mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis);
|
mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis);
|
||||||
mScript.simulateTimePassing(clockIncrement);
|
mScript.simulateTimePassing();
|
||||||
|
|
||||||
long expectedSystemClockMillis = phone2TimeMillis + clockIncrement;
|
long expectedSystemClockMillis =
|
||||||
|
mScript.calculateTimeInMillisForNow(phone2TimeSuggestion.getUtcTime());
|
||||||
|
|
||||||
mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
|
mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
|
||||||
.verifySystemClockWasSetAndResetCallTracking(
|
.verifySystemClockWasSetAndResetCallTracking(
|
||||||
@@ -239,7 +241,7 @@ public class TimeDetectorStrategyImplTest {
|
|||||||
int phoneId = ARBITRARY_PHONE_ID;
|
int phoneId = ARBITRARY_PHONE_ID;
|
||||||
PhoneTimeSuggestion timeSuggestion =
|
PhoneTimeSuggestion timeSuggestion =
|
||||||
mScript.generatePhoneTimeSuggestion(phoneId, ARBITRARY_TEST_TIME_MILLIS);
|
mScript.generatePhoneTimeSuggestion(phoneId, ARBITRARY_TEST_TIME_MILLIS);
|
||||||
mScript.simulateTimePassing(1000)
|
mScript.simulateTimePassing()
|
||||||
.simulatePhoneTimeSuggestion(timeSuggestion)
|
.simulatePhoneTimeSuggestion(timeSuggestion)
|
||||||
.verifySystemClockWasNotSetAndResetCallTracking()
|
.verifySystemClockWasNotSetAndResetCallTracking()
|
||||||
.assertLatestPhoneSuggestion(phoneId, timeSuggestion);
|
.assertLatestPhoneSuggestion(phoneId, timeSuggestion);
|
||||||
@@ -260,9 +262,8 @@ public class TimeDetectorStrategyImplTest {
|
|||||||
TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
|
TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
|
||||||
|
|
||||||
// Initialize the strategy / device with a time set from a phone suggestion.
|
// Initialize the strategy / device with a time set from a phone suggestion.
|
||||||
mScript.simulateTimePassing(100);
|
mScript.simulateTimePassing();
|
||||||
long expectedSystemClockMillis1 =
|
long expectedSystemClockMillis1 = mScript.calculateTimeInMillisForNow(utcTime1);
|
||||||
TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
|
|
||||||
mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
|
mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
|
||||||
.verifySystemClockWasSetAndResetCallTracking(
|
.verifySystemClockWasSetAndResetCallTracking(
|
||||||
expectedSystemClockMillis1, true /* expectNetworkBroadcast */)
|
expectedSystemClockMillis1, true /* expectNetworkBroadcast */)
|
||||||
@@ -299,8 +300,7 @@ public class TimeDetectorStrategyImplTest {
|
|||||||
long validReferenceTimeMillis = utcTime1.getReferenceTimeMillis() + 100;
|
long validReferenceTimeMillis = utcTime1.getReferenceTimeMillis() + 100;
|
||||||
TimestampedValue<Long> utcTime4 = new TimestampedValue<>(
|
TimestampedValue<Long> utcTime4 = new TimestampedValue<>(
|
||||||
validReferenceTimeMillis, validUtcTimeMillis);
|
validReferenceTimeMillis, validUtcTimeMillis);
|
||||||
long expectedSystemClockMillis4 =
|
long expectedSystemClockMillis4 = mScript.calculateTimeInMillisForNow(utcTime4);
|
||||||
TimeDetectorStrategy.getTimeAt(utcTime4, mScript.peekElapsedRealtimeMillis());
|
|
||||||
PhoneTimeSuggestion timeSuggestion4 =
|
PhoneTimeSuggestion timeSuggestion4 =
|
||||||
createPhoneTimeSuggestion(phoneId, utcTime4);
|
createPhoneTimeSuggestion(phoneId, utcTime4);
|
||||||
mScript.simulatePhoneTimeSuggestion(timeSuggestion4)
|
mScript.simulatePhoneTimeSuggestion(timeSuggestion4)
|
||||||
@@ -335,8 +335,7 @@ public class TimeDetectorStrategyImplTest {
|
|||||||
// Simulate more time passing.
|
// Simulate more time passing.
|
||||||
mScript.simulateTimePassing(clockIncrementMillis);
|
mScript.simulateTimePassing(clockIncrementMillis);
|
||||||
|
|
||||||
long expectedSystemClockMillis1 =
|
long expectedSystemClockMillis1 = mScript.calculateTimeInMillisForNow(utcTime1);
|
||||||
TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
|
|
||||||
|
|
||||||
// Turn on auto time detection.
|
// Turn on auto time detection.
|
||||||
mScript.simulateAutoTimeDetectionToggle()
|
mScript.simulateAutoTimeDetectionToggle()
|
||||||
@@ -357,8 +356,8 @@ public class TimeDetectorStrategyImplTest {
|
|||||||
// Simulate more time passing.
|
// Simulate more time passing.
|
||||||
mScript.simulateTimePassing(clockIncrementMillis);
|
mScript.simulateTimePassing(clockIncrementMillis);
|
||||||
|
|
||||||
long expectedSystemClockMillis2 = TimeDetectorStrategy.getTimeAt(
|
long expectedSystemClockMillis2 =
|
||||||
timeSuggestion2.getUtcTime(), mScript.peekElapsedRealtimeMillis());
|
mScript.calculateTimeInMillisForNow(timeSuggestion2.getUtcTime());
|
||||||
|
|
||||||
// The new time, though valid, should not be set in the system clock because auto time is
|
// The new time, though valid, should not be set in the system clock because auto time is
|
||||||
// disabled.
|
// disabled.
|
||||||
@@ -382,19 +381,21 @@ public class TimeDetectorStrategyImplTest {
|
|||||||
long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
|
long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
|
||||||
PhoneTimeSuggestion phoneSuggestion =
|
PhoneTimeSuggestion phoneSuggestion =
|
||||||
mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
|
mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
|
||||||
int clockIncrementMillis = 1000;
|
|
||||||
|
|
||||||
mScript.simulateTimePassing(clockIncrementMillis)
|
mScript.simulateTimePassing();
|
||||||
.simulatePhoneTimeSuggestion(phoneSuggestion)
|
|
||||||
|
long expectedSystemClockMillis =
|
||||||
|
mScript.calculateTimeInMillisForNow(phoneSuggestion.getUtcTime());
|
||||||
|
mScript.simulatePhoneTimeSuggestion(phoneSuggestion)
|
||||||
.verifySystemClockWasSetAndResetCallTracking(
|
.verifySystemClockWasSetAndResetCallTracking(
|
||||||
testTimeMillis + clockIncrementMillis, true /* expectedNetworkBroadcast */)
|
expectedSystemClockMillis, true /* expectedNetworkBroadcast */)
|
||||||
.assertLatestPhoneSuggestion(phoneId, phoneSuggestion);
|
.assertLatestPhoneSuggestion(phoneId, phoneSuggestion);
|
||||||
|
|
||||||
// Look inside and check what the strategy considers the current best phone suggestion.
|
// Look inside and check what the strategy considers the current best phone suggestion.
|
||||||
assertEquals(phoneSuggestion, mScript.peekBestPhoneSuggestion());
|
assertEquals(phoneSuggestion, mScript.peekBestPhoneSuggestion());
|
||||||
|
|
||||||
// Simulate time passing, long enough that phoneSuggestion is now too old.
|
// 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
|
// Look inside and check what the strategy considers the current best phone suggestion. It
|
||||||
// should still be the, it's just no longer used.
|
// should still be the, it's just no longer used.
|
||||||
@@ -407,13 +408,14 @@ public class TimeDetectorStrategyImplTest {
|
|||||||
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
|
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
|
||||||
.pokeAutoTimeDetectionEnabled(false);
|
.pokeAutoTimeDetectionEnabled(false);
|
||||||
|
|
||||||
long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
|
ManualTimeSuggestion timeSuggestion =
|
||||||
ManualTimeSuggestion timeSuggestion = mScript.generateManualTimeSuggestion(testTimeMillis);
|
mScript.generateManualTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS);
|
||||||
final int clockIncrement = 1000;
|
|
||||||
long expectedSystemClockMillis = testTimeMillis + clockIncrement;
|
|
||||||
|
|
||||||
mScript.simulateTimePassing(clockIncrement)
|
mScript.simulateTimePassing();
|
||||||
.simulateManualTimeSuggestion(timeSuggestion)
|
|
||||||
|
long expectedSystemClockMillis =
|
||||||
|
mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime());
|
||||||
|
mScript.simulateManualTimeSuggestion(timeSuggestion)
|
||||||
.verifySystemClockWasSetAndResetCallTracking(
|
.verifySystemClockWasSetAndResetCallTracking(
|
||||||
expectedSystemClockMillis, false /* expectNetworkBroadcast */);
|
expectedSystemClockMillis, false /* expectNetworkBroadcast */);
|
||||||
}
|
}
|
||||||
@@ -430,21 +432,19 @@ public class TimeDetectorStrategyImplTest {
|
|||||||
long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
|
long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
|
||||||
PhoneTimeSuggestion phoneTimeSuggestion =
|
PhoneTimeSuggestion phoneTimeSuggestion =
|
||||||
mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
|
mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
|
||||||
long expectedAutoClockMillis = phoneTimeSuggestion.getUtcTime().getValue();
|
|
||||||
final int clockIncrement = 1000;
|
|
||||||
|
|
||||||
// Simulate the passage of time.
|
// Simulate the passage of time.
|
||||||
mScript.simulateTimePassing(clockIncrement);
|
mScript.simulateTimePassing();
|
||||||
expectedAutoClockMillis += clockIncrement;
|
|
||||||
|
|
||||||
|
long expectedAutoClockMillis =
|
||||||
|
mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime());
|
||||||
mScript.simulatePhoneTimeSuggestion(phoneTimeSuggestion)
|
mScript.simulatePhoneTimeSuggestion(phoneTimeSuggestion)
|
||||||
.verifySystemClockWasSetAndResetCallTracking(
|
.verifySystemClockWasSetAndResetCallTracking(
|
||||||
expectedAutoClockMillis, true /* expectNetworkBroadcast */)
|
expectedAutoClockMillis, true /* expectNetworkBroadcast */)
|
||||||
.assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
|
.assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
|
||||||
|
|
||||||
// Simulate the passage of time.
|
// Simulate the passage of time.
|
||||||
mScript.simulateTimePassing(clockIncrement);
|
mScript.simulateTimePassing();
|
||||||
expectedAutoClockMillis += clockIncrement;
|
|
||||||
|
|
||||||
// Switch to manual.
|
// Switch to manual.
|
||||||
mScript.simulateAutoTimeDetectionToggle()
|
mScript.simulateAutoTimeDetectionToggle()
|
||||||
@@ -452,26 +452,29 @@ public class TimeDetectorStrategyImplTest {
|
|||||||
.assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
|
.assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
|
||||||
|
|
||||||
// Simulate the passage of time.
|
// Simulate the passage of time.
|
||||||
mScript.simulateTimePassing(clockIncrement);
|
mScript.simulateTimePassing();
|
||||||
expectedAutoClockMillis += clockIncrement;
|
|
||||||
|
|
||||||
// Simulate a manual suggestion 1 day different from the auto suggestion.
|
// Simulate a manual suggestion 1 day different from the auto suggestion.
|
||||||
long manualTimeMillis = testTimeMillis + ONE_DAY_MILLIS;
|
long manualTimeMillis = testTimeMillis + Duration.ofDays(1).toMillis();
|
||||||
long expectedManualClockMillis = manualTimeMillis;
|
|
||||||
ManualTimeSuggestion manualTimeSuggestion =
|
ManualTimeSuggestion manualTimeSuggestion =
|
||||||
mScript.generateManualTimeSuggestion(manualTimeMillis);
|
mScript.generateManualTimeSuggestion(manualTimeMillis);
|
||||||
|
mScript.simulateTimePassing();
|
||||||
|
|
||||||
|
long expectedManualClockMillis =
|
||||||
|
mScript.calculateTimeInMillisForNow(manualTimeSuggestion.getUtcTime());
|
||||||
mScript.simulateManualTimeSuggestion(manualTimeSuggestion)
|
mScript.simulateManualTimeSuggestion(manualTimeSuggestion)
|
||||||
.verifySystemClockWasSetAndResetCallTracking(
|
.verifySystemClockWasSetAndResetCallTracking(
|
||||||
expectedManualClockMillis, false /* expectNetworkBroadcast */)
|
expectedManualClockMillis, false /* expectNetworkBroadcast */)
|
||||||
.assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
|
.assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
|
||||||
|
|
||||||
// Simulate the passage of time.
|
// Simulate the passage of time.
|
||||||
mScript.simulateTimePassing(clockIncrement);
|
mScript.simulateTimePassing();
|
||||||
expectedAutoClockMillis += clockIncrement;
|
|
||||||
|
|
||||||
// Switch back to auto.
|
// Switch back to auto.
|
||||||
mScript.simulateAutoTimeDetectionToggle();
|
mScript.simulateAutoTimeDetectionToggle();
|
||||||
|
|
||||||
|
expectedAutoClockMillis =
|
||||||
|
mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime());
|
||||||
mScript.verifySystemClockWasSetAndResetCallTracking(
|
mScript.verifySystemClockWasSetAndResetCallTracking(
|
||||||
expectedAutoClockMillis, true /* expectNetworkBroadcast */)
|
expectedAutoClockMillis, true /* expectNetworkBroadcast */)
|
||||||
.assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
|
.assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
|
||||||
@@ -492,13 +495,143 @@ public class TimeDetectorStrategyImplTest {
|
|||||||
|
|
||||||
ManualTimeSuggestion timeSuggestion =
|
ManualTimeSuggestion timeSuggestion =
|
||||||
mScript.generateManualTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS);
|
mScript.generateManualTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS);
|
||||||
final int clockIncrement = 1000;
|
|
||||||
|
|
||||||
mScript.simulateTimePassing(clockIncrement)
|
mScript.simulateTimePassing()
|
||||||
.simulateManualTimeSuggestion(timeSuggestion)
|
.simulateManualTimeSuggestion(timeSuggestion)
|
||||||
.verifySystemClockWasNotSetAndResetCallTracking();
|
.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
|
* A fake implementation of TimeDetectorStrategy.Callback. Besides tracking changes and behaving
|
||||||
* like the real thing should, it also asserts preconditions.
|
* like the real thing should, it also asserts preconditions.
|
||||||
@@ -674,6 +807,11 @@ public class TimeDetectorStrategyImplTest {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Script simulateNetworkTimeSuggestion(NetworkTimeSuggestion timeSuggestion) {
|
||||||
|
mTimeDetectorStrategy.suggestNetworkTime(timeSuggestion);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
Script simulateAutoTimeDetectionToggle() {
|
Script simulateAutoTimeDetectionToggle() {
|
||||||
mFakeCallback.simulateAutoTimeZoneDetectionToggle();
|
mFakeCallback.simulateAutoTimeZoneDetectionToggle();
|
||||||
mTimeDetectorStrategy.handleAutoTimeDetectionChanged();
|
mTimeDetectorStrategy.handleAutoTimeDetectionChanged();
|
||||||
@@ -685,6 +823,13 @@ public class TimeDetectorStrategyImplTest {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulates time passing by an arbitrary (but relatively small) amount.
|
||||||
|
*/
|
||||||
|
Script simulateTimePassing() {
|
||||||
|
return simulateTimePassing(999);
|
||||||
|
}
|
||||||
|
|
||||||
Script verifySystemClockWasNotSetAndResetCallTracking() {
|
Script verifySystemClockWasNotSetAndResetCallTracking() {
|
||||||
mFakeCallback.verifySystemClockNotSet();
|
mFakeCallback.verifySystemClockNotSet();
|
||||||
mFakeCallback.verifyIntentWasNotBroadcast();
|
mFakeCallback.verifyIntentWasNotBroadcast();
|
||||||
@@ -710,14 +855,30 @@ public class TimeDetectorStrategyImplTest {
|
|||||||
return this;
|
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
|
* 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() {
|
PhoneTimeSuggestion peekBestPhoneSuggestion() {
|
||||||
return mTimeDetectorStrategy.findBestPhoneSuggestionForTests();
|
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
|
* Generates a ManualTimeSuggestion using the current elapsed realtime clock for the
|
||||||
* reference time.
|
* reference time.
|
||||||
@@ -739,6 +900,24 @@ public class TimeDetectorStrategyImplTest {
|
|||||||
}
|
}
|
||||||
return createPhoneTimeSuggestion(phoneId, time);
|
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,
|
private static PhoneTimeSuggestion createPhoneTimeSuggestion(int phoneId,
|
||||||
|
|||||||
Reference in New Issue
Block a user