Make TimeDetectorService more aware of origin
Make TimeDetectorService more aware of a time signal's origin by making the time signal from telephony less generic. When TimeDetectorService was written it was assumed that the TimeSignal could be generic and there would just be a String source identifier to indicate origin. With multiple SIMs / phones on a device it makes sense to keep track of the originating phone of the signal so that the time detector service could implement stickiness if it wanted. This would avoid a device alternating back and forth between two signals and help with debugging. As new signals are added, new "suggestXxxxTime()" methods will be added to TimeDetectorService. That is the point at which the strategy for ranking different sources can be decided. e.g. should NTP always rank higher than NITZ? The main change here is that TimeSignal is renamed to PhoneTimeSuggestion to indicate it's only for use by signals from telephony, sourceId has been removed and it has been given a phoneId instead. Extra free-form metadata (mDebugInfo) has been added to assist with logging / debugging. Bug:140712361 Test: atest android.app.timedetector Test: atest com.android.server.timedetector Change-Id: I5305c34516e5f4e254309193183f00e054801a46
This commit is contained in:
@@ -16,10 +16,10 @@
|
||||
|
||||
package android.app.timedetector;
|
||||
|
||||
import android.app.timedetector.TimeSignal;
|
||||
import android.app.timedetector.PhoneTimeSuggestion;
|
||||
|
||||
/**
|
||||
* System private API to comunicate with time detector service.
|
||||
* System private API to communicate with time detector service.
|
||||
*
|
||||
* <p>Used by parts of the Android system with signals associated with the device's time to provide
|
||||
* information to the Time Detector Service.
|
||||
@@ -32,5 +32,5 @@ import android.app.timedetector.TimeSignal;
|
||||
* {@hide}
|
||||
*/
|
||||
interface ITimeDetectorService {
|
||||
void suggestTime(in TimeSignal timeSignal);
|
||||
void suggestPhoneTime(in PhoneTimeSuggestion timeSuggestion);
|
||||
}
|
||||
|
||||
@@ -16,4 +16,4 @@
|
||||
|
||||
package android.app.timedetector;
|
||||
|
||||
parcelable TimeSignal;
|
||||
parcelable PhoneTimeSuggestion;
|
||||
137
core/java/android/app/timedetector/PhoneTimeSuggestion.java
Normal file
137
core/java/android/app/timedetector/PhoneTimeSuggestion.java
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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 telephony source. 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 PhoneTimeSuggestion implements Parcelable {
|
||||
|
||||
public static final @NonNull Parcelable.Creator<PhoneTimeSuggestion> CREATOR =
|
||||
new Parcelable.Creator<PhoneTimeSuggestion>() {
|
||||
public PhoneTimeSuggestion createFromParcel(Parcel in) {
|
||||
return PhoneTimeSuggestion.createFromParcel(in);
|
||||
}
|
||||
|
||||
public PhoneTimeSuggestion[] newArray(int size) {
|
||||
return new PhoneTimeSuggestion[size];
|
||||
}
|
||||
};
|
||||
|
||||
private final int mPhoneId;
|
||||
@NonNull
|
||||
private final TimestampedValue<Long> mUtcTime;
|
||||
@Nullable
|
||||
private ArrayList<String> mDebugInfo;
|
||||
|
||||
public PhoneTimeSuggestion(int phoneId, @NonNull TimestampedValue<Long> utcTime) {
|
||||
mPhoneId = phoneId;
|
||||
mUtcTime = Objects.requireNonNull(utcTime);
|
||||
}
|
||||
|
||||
private static PhoneTimeSuggestion createFromParcel(Parcel in) {
|
||||
int phoneId = in.readInt();
|
||||
TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */);
|
||||
PhoneTimeSuggestion suggestion = new PhoneTimeSuggestion(phoneId, 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.writeInt(mPhoneId);
|
||||
dest.writeParcelable(mUtcTime, 0);
|
||||
dest.writeList(mDebugInfo);
|
||||
}
|
||||
|
||||
public int getPhoneId() {
|
||||
return mPhoneId;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public TimestampedValue<Long> getUtcTime() {
|
||||
return mUtcTime;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<String> getDebugInfo() {
|
||||
return 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;
|
||||
}
|
||||
PhoneTimeSuggestion that = (PhoneTimeSuggestion) o;
|
||||
return mPhoneId == that.mPhoneId
|
||||
&& Objects.equals(mUtcTime, that.mUtcTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mPhoneId, mUtcTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PhoneTimeSuggestion{"
|
||||
+ "mPhoneId='" + mPhoneId + '\''
|
||||
+ ", mUtcTime=" + mUtcTime
|
||||
+ ", mDebugInfo=" + mDebugInfo
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
@@ -45,12 +45,12 @@ public final class TimeDetector {
|
||||
* signals are available such as those that come from more reliable sources or were
|
||||
* determined more recently.
|
||||
*/
|
||||
public void suggestTime(@NonNull TimeSignal timeSignal) {
|
||||
public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "suggestTime called: " + timeSignal);
|
||||
Log.d(TAG, "suggestPhoneTime called: " + timeSuggestion);
|
||||
}
|
||||
try {
|
||||
mITimeDetectorService.suggestTime(timeSignal);
|
||||
mITimeDetectorService.suggestPhoneTime(timeSuggestion);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.TimestampedValue;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A time signal from a named source. 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 TimeSignal implements Parcelable {
|
||||
|
||||
public static final @android.annotation.NonNull Parcelable.Creator<TimeSignal> CREATOR =
|
||||
new Parcelable.Creator<TimeSignal>() {
|
||||
public TimeSignal createFromParcel(Parcel in) {
|
||||
return TimeSignal.createFromParcel(in);
|
||||
}
|
||||
|
||||
public TimeSignal[] newArray(int size) {
|
||||
return new TimeSignal[size];
|
||||
}
|
||||
};
|
||||
|
||||
public static final String SOURCE_ID_NITZ = "nitz";
|
||||
|
||||
private final String mSourceId;
|
||||
private final TimestampedValue<Long> mUtcTime;
|
||||
|
||||
public TimeSignal(String sourceId, TimestampedValue<Long> utcTime) {
|
||||
mSourceId = Objects.requireNonNull(sourceId);
|
||||
mUtcTime = Objects.requireNonNull(utcTime);
|
||||
}
|
||||
|
||||
private static TimeSignal createFromParcel(Parcel in) {
|
||||
String sourceId = in.readString();
|
||||
TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */);
|
||||
return new TimeSignal(sourceId, utcTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeString(mSourceId);
|
||||
dest.writeParcelable(mUtcTime, 0);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getSourceId() {
|
||||
return mSourceId;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public TimestampedValue<Long> getUtcTime() {
|
||||
return mUtcTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
TimeSignal that = (TimeSignal) o;
|
||||
return Objects.equals(mSourceId, that.mSourceId)
|
||||
&& Objects.equals(mUtcTime, that.mUtcTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mSourceId, mUtcTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TimeSignal{"
|
||||
+ "mSourceId='" + mSourceId + '\''
|
||||
+ ", mUtcTime=" + mUtcTime
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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 org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.TimestampedValue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class PhoneTimeSuggestionTest {
|
||||
private static final int PHONE_ID = 99999;
|
||||
|
||||
@Test
|
||||
public void testEquals() {
|
||||
PhoneTimeSuggestion one =
|
||||
new PhoneTimeSuggestion(PHONE_ID, new TimestampedValue<>(1111L, 2222L));
|
||||
assertEquals(one, one);
|
||||
|
||||
PhoneTimeSuggestion two =
|
||||
new PhoneTimeSuggestion(PHONE_ID, new TimestampedValue<>(1111L, 2222L));
|
||||
assertEquals(one, two);
|
||||
assertEquals(two, one);
|
||||
|
||||
PhoneTimeSuggestion three =
|
||||
new PhoneTimeSuggestion(PHONE_ID + 1, new TimestampedValue<>(1111L, 2222L));
|
||||
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() {
|
||||
PhoneTimeSuggestion one =
|
||||
new PhoneTimeSuggestion(PHONE_ID, new TimestampedValue<>(1111L, 2222L));
|
||||
assertEquals(one, roundTripParcelable(one));
|
||||
|
||||
// DebugInfo should also be stored (but is not checked by equals()
|
||||
one.addDebugInfo("This is debug info");
|
||||
PhoneTimeSuggestion two = roundTripParcelable(one);
|
||||
assertEquals(one.getDebugInfo(), two.getDebugInfo());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends Parcelable> T roundTripParcelable(T one) {
|
||||
Parcel parcel = Parcel.obtain();
|
||||
parcel.writeTypedObject(one, 0);
|
||||
parcel.setDataPosition(0);
|
||||
|
||||
T toReturn = (T) parcel.readTypedObject(PhoneTimeSuggestion.CREATOR);
|
||||
parcel.recycle();
|
||||
return toReturn;
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ package com.android.server.timedetector;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.timedetector.TimeSignal;
|
||||
import android.app.timedetector.PhoneTimeSuggestion;
|
||||
import android.content.Intent;
|
||||
import android.util.Slog;
|
||||
import android.util.TimestampedValue;
|
||||
@@ -48,9 +48,8 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy {
|
||||
// @NonNull after initialize()
|
||||
private Callback mCallback;
|
||||
|
||||
// NITZ state.
|
||||
@Nullable private TimestampedValue<Long> mLastNitzTime;
|
||||
|
||||
// Last phone suggestion.
|
||||
@Nullable private PhoneTimeSuggestion mLastPhoneSuggestion;
|
||||
|
||||
// Information about the last time signal received: Used when toggling auto-time.
|
||||
@Nullable private TimestampedValue<Long> mLastSystemClockTime;
|
||||
@@ -65,46 +64,40 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestTime(@NonNull TimeSignal timeSignal) {
|
||||
if (!TimeSignal.SOURCE_ID_NITZ.equals(timeSignal.getSourceId())) {
|
||||
Slog.w(TAG, "Ignoring signal from unsupported source: " + timeSignal);
|
||||
return;
|
||||
}
|
||||
|
||||
public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) {
|
||||
// NITZ logic
|
||||
|
||||
TimestampedValue<Long> newNitzUtcTime = timeSignal.getUtcTime();
|
||||
boolean nitzTimeIsValid = validateNewNitzTime(newNitzUtcTime, mLastNitzTime);
|
||||
if (!nitzTimeIsValid) {
|
||||
boolean timeSuggestionIsValid =
|
||||
validateNewPhoneSuggestion(timeSuggestion, mLastPhoneSuggestion);
|
||||
if (!timeSuggestionIsValid) {
|
||||
return;
|
||||
}
|
||||
// Always store the last NITZ value received, regardless of whether we go on to use it to
|
||||
// update the system clock. This is so that we can validate future NITZ signals.
|
||||
mLastNitzTime = newNitzUtcTime;
|
||||
mLastPhoneSuggestion = timeSuggestion;
|
||||
|
||||
// System clock update logic.
|
||||
|
||||
// Historically, Android has sent a telephony broadcast only when setting the time using
|
||||
// NITZ.
|
||||
final boolean sendNetworkBroadcast =
|
||||
TimeSignal.SOURCE_ID_NITZ.equals(timeSignal.getSourceId());
|
||||
final boolean sendNetworkBroadcast = true;
|
||||
|
||||
final TimestampedValue<Long> newUtcTime = newNitzUtcTime;
|
||||
final TimestampedValue<Long> newUtcTime = timeSuggestion.getUtcTime();
|
||||
setSystemClockIfRequired(newUtcTime, sendNetworkBroadcast);
|
||||
}
|
||||
|
||||
private static boolean validateNewNitzTime(TimestampedValue<Long> newNitzUtcTime,
|
||||
TimestampedValue<Long> lastNitzTime) {
|
||||
private static boolean validateNewPhoneSuggestion(@NonNull PhoneTimeSuggestion newSuggestion,
|
||||
@Nullable PhoneTimeSuggestion lastSuggestion) {
|
||||
|
||||
if (lastNitzTime != null) {
|
||||
long referenceTimeDifference =
|
||||
TimestampedValue.referenceTimeDifference(newNitzUtcTime, lastNitzTime);
|
||||
if (lastSuggestion != null) {
|
||||
long referenceTimeDifference = TimestampedValue.referenceTimeDifference(
|
||||
newSuggestion.getUtcTime(), lastSuggestion.getUtcTime());
|
||||
if (referenceTimeDifference < 0 || referenceTimeDifference > Integer.MAX_VALUE) {
|
||||
// Out of order or bogus.
|
||||
Slog.w(TAG, "validateNewNitzTime: Bad NITZ signal received."
|
||||
+ " referenceTimeDifference=" + referenceTimeDifference
|
||||
+ " lastNitzTime=" + lastNitzTime
|
||||
+ " newNitzUtcTime=" + newNitzUtcTime);
|
||||
+ " lastSuggestion=" + lastSuggestion
|
||||
+ " newSuggestion=" + newSuggestion);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -182,7 +175,7 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy {
|
||||
|
||||
@Override
|
||||
public void dump(@NonNull PrintWriter pw, @Nullable String[] args) {
|
||||
pw.println("mLastNitzTime=" + mLastNitzTime);
|
||||
pw.println("mLastPhoneSuggestion=" + mLastPhoneSuggestion);
|
||||
pw.println("mLastSystemClockTimeSet=" + mLastSystemClockTimeSet);
|
||||
pw.println("mLastSystemClockTime=" + mLastSystemClockTime);
|
||||
pw.println("mLastSystemClockTimeSendNetworkBroadcast="
|
||||
|
||||
@@ -19,7 +19,7 @@ package com.android.server.timedetector;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.timedetector.ITimeDetectorService;
|
||||
import android.app.timedetector.TimeSignal;
|
||||
import android.app.timedetector.PhoneTimeSuggestion;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
@@ -96,14 +96,14 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestTime(@NonNull TimeSignal timeSignal) {
|
||||
public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSignal) {
|
||||
enforceSetTimePermission();
|
||||
Objects.requireNonNull(timeSignal);
|
||||
|
||||
long idToken = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mStrategyLock) {
|
||||
mTimeDetectorStrategy.suggestTime(timeSignal);
|
||||
mTimeDetectorStrategy.suggestPhoneTime(timeSignal);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(idToken);
|
||||
|
||||
@@ -18,7 +18,7 @@ package com.android.server.timedetector;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.timedetector.TimeSignal;
|
||||
import android.app.timedetector.PhoneTimeSuggestion;
|
||||
import android.content.Intent;
|
||||
import android.util.TimestampedValue;
|
||||
|
||||
@@ -72,7 +72,7 @@ public interface TimeDetectorStrategy {
|
||||
void initialize(@NonNull Callback callback);
|
||||
|
||||
/** Process the suggested time. */
|
||||
void suggestTime(@NonNull TimeSignal timeSignal);
|
||||
void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion);
|
||||
|
||||
/** Handle the auto-time setting being toggled on or off. */
|
||||
void handleAutoTimeDetectionToggle(boolean enabled);
|
||||
|
||||
@@ -23,7 +23,7 @@ import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.app.timedetector.TimeSignal;
|
||||
import android.app.timedetector.PhoneTimeSuggestion;
|
||||
import android.content.Intent;
|
||||
import android.icu.util.Calendar;
|
||||
import android.icu.util.GregorianCalendar;
|
||||
@@ -45,6 +45,8 @@ public class SimpleTimeZoneDetectorStrategyTest {
|
||||
.setActualTimeUtc(2018, 1, 1, 12, 0, 0)
|
||||
.build();
|
||||
|
||||
private static final int ARBITRARY_PHONE_ID = 123456;
|
||||
|
||||
private Script mScript;
|
||||
|
||||
@Before
|
||||
@@ -53,30 +55,32 @@ public class SimpleTimeZoneDetectorStrategyTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuggestTime_nitz_timeDetectionEnabled() {
|
||||
public void testSuggestPhoneTime_nitz_timeDetectionEnabled() {
|
||||
Scenario scenario = SCENARIO_1;
|
||||
mScript.pokeFakeClocks(scenario)
|
||||
.pokeTimeDetectionEnabled(true);
|
||||
|
||||
TimeSignal timeSignal = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
|
||||
PhoneTimeSuggestion timeSuggestion =
|
||||
scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
|
||||
final int clockIncrement = 1000;
|
||||
long expectSystemClockMillis = scenario.getActualTimeMillis() + clockIncrement;
|
||||
|
||||
mScript.simulateTimePassing(clockIncrement)
|
||||
.simulateTimeSignalReceived(timeSignal)
|
||||
.simulatePhoneTimeSuggestion(timeSuggestion)
|
||||
.verifySystemClockWasSetAndResetCallTracking(expectSystemClockMillis);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuggestTime_systemClockThreshold() {
|
||||
public void testSuggestPhoneTime_systemClockThreshold() {
|
||||
Scenario scenario = SCENARIO_1;
|
||||
final int systemClockUpdateThresholdMillis = 1000;
|
||||
mScript.pokeFakeClocks(scenario)
|
||||
.pokeThresholds(systemClockUpdateThresholdMillis)
|
||||
.pokeTimeDetectionEnabled(true);
|
||||
|
||||
TimeSignal timeSignal1 = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
|
||||
TimestampedValue<Long> utcTime1 = timeSignal1.getUtcTime();
|
||||
PhoneTimeSuggestion timeSuggestion1 =
|
||||
scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
|
||||
TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
|
||||
|
||||
final int clockIncrement = 100;
|
||||
// Increment the the device clocks to simulate the passage of time.
|
||||
@@ -86,7 +90,7 @@ public class SimpleTimeZoneDetectorStrategyTest {
|
||||
TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
|
||||
|
||||
// Send the first time signal. It should be used.
|
||||
mScript.simulateTimeSignalReceived(timeSignal1)
|
||||
mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
|
||||
.verifySystemClockWasSetAndResetCallTracking(expectSystemClockMillis1);
|
||||
|
||||
// Now send another time signal, but one that is too similar to the last one and should be
|
||||
@@ -95,9 +99,9 @@ public class SimpleTimeZoneDetectorStrategyTest {
|
||||
TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
|
||||
mScript.peekElapsedRealtimeMillis(),
|
||||
mScript.peekSystemClockMillis() + underThresholdMillis);
|
||||
TimeSignal timeSignal2 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime2);
|
||||
PhoneTimeSuggestion timeSuggestion2 = new PhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime2);
|
||||
mScript.simulateTimePassing(clockIncrement)
|
||||
.simulateTimeSignalReceived(timeSignal2)
|
||||
.simulatePhoneTimeSuggestion(timeSuggestion2)
|
||||
.verifySystemClockWasNotSetAndResetCallTracking();
|
||||
|
||||
// Now send another time signal, but one that is on the threshold and so should be used.
|
||||
@@ -105,42 +109,44 @@ public class SimpleTimeZoneDetectorStrategyTest {
|
||||
mScript.peekElapsedRealtimeMillis(),
|
||||
mScript.peekSystemClockMillis() + systemClockUpdateThresholdMillis);
|
||||
|
||||
TimeSignal timeSignal3 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime3);
|
||||
PhoneTimeSuggestion timeSuggestion3 = new PhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime3);
|
||||
mScript.simulateTimePassing(clockIncrement);
|
||||
|
||||
long expectSystemClockMillis3 =
|
||||
TimeDetectorStrategy.getTimeAt(utcTime3, mScript.peekElapsedRealtimeMillis());
|
||||
|
||||
mScript.simulateTimeSignalReceived(timeSignal3)
|
||||
mScript.simulatePhoneTimeSuggestion(timeSuggestion3)
|
||||
.verifySystemClockWasSetAndResetCallTracking(expectSystemClockMillis3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuggestTime_nitz_timeDetectionDisabled() {
|
||||
public void testSuggestPhoneTime_nitz_timeDetectionDisabled() {
|
||||
Scenario scenario = SCENARIO_1;
|
||||
mScript.pokeFakeClocks(scenario)
|
||||
.pokeTimeDetectionEnabled(false);
|
||||
|
||||
TimeSignal timeSignal = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
|
||||
mScript.simulateTimeSignalReceived(timeSignal)
|
||||
PhoneTimeSuggestion timeSuggestion =
|
||||
scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
|
||||
mScript.simulatePhoneTimeSuggestion(timeSuggestion)
|
||||
.verifySystemClockWasNotSetAndResetCallTracking();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuggestTime_nitz_invalidNitzReferenceTimesIgnored() {
|
||||
public void testSuggestPhoneTime_nitz_invalidNitzReferenceTimesIgnored() {
|
||||
Scenario scenario = SCENARIO_1;
|
||||
final int systemClockUpdateThreshold = 2000;
|
||||
mScript.pokeFakeClocks(scenario)
|
||||
.pokeThresholds(systemClockUpdateThreshold)
|
||||
.pokeTimeDetectionEnabled(true);
|
||||
TimeSignal timeSignal1 = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
|
||||
TimestampedValue<Long> utcTime1 = timeSignal1.getUtcTime();
|
||||
PhoneTimeSuggestion timeSuggestion1 =
|
||||
scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
|
||||
TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
|
||||
|
||||
// Initialize the strategy / device with a time set from NITZ.
|
||||
mScript.simulateTimePassing(100);
|
||||
long expectedSystemClockMillis1 =
|
||||
TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
|
||||
mScript.simulateTimeSignalReceived(timeSignal1)
|
||||
mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
|
||||
.verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1);
|
||||
|
||||
// The UTC time increment should be larger than the system clock update threshold so we
|
||||
@@ -152,8 +158,8 @@ public class SimpleTimeZoneDetectorStrategyTest {
|
||||
long referenceTimeBeforeLastSignalMillis = utcTime1.getReferenceTimeMillis() - 1;
|
||||
TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
|
||||
referenceTimeBeforeLastSignalMillis, validUtcTimeMillis);
|
||||
TimeSignal timeSignal2 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime2);
|
||||
mScript.simulateTimeSignalReceived(timeSignal2)
|
||||
PhoneTimeSuggestion timeSuggestion2 = new PhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime2);
|
||||
mScript.simulatePhoneTimeSuggestion(timeSuggestion2)
|
||||
.verifySystemClockWasNotSetAndResetCallTracking();
|
||||
|
||||
// Now supply a new signal that has an obviously bogus reference time : substantially in the
|
||||
@@ -162,8 +168,8 @@ public class SimpleTimeZoneDetectorStrategyTest {
|
||||
utcTime1.getReferenceTimeMillis() + Integer.MAX_VALUE + 1;
|
||||
TimestampedValue<Long> utcTime3 = new TimestampedValue<>(
|
||||
referenceTimeInFutureMillis, validUtcTimeMillis);
|
||||
TimeSignal timeSignal3 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime3);
|
||||
mScript.simulateTimeSignalReceived(timeSignal3)
|
||||
PhoneTimeSuggestion timeSuggestion3 = new PhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime3);
|
||||
mScript.simulatePhoneTimeSuggestion(timeSuggestion3)
|
||||
.verifySystemClockWasNotSetAndResetCallTracking();
|
||||
|
||||
// Just to prove validUtcTimeMillis is valid.
|
||||
@@ -172,13 +178,13 @@ public class SimpleTimeZoneDetectorStrategyTest {
|
||||
validReferenceTimeMillis, validUtcTimeMillis);
|
||||
long expectedSystemClockMillis4 =
|
||||
TimeDetectorStrategy.getTimeAt(utcTime4, mScript.peekElapsedRealtimeMillis());
|
||||
TimeSignal timeSignal4 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime4);
|
||||
mScript.simulateTimeSignalReceived(timeSignal4)
|
||||
PhoneTimeSuggestion timeSuggestion4 = new PhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime4);
|
||||
mScript.simulatePhoneTimeSuggestion(timeSuggestion4)
|
||||
.verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuggestTime_timeDetectionToggled() {
|
||||
public void testSuggestPhoneTime_timeDetectionToggled() {
|
||||
Scenario scenario = SCENARIO_1;
|
||||
final int clockIncrementMillis = 100;
|
||||
final int systemClockUpdateThreshold = 2000;
|
||||
@@ -186,15 +192,16 @@ public class SimpleTimeZoneDetectorStrategyTest {
|
||||
.pokeThresholds(systemClockUpdateThreshold)
|
||||
.pokeTimeDetectionEnabled(false);
|
||||
|
||||
TimeSignal timeSignal1 = scenario.createTimeSignalForActual(TimeSignal.SOURCE_ID_NITZ);
|
||||
TimestampedValue<Long> utcTime1 = timeSignal1.getUtcTime();
|
||||
PhoneTimeSuggestion timeSuggestion1 =
|
||||
scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
|
||||
TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
|
||||
|
||||
// Simulate time passing.
|
||||
mScript.simulateTimePassing(clockIncrementMillis);
|
||||
|
||||
// Simulate the time signal being received. It should not be used because auto time
|
||||
// detection is off but it should be recorded.
|
||||
mScript.simulateTimeSignalReceived(timeSignal1)
|
||||
mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
|
||||
.verifySystemClockWasNotSetAndResetCallTracking();
|
||||
|
||||
// Simulate more time passing.
|
||||
@@ -216,7 +223,7 @@ public class SimpleTimeZoneDetectorStrategyTest {
|
||||
TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
|
||||
mScript.peekElapsedRealtimeMillis(),
|
||||
mScript.peekSystemClockMillis() + systemClockUpdateThreshold);
|
||||
TimeSignal timeSignal2 = new TimeSignal(TimeSignal.SOURCE_ID_NITZ, utcTime2);
|
||||
PhoneTimeSuggestion timeSuggestion2 = new PhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime2);
|
||||
|
||||
// Simulate more time passing.
|
||||
mScript.simulateTimePassing(clockIncrementMillis);
|
||||
@@ -226,7 +233,7 @@ public class SimpleTimeZoneDetectorStrategyTest {
|
||||
|
||||
// The new time, though valid, should not be set in the system clock because auto time is
|
||||
// disabled.
|
||||
mScript.simulateTimeSignalReceived(timeSignal2)
|
||||
mScript.simulatePhoneTimeSuggestion(timeSuggestion2)
|
||||
.verifySystemClockWasNotSetAndResetCallTracking();
|
||||
|
||||
// Turn on auto time detection.
|
||||
@@ -234,17 +241,6 @@ public class SimpleTimeZoneDetectorStrategyTest {
|
||||
.verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuggestTime_unknownSource() {
|
||||
Scenario scenario = SCENARIO_1;
|
||||
mScript.pokeFakeClocks(scenario)
|
||||
.pokeTimeDetectionEnabled(true);
|
||||
|
||||
TimeSignal timeSignal = scenario.createTimeSignalForActual("unknown");
|
||||
mScript.simulateTimeSignalReceived(timeSignal)
|
||||
.verifySystemClockWasNotSetAndResetCallTracking();
|
||||
}
|
||||
|
||||
/**
|
||||
* A fake implementation of TimeDetectorStrategy.Callback. Besides tracking changes and behaving
|
||||
* like the real thing should, it also asserts preconditions.
|
||||
@@ -407,8 +403,8 @@ public class SimpleTimeZoneDetectorStrategyTest {
|
||||
return mFakeCallback.peekSystemClockMillis();
|
||||
}
|
||||
|
||||
Script simulateTimeSignalReceived(TimeSignal timeSignal) {
|
||||
mSimpleTimeDetectorStrategy.suggestTime(timeSignal);
|
||||
Script simulatePhoneTimeSuggestion(PhoneTimeSuggestion timeSuggestion) {
|
||||
mSimpleTimeDetectorStrategy.suggestPhoneTime(timeSuggestion);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -466,10 +462,10 @@ public class SimpleTimeZoneDetectorStrategyTest {
|
||||
return mActualTimeMillis;
|
||||
}
|
||||
|
||||
TimeSignal createTimeSignalForActual(String sourceId) {
|
||||
PhoneTimeSuggestion createPhoneTimeSuggestionForActual(int phoneId) {
|
||||
TimestampedValue<Long> time = new TimestampedValue<>(
|
||||
mInitialDeviceRealtimeMillis, mActualTimeMillis);
|
||||
return new TimeSignal(sourceId, time);
|
||||
return new PhoneTimeSuggestion(phoneId, time);
|
||||
}
|
||||
|
||||
static class Builder {
|
||||
|
||||
@@ -28,7 +28,7 @@ import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.timedetector.TimeSignal;
|
||||
import android.app.timedetector.PhoneTimeSuggestion;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.util.TimestampedValue;
|
||||
@@ -67,10 +67,10 @@ public class TimeDetectorServiceTest {
|
||||
public void testStubbedCall_withoutPermission() {
|
||||
doThrow(new SecurityException("Mock"))
|
||||
.when(mMockContext).enforceCallingPermission(anyString(), any());
|
||||
TimeSignal timeSignal = createNitzTimeSignal();
|
||||
PhoneTimeSuggestion phoneTimeSuggestion = createPhoneTimeSuggestion();
|
||||
|
||||
try {
|
||||
mTimeDetectorService.suggestTime(timeSignal);
|
||||
mTimeDetectorService.suggestPhoneTime(phoneTimeSuggestion);
|
||||
} finally {
|
||||
verify(mMockContext).enforceCallingPermission(
|
||||
eq(android.Manifest.permission.SET_TIME), anyString());
|
||||
@@ -78,15 +78,15 @@ public class TimeDetectorServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuggestTime() {
|
||||
public void testSuggestPhoneTime() {
|
||||
doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
|
||||
|
||||
TimeSignal timeSignal = createNitzTimeSignal();
|
||||
mTimeDetectorService.suggestTime(timeSignal);
|
||||
PhoneTimeSuggestion phoneTimeSuggestion = createPhoneTimeSuggestion();
|
||||
mTimeDetectorService.suggestPhoneTime(phoneTimeSuggestion);
|
||||
|
||||
verify(mMockContext)
|
||||
.enforceCallingPermission(eq(android.Manifest.permission.SET_TIME), anyString());
|
||||
mStubbedTimeDetectorStrategy.verifySuggestTimeCalled(timeSignal);
|
||||
mStubbedTimeDetectorStrategy.verifySuggestPhoneTimeCalled(phoneTimeSuggestion);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -115,15 +115,16 @@ public class TimeDetectorServiceTest {
|
||||
mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled(false);
|
||||
}
|
||||
|
||||
private static TimeSignal createNitzTimeSignal() {
|
||||
private static PhoneTimeSuggestion createPhoneTimeSuggestion() {
|
||||
int phoneId = 1234;
|
||||
TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L);
|
||||
return new TimeSignal(TimeSignal.SOURCE_ID_NITZ, timeValue);
|
||||
return new PhoneTimeSuggestion(phoneId, timeValue);
|
||||
}
|
||||
|
||||
private static class StubbedTimeDetectorStrategy implements TimeDetectorStrategy {
|
||||
|
||||
// Call tracking.
|
||||
private TimeSignal mLastSuggestedTime;
|
||||
private PhoneTimeSuggestion mLastPhoneSuggestion;
|
||||
private Boolean mLastAutoTimeDetectionToggle;
|
||||
private boolean mDumpCalled;
|
||||
|
||||
@@ -132,9 +133,9 @@ public class TimeDetectorServiceTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestTime(TimeSignal timeSignal) {
|
||||
public void suggestPhoneTime(PhoneTimeSuggestion timeSuggestion) {
|
||||
resetCallTracking();
|
||||
mLastSuggestedTime = timeSignal;
|
||||
mLastPhoneSuggestion = timeSuggestion;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -150,13 +151,13 @@ public class TimeDetectorServiceTest {
|
||||
}
|
||||
|
||||
void resetCallTracking() {
|
||||
mLastSuggestedTime = null;
|
||||
mLastPhoneSuggestion = null;
|
||||
mLastAutoTimeDetectionToggle = null;
|
||||
mDumpCalled = false;
|
||||
}
|
||||
|
||||
void verifySuggestTimeCalled(TimeSignal expectedSignal) {
|
||||
assertEquals(expectedSignal, mLastSuggestedTime);
|
||||
void verifySuggestPhoneTimeCalled(PhoneTimeSuggestion expectedSignal) {
|
||||
assertEquals(expectedSignal, mLastPhoneSuggestion);
|
||||
}
|
||||
|
||||
void verifyHandleAutoTimeDetectionToggleCalled(boolean expectedEnable) {
|
||||
|
||||
Reference in New Issue
Block a user