Merge "Add a Y2038 check into the time_detector"

This commit is contained in:
Neil Fuller
2022-05-10 11:44:09 +00:00
committed by Gerrit Code Review
18 changed files with 805 additions and 162 deletions

View File

@@ -19,15 +19,14 @@ package android.app.time;
import android.annotation.CurrentTimeMillisLong;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.timedetector.TimeSuggestionHelper;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.ShellCommand;
import android.os.TimestampedValue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.io.PrintWriter;
import java.util.List;
import java.util.Objects;
@@ -75,7 +74,9 @@ public final class ExternalTimeSuggestion implements Parcelable {
public static final @NonNull Creator<ExternalTimeSuggestion> CREATOR =
new Creator<ExternalTimeSuggestion>() {
public ExternalTimeSuggestion createFromParcel(Parcel in) {
return ExternalTimeSuggestion.createFromParcel(in);
TimeSuggestionHelper helper = TimeSuggestionHelper.handleCreateFromParcel(
ExternalTimeSuggestion.class, in);
return new ExternalTimeSuggestion(helper);
}
public ExternalTimeSuggestion[] newArray(int size) {
@@ -83,10 +84,7 @@ public final class ExternalTimeSuggestion implements Parcelable {
}
};
@NonNull
private final TimestampedValue<Long> mUnixEpochTime;
@Nullable
private ArrayList<String> mDebugInfo;
@NonNull private final TimeSuggestionHelper mTimeSuggestionHelper;
/**
* Creates a time suggestion cross-referenced to the elapsed realtime clock. See {@link
@@ -98,17 +96,12 @@ public final class ExternalTimeSuggestion implements Parcelable {
*/
public ExternalTimeSuggestion(@ElapsedRealtimeLong long elapsedRealtimeMillis,
@CurrentTimeMillisLong long suggestionMillis) {
mUnixEpochTime = new TimestampedValue(elapsedRealtimeMillis, suggestionMillis);
mTimeSuggestionHelper = new TimeSuggestionHelper(ExternalTimeSuggestion.class,
new TimestampedValue<>(elapsedRealtimeMillis, suggestionMillis));
}
private static ExternalTimeSuggestion createFromParcel(Parcel in) {
TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */);
ExternalTimeSuggestion suggestion =
new ExternalTimeSuggestion(utcTime.getReferenceTimeMillis(), utcTime.getValue());
@SuppressWarnings("unchecked")
ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
suggestion.mDebugInfo = debugInfo;
return suggestion;
private ExternalTimeSuggestion(@NonNull TimeSuggestionHelper helper) {
mTimeSuggestionHelper = Objects.requireNonNull(helper);
}
@Override
@@ -118,8 +111,7 @@ public final class ExternalTimeSuggestion implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeParcelable(mUnixEpochTime, 0);
dest.writeList(mDebugInfo);
mTimeSuggestionHelper.handleWriteToParcel(dest, flags);
}
/**
@@ -127,7 +119,7 @@ public final class ExternalTimeSuggestion implements Parcelable {
*/
@NonNull
public TimestampedValue<Long> getUnixEpochTime() {
return mUnixEpochTime;
return mTimeSuggestionHelper.getUnixEpochTime();
}
/**
@@ -135,9 +127,7 @@ public final class ExternalTimeSuggestion implements Parcelable {
*/
@NonNull
public List<String> getDebugInfo() {
return mDebugInfo == null
? Collections.emptyList()
: Collections.unmodifiableList(mDebugInfo);
return mTimeSuggestionHelper.getDebugInfo();
}
/**
@@ -146,10 +136,7 @@ public final class ExternalTimeSuggestion implements Parcelable {
* #equals(Object)} and {@link #hashCode()}.
*/
public void addDebugInfo(@NonNull String... debugInfos) {
if (mDebugInfo == null) {
mDebugInfo = new ArrayList<>();
}
mDebugInfo.addAll(Arrays.asList(debugInfos));
mTimeSuggestionHelper.addDebugInfo(debugInfos);
}
@Override
@@ -161,18 +148,29 @@ public final class ExternalTimeSuggestion implements Parcelable {
return false;
}
ExternalTimeSuggestion that = (ExternalTimeSuggestion) o;
return Objects.equals(mUnixEpochTime, that.mUnixEpochTime);
return mTimeSuggestionHelper.handleEquals(that.mTimeSuggestionHelper);
}
@Override
public int hashCode() {
return Objects.hash(mUnixEpochTime);
return mTimeSuggestionHelper.hashCode();
}
@Override
public String toString() {
return "ExternalTimeSuggestion{" + "mUnixEpochTime=" + mUnixEpochTime
+ ", mDebugInfo=" + mDebugInfo
+ '}';
return mTimeSuggestionHelper.handleToString();
}
/** @hide */
public static ExternalTimeSuggestion parseCommandLineArg(@NonNull ShellCommand cmd)
throws IllegalArgumentException {
return new ExternalTimeSuggestion(
TimeSuggestionHelper.handleParseCommandLineArg(ExternalTimeSuggestion.class, cmd));
}
/** @hide */
public static void printCommandLineOpts(PrintWriter pw) {
TimeSuggestionHelper.handlePrintCommandLineOpts(
pw, "External", ExternalTimeSuggestion.class);
}
}

View File

@@ -17,30 +17,19 @@
package android.app.timedetector;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.ShellCommand;
import android.os.TimestampedValue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.io.PrintWriter;
import java.util.List;
import java.util.Objects;
/**
* A time signal from a GNSS source.
*
* <p>{@code unixEpochTime} is the suggested time. The {@code unixEpochTime.value} is the number of
* milliseconds elapsed since 1/1/1970 00:00:00 UTC according to the Unix time system. The {@code
* unixEpochTime.referenceTimeMillis} is the value of the elapsed realtime clock when the {@code
* unixEpochTime.value} was established. Note that the elapsed realtime clock is considered accurate
* but it is volatile, so time suggestions cannot be persisted across device resets.
*
* <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to
* record why the suggestion exists and how it was entered. This information exists only to aid in
* debugging and therefore is used by {@link #toString()}, but it is not for use in detection
* logic and is not considered in {@link #hashCode()} or {@link #equals(Object)}.
* <p>See {@link TimeSuggestionHelper} for property information.
*
* @hide
*/
@@ -49,7 +38,9 @@ public final class GnssTimeSuggestion implements Parcelable {
public static final @NonNull Creator<GnssTimeSuggestion> CREATOR =
new Creator<GnssTimeSuggestion>() {
public GnssTimeSuggestion createFromParcel(Parcel in) {
return GnssTimeSuggestion.createFromParcel(in);
TimeSuggestionHelper helper = TimeSuggestionHelper.handleCreateFromParcel(
GnssTimeSuggestion.class, in);
return new GnssTimeSuggestion(helper);
}
public GnssTimeSuggestion[] newArray(int size) {
@@ -57,21 +48,14 @@ public final class GnssTimeSuggestion implements Parcelable {
}
};
@NonNull private final TimestampedValue<Long> mUnixEpochTime;
@Nullable private ArrayList<String> mDebugInfo;
@NonNull private final TimeSuggestionHelper mTimeSuggestionHelper;
public GnssTimeSuggestion(@NonNull TimestampedValue<Long> unixEpochTime) {
mUnixEpochTime = Objects.requireNonNull(unixEpochTime);
Objects.requireNonNull(unixEpochTime.getValue());
mTimeSuggestionHelper = new TimeSuggestionHelper(GnssTimeSuggestion.class, unixEpochTime);
}
private static GnssTimeSuggestion createFromParcel(Parcel in) {
TimestampedValue<Long> unixEpochTime = in.readParcelable(null /* classLoader */);
GnssTimeSuggestion suggestion = new GnssTimeSuggestion(unixEpochTime);
@SuppressWarnings("unchecked")
ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
suggestion.mDebugInfo = debugInfo;
return suggestion;
private GnssTimeSuggestion(@NonNull TimeSuggestionHelper helper) {
mTimeSuggestionHelper = Objects.requireNonNull(helper);
}
@Override
@@ -81,19 +65,17 @@ public final class GnssTimeSuggestion implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeParcelable(mUnixEpochTime, 0);
dest.writeList(mDebugInfo);
mTimeSuggestionHelper.handleWriteToParcel(dest, flags);
}
@NonNull
public TimestampedValue<Long> getUnixEpochTime() {
return mUnixEpochTime;
return mTimeSuggestionHelper.getUnixEpochTime();
}
@NonNull
public List<String> getDebugInfo() {
return mDebugInfo == null
? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo);
return mTimeSuggestionHelper.getDebugInfo();
}
/**
@@ -102,10 +84,7 @@ public final class GnssTimeSuggestion implements Parcelable {
* {@link #equals(Object)} and {@link #hashCode()}.
*/
public void addDebugInfo(String... debugInfos) {
if (mDebugInfo == null) {
mDebugInfo = new ArrayList<>();
}
mDebugInfo.addAll(Arrays.asList(debugInfos));
mTimeSuggestionHelper.addDebugInfo(debugInfos);
}
@Override
@@ -117,19 +96,29 @@ public final class GnssTimeSuggestion implements Parcelable {
return false;
}
GnssTimeSuggestion that = (GnssTimeSuggestion) o;
return Objects.equals(mUnixEpochTime, that.mUnixEpochTime);
return mTimeSuggestionHelper.handleEquals(that.mTimeSuggestionHelper);
}
@Override
public int hashCode() {
return Objects.hash(mUnixEpochTime);
return mTimeSuggestionHelper.hashCode();
}
@Override
public String toString() {
return "GnssTimeSuggestion{"
+ "mUnixEpochTime=" + mUnixEpochTime
+ ", mDebugInfo=" + mDebugInfo
+ '}';
return mTimeSuggestionHelper.handleToString();
}
/** Parses command line args to create a {@link GnssTimeSuggestion}. */
public static GnssTimeSuggestion parseCommandLineArg(@NonNull ShellCommand cmd)
throws IllegalArgumentException {
TimeSuggestionHelper suggestionHelper =
TimeSuggestionHelper.handleParseCommandLineArg(GnssTimeSuggestion.class, cmd);
return new GnssTimeSuggestion(suggestionHelper);
}
/** Prints the command line args needed to create a {@link GnssTimeSuggestion}. */
public static void printCommandLineOpts(PrintWriter pw) {
TimeSuggestionHelper.handlePrintCommandLineOpts(pw, "GNSS", GnssTimeSuggestion.class);
}
}

View File

@@ -20,27 +20,17 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.ShellCommand;
import android.os.TimestampedValue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.io.PrintWriter;
import java.util.List;
import java.util.Objects;
/**
* A time signal from a manual (user provided) source.
*
* <p>{@code unixEpochTime} is the suggested time. The {@code unixEpochTime.value} is the number of
* milliseconds elapsed since 1/1/1970 00:00:00 UTC. The {@code unixEpochTime.referenceTimeMillis}
* is the value of the elapsed realtime clock when the {@code unixEpochTime.value} was established.
* Note that the elapsed realtime clock is considered accurate but it is volatile, so time
* suggestions cannot be persisted across device resets.
*
* <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to
* record why the suggestion exists and how it was entered. This information exists only to aid in
* debugging and therefore is used by {@link #toString()}, but it is not for use in detection
* logic and is not considered in {@link #hashCode()} or {@link #equals(Object)}.
* <p>See {@link TimeSuggestionHelper} for property information.
*
* @hide
*/
@@ -49,7 +39,9 @@ public final class ManualTimeSuggestion implements Parcelable {
public static final @NonNull Creator<ManualTimeSuggestion> CREATOR =
new Creator<ManualTimeSuggestion>() {
public ManualTimeSuggestion createFromParcel(Parcel in) {
return ManualTimeSuggestion.createFromParcel(in);
TimeSuggestionHelper helper = TimeSuggestionHelper.handleCreateFromParcel(
ManualTimeSuggestion.class, in);
return new ManualTimeSuggestion(helper);
}
public ManualTimeSuggestion[] newArray(int size) {
@@ -57,21 +49,14 @@ public final class ManualTimeSuggestion implements Parcelable {
}
};
@NonNull private final TimestampedValue<Long> mUnixEpochTime;
@Nullable private ArrayList<String> mDebugInfo;
@NonNull private final TimeSuggestionHelper mTimeSuggestionHelper;
public ManualTimeSuggestion(@NonNull TimestampedValue<Long> unixEpochTime) {
mUnixEpochTime = Objects.requireNonNull(unixEpochTime);
Objects.requireNonNull(unixEpochTime.getValue());
mTimeSuggestionHelper = new TimeSuggestionHelper(ManualTimeSuggestion.class, unixEpochTime);
}
private static ManualTimeSuggestion createFromParcel(Parcel in) {
TimestampedValue<Long> unixEpochTime = in.readParcelable(null /* classLoader */);
ManualTimeSuggestion suggestion = new ManualTimeSuggestion(unixEpochTime);
@SuppressWarnings("unchecked")
ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
suggestion.mDebugInfo = debugInfo;
return suggestion;
private ManualTimeSuggestion(@NonNull TimeSuggestionHelper helper) {
mTimeSuggestionHelper = Objects.requireNonNull(helper);
}
@Override
@@ -81,19 +66,17 @@ public final class ManualTimeSuggestion implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeParcelable(mUnixEpochTime, 0);
dest.writeList(mDebugInfo);
mTimeSuggestionHelper.handleWriteToParcel(dest, flags);
}
@NonNull
public TimestampedValue<Long> getUnixEpochTime() {
return mUnixEpochTime;
return mTimeSuggestionHelper.getUnixEpochTime();
}
@NonNull
public List<String> getDebugInfo() {
return mDebugInfo == null
? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo);
return mTimeSuggestionHelper.getDebugInfo();
}
/**
@@ -102,10 +85,7 @@ public final class ManualTimeSuggestion implements Parcelable {
* {@link #equals(Object)} and {@link #hashCode()}.
*/
public void addDebugInfo(String... debugInfos) {
if (mDebugInfo == null) {
mDebugInfo = new ArrayList<>();
}
mDebugInfo.addAll(Arrays.asList(debugInfos));
mTimeSuggestionHelper.addDebugInfo(debugInfos);
}
@Override
@@ -117,19 +97,28 @@ public final class ManualTimeSuggestion implements Parcelable {
return false;
}
ManualTimeSuggestion that = (ManualTimeSuggestion) o;
return Objects.equals(mUnixEpochTime, that.mUnixEpochTime);
return mTimeSuggestionHelper.handleEquals(that.mTimeSuggestionHelper);
}
@Override
public int hashCode() {
return Objects.hash(mUnixEpochTime);
return mTimeSuggestionHelper.hashCode();
}
@Override
public String toString() {
return "ManualTimeSuggestion{"
+ "mUnixEpochTime=" + mUnixEpochTime
+ ", mDebugInfo=" + mDebugInfo
+ '}';
return mTimeSuggestionHelper.handleToString();
}
/** @hide */
public static ManualTimeSuggestion parseCommandLineArg(@NonNull ShellCommand cmd)
throws IllegalArgumentException {
return new ManualTimeSuggestion(
TimeSuggestionHelper.handleParseCommandLineArg(ManualTimeSuggestion.class, cmd));
}
/** @hide */
public static void printCommandLineOpts(PrintWriter pw) {
TimeSuggestionHelper.handlePrintCommandLineOpts(pw, "Manual", ManualTimeSuggestion.class);
}
}

View File

@@ -17,31 +17,19 @@
package android.app.timedetector;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.ShellCommand;
import android.os.TimestampedValue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.io.PrintWriter;
import java.util.List;
import java.util.Objects;
/**
* A time signal from a network time source like NTP.
*
* <p>{@code unixEpochTime} contains the suggested time. The {@code unixEpochTime.value} is the
* number of milliseconds elapsed since 1/1/1970 00:00:00 UTC according to the Unix time system.
* The {@code unixEpochTime.referenceTimeMillis} is the value of the elapsed realtime clock when
* the {@code unixEpochTime.value} was established. Note that the elapsed realtime clock is
* considered accurate but it is volatile, so time suggestions cannot be persisted across device
* resets.
*
* <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to
* record why the suggestion exists and how it was determined. This information exists only to aid
* in debugging and therefore is used by {@link #toString()}, but it is not for use in detection
* logic and is not considered in {@link #hashCode()} or {@link #equals(Object)}.
* <p>See {@link TimeSuggestionHelper} for property information.
*
* @hide
*/
@@ -50,7 +38,9 @@ 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);
TimeSuggestionHelper helper = TimeSuggestionHelper.handleCreateFromParcel(
NetworkTimeSuggestion.class, in);
return new NetworkTimeSuggestion(helper);
}
public NetworkTimeSuggestion[] newArray(int size) {
@@ -58,21 +48,15 @@ public final class NetworkTimeSuggestion implements Parcelable {
}
};
@NonNull private final TimestampedValue<Long> mUnixEpochTime;
@Nullable private ArrayList<String> mDebugInfo;
@NonNull private final TimeSuggestionHelper mTimeSuggestionHelper;
public NetworkTimeSuggestion(@NonNull TimestampedValue<Long> unixEpochTime) {
mUnixEpochTime = Objects.requireNonNull(unixEpochTime);
Objects.requireNonNull(unixEpochTime.getValue());
mTimeSuggestionHelper = new TimeSuggestionHelper(
NetworkTimeSuggestion.class, unixEpochTime);
}
private static NetworkTimeSuggestion createFromParcel(Parcel in) {
TimestampedValue<Long> unixEpochTime = in.readParcelable(null /* classLoader */);
NetworkTimeSuggestion suggestion = new NetworkTimeSuggestion(unixEpochTime);
@SuppressWarnings("unchecked")
ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
suggestion.mDebugInfo = debugInfo;
return suggestion;
private NetworkTimeSuggestion(@NonNull TimeSuggestionHelper helper) {
mTimeSuggestionHelper = Objects.requireNonNull(helper);
}
@Override
@@ -82,35 +66,30 @@ public final class NetworkTimeSuggestion implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeParcelable(mUnixEpochTime, 0);
dest.writeList(mDebugInfo);
mTimeSuggestionHelper.handleWriteToParcel(dest, flags);
}
@NonNull
public TimestampedValue<Long> getUnixEpochTime() {
return mUnixEpochTime;
return mTimeSuggestionHelper.getUnixEpochTime();
}
@NonNull
public List<String> getDebugInfo() {
return mDebugInfo == null
? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo);
return mTimeSuggestionHelper.getDebugInfo();
}
/**
* 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()}.
* 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));
mTimeSuggestionHelper.addDebugInfo(debugInfos);
}
@Override
public boolean equals(@Nullable Object o) {
public boolean equals(Object o) {
if (this == o) {
return true;
}
@@ -118,19 +97,28 @@ public final class NetworkTimeSuggestion implements Parcelable {
return false;
}
NetworkTimeSuggestion that = (NetworkTimeSuggestion) o;
return Objects.equals(mUnixEpochTime, that.mUnixEpochTime);
return mTimeSuggestionHelper.handleEquals(that.mTimeSuggestionHelper);
}
@Override
public int hashCode() {
return Objects.hash(mUnixEpochTime);
return mTimeSuggestionHelper.hashCode();
}
@Override
public String toString() {
return "NetworkTimeSuggestion{"
+ "mUnixEpochTime=" + mUnixEpochTime
+ ", mDebugInfo=" + mDebugInfo
+ '}';
return mTimeSuggestionHelper.handleToString();
}
/** @hide */
public static NetworkTimeSuggestion parseCommandLineArg(@NonNull ShellCommand cmd)
throws IllegalArgumentException {
return new NetworkTimeSuggestion(
TimeSuggestionHelper.handleParseCommandLineArg(NetworkTimeSuggestion.class, cmd));
}
/** @hide */
public static void printCommandLineOpts(PrintWriter pw) {
TimeSuggestionHelper.handlePrintCommandLineOpts(pw, "Network", NetworkTimeSuggestion.class);
}
}

View File

@@ -20,8 +20,10 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.ShellCommand;
import android.os.TimestampedValue;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -88,6 +90,61 @@ public final class TelephonyTimeSuggestion implements Parcelable {
return suggestion;
}
/** @hide */
public static TelephonyTimeSuggestion parseCommandLineArg(@NonNull ShellCommand cmd)
throws IllegalArgumentException {
Integer slotIndex = null;
Long referenceTimeMillis = null;
Long unixEpochTimeMillis = null;
String opt;
while ((opt = cmd.getNextArg()) != null) {
switch (opt) {
case "--slot_index": {
slotIndex = Integer.parseInt(cmd.getNextArgRequired());
break;
}
case "--reference_time": {
referenceTimeMillis = Long.parseLong(cmd.getNextArgRequired());
break;
}
case "--unix_epoch_time": {
unixEpochTimeMillis = Long.parseLong(cmd.getNextArgRequired());
break;
}
default: {
throw new IllegalArgumentException("Unknown option: " + opt);
}
}
}
if (slotIndex == null) {
throw new IllegalArgumentException("No slotIndex specified.");
}
if (referenceTimeMillis == null) {
throw new IllegalArgumentException("No referenceTimeMillis specified.");
}
if (unixEpochTimeMillis == null) {
throw new IllegalArgumentException("No unixEpochTimeMillis specified.");
}
TimestampedValue<Long> timeSignal =
new TimestampedValue<>(referenceTimeMillis, unixEpochTimeMillis);
Builder builder = new Builder(slotIndex)
.setUnixEpochTime(timeSignal)
.addDebugInfo("Command line injection");
return builder.build();
}
/** @hide */
public static void printCommandLineOpts(PrintWriter pw) {
pw.println("Telephony suggestion options:");
pw.println(" --slot_index <number>");
pw.println(" --reference_time <elapsed realtime millis>");
pw.println(" --unix_epoch_time <Unix epoch time millis>");
pw.println();
pw.println("See " + TelephonyTimeSuggestion.class.getName() + " for more information");
}
@Override
public int describeContents() {
return 0;

View File

@@ -43,6 +43,36 @@ public interface TimeDetector {
*/
String SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED = "is_auto_detection_enabled";
/**
* A shell command that injects a manual time suggestion.
* @hide
*/
String SHELL_COMMAND_SUGGEST_MANUAL_TIME = "suggest_manual_time";
/**
* A shell command that injects a telephony time suggestion.
* @hide
*/
String SHELL_COMMAND_SUGGEST_TELEPHONY_TIME = "suggest_telephony_time";
/**
* A shell command that injects a network time suggestion.
* @hide
*/
String SHELL_COMMAND_SUGGEST_NETWORK_TIME = "suggest_network_time";
/**
* A shell command that injects a GNSS time suggestion.
* @hide
*/
String SHELL_COMMAND_SUGGEST_GNSS_TIME = "suggest_gnss_time";
/**
* A shell command that injects a external time suggestion.
* @hide
*/
String SHELL_COMMAND_SUGGEST_EXTERNAL_TIME = "suggest_external_time";
/**
* A shared utility method to create a {@link ManualTimeSuggestion}.
*

View File

@@ -0,0 +1,209 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.timedetector;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.ShellCommand;
import android.os.TimestampedValue;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* A delegate class to support time suggestion classes that could diverge in the future. This class
* exists purely for code re-use and provides support methods. It avoids class inheritance
* deliberately to allow each suggestion to evolve in different directions later without affecting
* SDK APIs.
*
* <p>{@code unixEpochTime} is the suggested time. The {@code unixEpochTime.value} is the number of
* milliseconds elapsed since 1/1/1970 00:00:00 UTC according to the Unix time system. The {@code
* unixEpochTime.referenceTimeMillis} is the value of the elapsed realtime clock when the {@code
* unixEpochTime.value} was established. Note that the elapsed realtime clock is considered accurate
* but it is volatile, so time suggestions cannot be persisted across device resets.
*
* <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to
* record why the suggestion exists and how it was entered. This information exists only to aid in
* debugging and therefore is used by {@link #toString()}, but it is not for use in detection
* logic and is not considered in {@link #hashCode()} or {@link #equals(Object)}.
*
* @hide
*/
public final class TimeSuggestionHelper {
@NonNull private final Class<?> mHelpedClass;
@NonNull private final TimestampedValue<Long> mUnixEpochTime;
@Nullable private ArrayList<String> mDebugInfo;
/** Creates a helper for the specified class, containing the supplied properties. */
public TimeSuggestionHelper(@NonNull Class<?> helpedClass,
@NonNull TimestampedValue<Long> unixEpochTime) {
mHelpedClass = Objects.requireNonNull(helpedClass);
mUnixEpochTime = Objects.requireNonNull(unixEpochTime);
Objects.requireNonNull(unixEpochTime.getValue());
}
/** See {@link TimeSuggestionHelper} for property details. */
@NonNull
public TimestampedValue<Long> getUnixEpochTime() {
return mUnixEpochTime;
}
/** See {@link TimeSuggestionHelper} for information about {@code debugInfo}. */
@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.
*
* <p>See {@link TimeSuggestionHelper} for more information about {@code debugInfo}.
*/
public void addDebugInfo(@NonNull String debugInfo) {
if (mDebugInfo == null) {
mDebugInfo = new ArrayList<>();
}
mDebugInfo.add(debugInfo);
}
/**
* 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) {
addDebugInfo(Arrays.asList(debugInfos));
}
/**
* Associates information with the instance that can be useful for debugging / logging.
*
* <p>See {@link TimeSuggestionHelper} for more information about {@code debugInfo}.
*/
public void addDebugInfo(@NonNull List<String> debugInfo) {
if (mDebugInfo == null) {
mDebugInfo = new ArrayList<>(debugInfo.size());
}
mDebugInfo.addAll(debugInfo);
}
/**
* Implemented in case users call this insteam of {@link #handleEquals(TimeSuggestionHelper)}.
*/
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TimeSuggestionHelper that = (TimeSuggestionHelper) o;
return handleEquals(that);
}
/** Used to implement {@link Object#equals(Object)}. */
public boolean handleEquals(TimeSuggestionHelper o) {
return Objects.equals(mHelpedClass, o.mHelpedClass)
&& Objects.equals(mUnixEpochTime, o.mUnixEpochTime);
}
@Override
public int hashCode() {
return Objects.hash(mUnixEpochTime);
}
/** Used to implement {@link Object#toString()}. */
public String handleToString() {
return mHelpedClass.getSimpleName() + "{"
+ "mUnixEpochTime=" + mUnixEpochTime
+ ", mDebugInfo=" + mDebugInfo
+ '}';
}
/** Constructs a helper with suggestion state from a Parcel. */
public static TimeSuggestionHelper handleCreateFromParcel(@NonNull Class<?> helpedClass,
@NonNull Parcel in) {
@SuppressWarnings("unchecked")
TimestampedValue<Long> unixEpochTime = in.readParcelable(
null /* classLoader */, TimestampedValue.class);
TimeSuggestionHelper suggestionHelper =
new TimeSuggestionHelper(helpedClass, unixEpochTime);
suggestionHelper.mDebugInfo = in.readArrayList(null /* classLoader */, String.class);
return suggestionHelper;
}
/** Writes the helper suggestion state to a Parcel. */
public void handleWriteToParcel(@NonNull Parcel dest, int flags) {
dest.writeParcelable(mUnixEpochTime, 0);
dest.writeList(mDebugInfo);
}
/** Parses command line args to create a {@link TimeSuggestionHelper}. */
public static TimeSuggestionHelper handleParseCommandLineArg(
@NonNull Class<?> helpedClass, @NonNull ShellCommand cmd)
throws IllegalArgumentException {
Long referenceTimeMillis = null;
Long unixEpochTimeMillis = null;
String opt;
while ((opt = cmd.getNextArg()) != null) {
switch (opt) {
case "--reference_time": {
referenceTimeMillis = Long.parseLong(cmd.getNextArgRequired());
break;
}
case "--unix_epoch_time": {
unixEpochTimeMillis = Long.parseLong(cmd.getNextArgRequired());
break;
}
default: {
throw new IllegalArgumentException("Unknown option: " + opt);
}
}
}
if (referenceTimeMillis == null) {
throw new IllegalArgumentException("No referenceTimeMillis specified.");
}
if (unixEpochTimeMillis == null) {
throw new IllegalArgumentException("No unixEpochTimeMillis specified.");
}
TimestampedValue<Long> timeSignal =
new TimestampedValue<>(referenceTimeMillis, unixEpochTimeMillis);
TimeSuggestionHelper suggestionHelper = new TimeSuggestionHelper(helpedClass, timeSignal);
suggestionHelper.addDebugInfo("Command line injection");
return suggestionHelper;
}
/** Prints the command line args needed to create a {@link TimeSuggestionHelper}. */
public static void handlePrintCommandLineOpts(
@NonNull PrintWriter pw, @NonNull String typeName, @NonNull Class<?> clazz) {
pw.printf("%s suggestion options:\n", typeName);
pw.println(" --reference_time <elapsed realtime millis>");
pw.println(" --unix_epoch_time <Unix epoch time millis>");
pw.println();
pw.println("See " + clazz.getName() + " for more information");
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.time;
import static android.app.timezonedetector.ShellCommandTestSupport.createShellCommandWithArgsAndOptions;
import static org.junit.Assert.assertEquals;
import android.os.ShellCommand;
import org.junit.Test;
/**
* Tests for non-SDK methods on {@link ExternalTimeSuggestion}.
* Also see {@link android.app.time.cts.ExternalTimeSuggestionTest}
*/
public class ExternalTimeSuggestionTest {
@Test(expected = IllegalArgumentException.class)
public void testParseCommandLineArg_noReferenceTime() {
ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
"--unix_epoch_time 12345");
ExternalTimeSuggestion.parseCommandLineArg(testShellCommand);
}
@Test(expected = IllegalArgumentException.class)
public void testParseCommandLineArg_noUnixEpochTime() {
ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
"--reference_time 54321");
ExternalTimeSuggestion.parseCommandLineArg(testShellCommand);
}
@Test
public void testParseCommandLineArg_validSuggestion() {
ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
"--reference_time 54321 --unix_epoch_time 12345");
ExternalTimeSuggestion expectedSuggestion = new ExternalTimeSuggestion(54321L, 12345L);
ExternalTimeSuggestion actualSuggestion =
ExternalTimeSuggestion.parseCommandLineArg(testShellCommand);
assertEquals(expectedSuggestion, actualSuggestion);
}
@Test(expected = IllegalArgumentException.class)
public void testParseCommandLineArg_unknownArgument() {
ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
"--reference_time 54321 --unix_epoch_time 12345 --bad_arg 0");
ExternalTimeSuggestion.parseCommandLineArg(testShellCommand);
}
}

View File

@@ -18,10 +18,12 @@ package android.app.timedetector;
import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable;
import static android.app.timezonedetector.ShellCommandTestSupport.createShellCommandWithArgsAndOptions;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import android.os.ShellCommand;
import android.os.TimestampedValue;
import org.junit.Test;
@@ -63,4 +65,36 @@ public class GnssTimeSuggestionTest {
GnssTimeSuggestion rtSuggestion = roundTripParcelable(suggestion);
assertEquals(suggestion.getDebugInfo(), rtSuggestion.getDebugInfo());
}
@Test(expected = IllegalArgumentException.class)
public void testParseCommandLineArg_noReferenceTime() {
ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
"--unix_epoch_time 12345");
GnssTimeSuggestion.parseCommandLineArg(testShellCommand);
}
@Test(expected = IllegalArgumentException.class)
public void testParseCommandLineArg_noUnixEpochTime() {
ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
"--reference_time 54321");
GnssTimeSuggestion.parseCommandLineArg(testShellCommand);
}
@Test
public void testParseCommandLineArg_validSuggestion() {
ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
"--reference_time 54321 --unix_epoch_time 12345");
TimestampedValue<Long> timeSignal = new TimestampedValue<>(54321L, 12345L);
GnssTimeSuggestion expectedSuggestion = new GnssTimeSuggestion(timeSignal);
GnssTimeSuggestion actualSuggestion =
GnssTimeSuggestion.parseCommandLineArg(testShellCommand);
assertEquals(expectedSuggestion, actualSuggestion);
}
@Test(expected = IllegalArgumentException.class)
public void testParseCommandLineArg_unknownArgument() {
ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
"--reference_time 54321 --unix_epoch_time 12345 --bad_arg 0");
GnssTimeSuggestion.parseCommandLineArg(testShellCommand);
}
}

View File

@@ -18,10 +18,12 @@ package android.app.timedetector;
import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable;
import static android.app.timezonedetector.ShellCommandTestSupport.createShellCommandWithArgsAndOptions;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import android.os.ShellCommand;
import android.os.TimestampedValue;
import org.junit.Test;
@@ -63,4 +65,36 @@ public class ManualTimeSuggestionTest {
ManualTimeSuggestion rtSuggestion = roundTripParcelable(suggestion);
assertEquals(suggestion.getDebugInfo(), rtSuggestion.getDebugInfo());
}
@Test(expected = IllegalArgumentException.class)
public void testParseCommandLineArg_noReferenceTime() {
ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
"--unix_epoch_time 12345");
ManualTimeSuggestion.parseCommandLineArg(testShellCommand);
}
@Test(expected = IllegalArgumentException.class)
public void testParseCommandLineArg_noUnixEpochTime() {
ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
"--reference_time 54321");
ManualTimeSuggestion.parseCommandLineArg(testShellCommand);
}
@Test
public void testParseCommandLineArg_validSuggestion() {
ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
"--reference_time 54321 --unix_epoch_time 12345");
TimestampedValue<Long> timeSignal = new TimestampedValue<>(54321L, 12345L);
ManualTimeSuggestion expectedSuggestion = new ManualTimeSuggestion(timeSignal);
ManualTimeSuggestion actualSuggestion =
ManualTimeSuggestion.parseCommandLineArg(testShellCommand);
assertEquals(expectedSuggestion, actualSuggestion);
}
@Test(expected = IllegalArgumentException.class)
public void testParseCommandLineArg_unknownArgument() {
ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
"--reference_time 54321 --unix_epoch_time 12345 --bad_arg 0");
ManualTimeSuggestion.parseCommandLineArg(testShellCommand);
}
}

View File

@@ -18,10 +18,12 @@ package android.app.timedetector;
import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable;
import static android.app.timezonedetector.ShellCommandTestSupport.createShellCommandWithArgsAndOptions;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import android.os.ShellCommand;
import android.os.TimestampedValue;
import org.junit.Test;
@@ -63,4 +65,36 @@ public class NetworkTimeSuggestionTest {
NetworkTimeSuggestion rtSuggestion = roundTripParcelable(suggestion);
assertEquals(suggestion.getDebugInfo(), rtSuggestion.getDebugInfo());
}
@Test(expected = IllegalArgumentException.class)
public void testParseCommandLineArg_noReferenceTime() {
ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
"--unix_epoch_time 12345");
NetworkTimeSuggestion.parseCommandLineArg(testShellCommand);
}
@Test(expected = IllegalArgumentException.class)
public void testParseCommandLineArg_noUnixEpochTime() {
ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
"--reference_time 54321");
NetworkTimeSuggestion.parseCommandLineArg(testShellCommand);
}
@Test
public void testParseCommandLineArg_validSuggestion() {
ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
"--reference_time 54321 --unix_epoch_time 12345");
TimestampedValue<Long> timeSignal = new TimestampedValue<>(54321L, 12345L);
NetworkTimeSuggestion expectedSuggestion = new NetworkTimeSuggestion(timeSignal);
NetworkTimeSuggestion actualSuggestion =
NetworkTimeSuggestion.parseCommandLineArg(testShellCommand);
assertEquals(expectedSuggestion, actualSuggestion);
}
@Test(expected = IllegalArgumentException.class)
public void testParseCommandLineArg_unknownArgument() {
ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
"--reference_time 54321 --unix_epoch_time 12345 --bad_arg 0");
NetworkTimeSuggestion.parseCommandLineArg(testShellCommand);
}
}

View File

@@ -18,10 +18,12 @@ package android.app.timedetector;
import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable;
import static android.app.timezonedetector.ShellCommandTestSupport.createShellCommandWithArgsAndOptions;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import android.os.ShellCommand;
import android.os.TimestampedValue;
import org.junit.Test;
@@ -95,4 +97,45 @@ public class TelephonyTimeSuggestionTest {
assertEquals(suggestion1.getDebugInfo(), rtSuggestion1.getDebugInfo());
}
}
@Test(expected = IllegalArgumentException.class)
public void testParseCommandLineArg_noSlotIndex() {
ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
"--reference_time 54321 --unix_epoch_time 12345");
TelephonyTimeSuggestion.parseCommandLineArg(testShellCommand);
}
@Test(expected = IllegalArgumentException.class)
public void testParseCommandLineArg_noReferenceTime() {
ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
"--slot_index 0 --unix_epoch_time 12345");
TelephonyTimeSuggestion.parseCommandLineArg(testShellCommand);
}
@Test(expected = IllegalArgumentException.class)
public void testParseCommandLineArg_noUnixEpochTime() {
ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
"--slot_index 0 --reference_time 54321");
TelephonyTimeSuggestion.parseCommandLineArg(testShellCommand);
}
@Test
public void testParseCommandLineArg_validSuggestion() {
ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
"--slot_index 0 --reference_time 54321 --unix_epoch_time 12345");
TelephonyTimeSuggestion expectedSuggestion =
new TelephonyTimeSuggestion.Builder(0)
.setUnixEpochTime(new TimestampedValue<>(54321L, 12345L))
.build();
TelephonyTimeSuggestion actualSuggestion =
TelephonyTimeSuggestion.parseCommandLineArg(testShellCommand);
assertEquals(expectedSuggestion, actualSuggestion);
}
@Test(expected = IllegalArgumentException.class)
public void testParseCommandLineArg_unknownArgument() {
ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
"--slot_index 0 --reference_time 54321 --unix_epoch_time 12345 --bad_arg 0");
TelephonyTimeSuggestion.parseCommandLineArg(testShellCommand);
}
}

View File

@@ -26,14 +26,14 @@ import java.util.Arrays;
import java.util.List;
/** Utility methods related to {@link ShellCommand} objects used in several tests. */
final class ShellCommandTestSupport {
public final class ShellCommandTestSupport {
private ShellCommandTestSupport() {}
static ShellCommand createShellCommandWithArgsAndOptions(String argsWithSpaces) {
public static ShellCommand createShellCommandWithArgsAndOptions(String argsWithSpaces) {
return createShellCommandWithArgsAndOptions(Arrays.asList(argsWithSpaces.split(" ")));
}
static ShellCommand createShellCommandWithArgsAndOptions(List<String> args) {
public static ShellCommand createShellCommandWithArgsAndOptions(List<String> args) {
ShellCommand command = mock(ShellCommand.class);
class ArgProvider {
private int mCount;

View File

@@ -478,6 +478,12 @@
<uses-permission android:name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" />
<uses-permission android:name="android.permission.SUGGEST_EXTERNAL_TIME" />
<!-- Permissions used for manual testing of time detection behavior. -->
<uses-permission android:name="android.permission.SUGGEST_MANUAL_TIME" />
<uses-permission android:name="android.permission.SUGGEST_TELEPHONY_TIME" />
<uses-permission android:name="android.permission.SUGGEST_NETWORK_TIME" />
<uses-permission android:name="android.permission.SUGGEST_GNSS_TIME" />
<!-- Permission required for CTS test - android.server.biometrics -->
<uses-permission android:name="android.permission.USE_BIOMETRIC" />

View File

@@ -22,6 +22,7 @@ import android.app.AlarmManager;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.os.Build;
import android.os.Handler;
import android.os.PowerManager;
import android.os.SystemClock;
@@ -166,6 +167,11 @@ final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environment {
mWakeLock.release();
}
@Override
public boolean deviceHasY2038Issue() {
return Build.SUPPORTED_32_BIT_ABIS.length > 0;
}
private void checkWakeLockHeld() {
if (!mWakeLock.isHeld()) {
Slog.wtf(LOG_TAG, "WakeLock " + mWakeLock + " not held");

View File

@@ -17,14 +17,26 @@ package com.android.server.timedetector;
import static android.app.timedetector.TimeDetector.SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED;
import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SERVICE_NAME;
import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SUGGEST_EXTERNAL_TIME;
import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SUGGEST_GNSS_TIME;
import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SUGGEST_MANUAL_TIME;
import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SUGGEST_NETWORK_TIME;
import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SUGGEST_TELEPHONY_TIME;
import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME;
import static com.android.server.timedetector.ServerFlags.KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE;
import static com.android.server.timedetector.ServerFlags.KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE;
import android.app.time.ExternalTimeSuggestion;
import android.app.timedetector.GnssTimeSuggestion;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.NetworkTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion;
import android.os.ShellCommand;
import java.io.PrintWriter;
import java.util.function.Consumer;
import java.util.function.Supplier;
/** Implements the shell command interface for {@link TimeDetectorService}. */
class TimeDetectorShellCommand extends ShellCommand {
@@ -44,6 +56,16 @@ class TimeDetectorShellCommand extends ShellCommand {
switch (cmd) {
case SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED:
return runIsAutoDetectionEnabled();
case SHELL_COMMAND_SUGGEST_MANUAL_TIME:
return runSuggestManualTime();
case SHELL_COMMAND_SUGGEST_TELEPHONY_TIME:
return runSuggestTelephonyTime();
case SHELL_COMMAND_SUGGEST_NETWORK_TIME:
return runSuggestNetworkTime();
case SHELL_COMMAND_SUGGEST_GNSS_TIME:
return runSuggestGnssTime();
case SHELL_COMMAND_SUGGEST_EXTERNAL_TIME:
return runSuggestExternalTime();
default: {
return handleDefaultCommands(cmd);
}
@@ -59,6 +81,53 @@ class TimeDetectorShellCommand extends ShellCommand {
return 0;
}
private int runSuggestManualTime() {
return runSuggestTime(
() -> ManualTimeSuggestion.parseCommandLineArg(this),
mInterface::suggestManualTime);
}
private int runSuggestTelephonyTime() {
return runSuggestTime(
() -> TelephonyTimeSuggestion.parseCommandLineArg(this),
mInterface::suggestTelephonyTime);
}
private int runSuggestNetworkTime() {
return runSuggestTime(
() -> NetworkTimeSuggestion.parseCommandLineArg(this),
mInterface::suggestNetworkTime);
}
private int runSuggestGnssTime() {
return runSuggestTime(
() -> GnssTimeSuggestion.parseCommandLineArg(this),
mInterface::suggestGnssTime);
}
private int runSuggestExternalTime() {
return runSuggestTime(
() -> ExternalTimeSuggestion.parseCommandLineArg(this),
mInterface::suggestExternalTime);
}
private <T> int runSuggestTime(Supplier<T> suggestionParser, Consumer<T> invoker) {
final PrintWriter pw = getOutPrintWriter();
try {
T suggestion = suggestionParser.get();
if (suggestion == null) {
pw.println("Error: suggestion not specified");
return 1;
}
invoker.accept(suggestion);
pw.println("Suggestion " + suggestion + " injected.");
return 0;
} catch (RuntimeException e) {
pw.println(e);
return 1;
}
}
@Override
public void onHelp() {
final PrintWriter pw = getOutPrintWriter();
@@ -68,6 +137,22 @@ class TimeDetectorShellCommand extends ShellCommand {
pw.printf(" %s\n", SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED);
pw.printf(" Prints true/false according to the automatic time detection setting.\n");
pw.println();
pw.printf(" %s <manual suggestion opts>\n", SHELL_COMMAND_SUGGEST_MANUAL_TIME);
pw.printf(" %s <telephony suggestion opts>\n", SHELL_COMMAND_SUGGEST_TELEPHONY_TIME);
pw.printf(" %s <network suggestion opts>\n", SHELL_COMMAND_SUGGEST_NETWORK_TIME);
pw.printf(" %s <gnss suggestion opts>\n", SHELL_COMMAND_SUGGEST_GNSS_TIME);
pw.printf(" %s <external suggestion opts>\n", SHELL_COMMAND_SUGGEST_EXTERNAL_TIME);
pw.println();
ManualTimeSuggestion.printCommandLineOpts(pw);
pw.println();
TelephonyTimeSuggestion.printCommandLineOpts(pw);
pw.println();
NetworkTimeSuggestion.printCommandLineOpts(pw);
pw.println();
GnssTimeSuggestion.printCommandLineOpts(pw);
pw.println();
ExternalTimeSuggestion.printCommandLineOpts(pw);
pw.println();
pw.printf("This service is also affected by the following device_config flags in the"
+ " %s namespace:\n", NAMESPACE_SYSTEM_TIME);
pw.printf(" %s\n", KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE);

View File

@@ -85,6 +85,9 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
*/
private static final int KEEP_SUGGESTION_HISTORY_SIZE = 10;
/** The value in Unix epoch milliseconds of the Y2038 issue. */
private static final long Y2038_LIMIT_IN_MILLIS = 1000L * Integer.MAX_VALUE;
/**
* A log that records the decisions / decision metadata that affected the device's system clock
* time. This is logged in bug reports to assist with debugging issues with detection.
@@ -185,6 +188,12 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
/** Release the wake lock acquired by a call to {@link #acquireWakeLock()}. */
void releaseWakeLock();
/**
* Returns {@code true} if the device may be at risk of time_t overflow (because bionic
* defines time_t as a 32-bit signed integer for 32-bit processes).
*/
boolean deviceHasY2038Issue();
}
static TimeDetectorStrategy create(
@@ -333,6 +342,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
.mapToObj(TimeDetectorStrategy::originToString)
.collect(joining(",", "[", "]"));
ipw.println("mEnvironment.autoOriginPriorities()=" + priorities);
ipw.println("mEnvironment.deviceHasY2038Issue()=" + mEnvironment.deviceHasY2038Issue());
ipw.println("Time change log:");
ipw.increaseIndent(); // level 2
@@ -413,6 +423,16 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
+ ", suggestion=" + suggestion);
return false;
}
if (newUnixEpochTime.getValue() > Y2038_LIMIT_IN_MILLIS
&& mEnvironment.deviceHasY2038Issue()) {
// This check won't prevent a device's system clock exceeding Integer.MAX_VALUE Unix
// seconds through the normal passage of time, but it will stop it jumping above 2038
// because of a "bad" suggestion. b/204193177
Slog.w(LOG_TAG, "Suggested value is above max time supported by this device."
+ " suggestion=" + suggestion);
return false;
}
return true;
}

View File

@@ -1131,6 +1131,49 @@ public class TimeDetectorStrategyImplTest {
.verifySystemClockWasSetAndResetCallTracking(ARBITRARY_TEST_TIME.toEpochMilli());
}
@Test
public void manualY2038SuggestionsAreRejectedOnAffectedDevices() {
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
.pokeAutoTimeDetectionEnabled(false)
.pokeAutoOriginPriorities(ORIGIN_TELEPHONY)
.pokeDeviceHasY2038Issues(true);
Instant y2038IssueTime = Instant.ofEpochMilli((1L + Integer.MAX_VALUE) * 1000L);
ManualTimeSuggestion timeSuggestion = mScript.generateManualTimeSuggestion(y2038IssueTime);
mScript.simulateManualTimeSuggestion(timeSuggestion, false /* expectedResult */)
.verifySystemClockWasNotSetAndResetCallTracking();
}
@Test
public void telephonyY2038SuggestionsAreRejectedOnAffectedDevices() {
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
.pokeAutoTimeDetectionEnabled(true)
.pokeAutoOriginPriorities(ORIGIN_TELEPHONY)
.pokeDeviceHasY2038Issues(true);
final int slotIndex = 0;
Instant y2038IssueTime = Instant.ofEpochMilli((1L + Integer.MAX_VALUE) * 1000L);
TelephonyTimeSuggestion timeSuggestion =
mScript.generateTelephonyTimeSuggestion(slotIndex, y2038IssueTime);
mScript.simulateTelephonyTimeSuggestion(timeSuggestion)
.verifySystemClockWasNotSetAndResetCallTracking();
}
@Test
public void telephonyY2038SuggestionsAreNotRejectedOnUnaffectedDevices() {
mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
.pokeAutoTimeDetectionEnabled(true)
.pokeAutoOriginPriorities(ORIGIN_TELEPHONY)
.pokeDeviceHasY2038Issues(false);
final int slotIndex = 0;
Instant y2038IssueTime = Instant.ofEpochMilli((1L + Integer.MAX_VALUE) * 1000L);
TelephonyTimeSuggestion timeSuggestion =
mScript.generateTelephonyTimeSuggestion(slotIndex, y2038IssueTime);
mScript.simulateTelephonyTimeSuggestion(timeSuggestion)
.verifySystemClockWasSetAndResetCallTracking(y2038IssueTime.toEpochMilli());
}
/**
* A fake implementation of {@link TimeDetectorStrategyImpl.Environment}. Besides tracking
* changes and behaving like the real thing should, it also asserts preconditions.
@@ -1143,6 +1186,7 @@ public class TimeDetectorStrategyImplTest {
private int mSystemClockUpdateThresholdMillis = 2000;
private int[] mAutoOriginPriorities = PROVIDERS_PRIORITY;
private ConfigurationChangeListener mConfigChangeListener;
private boolean mDeviceHas2038Issues = false;
// Tracking operations.
private boolean mSystemClockWasSet;
@@ -1208,6 +1252,15 @@ public class TimeDetectorStrategyImplTest {
mWakeLockAcquired = false;
}
public void setDeviceHas2038Issues(boolean hasIssues) {
mDeviceHas2038Issues = hasIssues;
}
@Override
public boolean deviceHasY2038Issue() {
return mDeviceHas2038Issues;
}
// Methods below are for managing the fake's behavior.
void pokeSystemClockUpdateThreshold(int thresholdMillis) {
@@ -1304,6 +1357,11 @@ public class TimeDetectorStrategyImplTest {
return this;
}
Script pokeDeviceHasY2038Issues(boolean hasIssues) {
mFakeEnvironment.setDeviceHas2038Issues(hasIssues);
return this;
}
long peekElapsedRealtimeMillis() {
return mFakeEnvironment.peekElapsedRealtimeMillis();
}