diff --git a/core/java/android/app/timedetector/ITimeDetectorService.aidl b/core/java/android/app/timedetector/ITimeDetectorService.aidl index b441359b1614b..9b7385ea67335 100644 --- a/core/java/android/app/timedetector/ITimeDetectorService.aidl +++ b/core/java/android/app/timedetector/ITimeDetectorService.aidl @@ -22,7 +22,6 @@ import android.app.time.TimeCapabilitiesAndConfig; import android.app.time.TimeConfiguration; import android.app.timedetector.GnssTimeSuggestion; import android.app.timedetector.ManualTimeSuggestion; -import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.TelephonyTimeSuggestion; import android.app.timedetector.TimePoint; @@ -49,7 +48,6 @@ interface ITimeDetectorService { void suggestExternalTime(in ExternalTimeSuggestion timeSuggestion); void suggestGnssTime(in GnssTimeSuggestion timeSuggestion); boolean suggestManualTime(in ManualTimeSuggestion timeSuggestion); - void suggestNetworkTime(in NetworkTimeSuggestion timeSuggestion); void suggestTelephonyTime(in TelephonyTimeSuggestion timeSuggestion); TimePoint latestNetworkTime(); diff --git a/core/java/android/app/timedetector/NetworkTimeSuggestion.aidl b/core/java/android/app/timedetector/NetworkTimeSuggestion.aidl deleted file mode 100644 index 731c907f6837d..0000000000000 --- a/core/java/android/app/timedetector/NetworkTimeSuggestion.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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; diff --git a/core/java/android/app/timedetector/NetworkTimeSuggestion.java b/core/java/android/app/timedetector/NetworkTimeSuggestion.java deleted file mode 100644 index e93c75cb0a0c5..0000000000000 --- a/core/java/android/app/timedetector/NetworkTimeSuggestion.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * 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.os.Parcel; -import android.os.Parcelable; -import android.os.ShellCommand; -import android.os.TimestampedValue; - -import java.io.PrintWriter; -import java.util.List; -import java.util.Objects; - -/** - * A time signal from a network time source like NTP. - * - *

See {@link TimeSuggestionHelper} for property information. - * - * @hide - */ -public final class NetworkTimeSuggestion implements Parcelable { - - public static final @NonNull Creator CREATOR = - new Creator() { - public NetworkTimeSuggestion createFromParcel(Parcel in) { - TimeSuggestionHelper helper = TimeSuggestionHelper.handleCreateFromParcel( - NetworkTimeSuggestion.class, in); - return new NetworkTimeSuggestion(helper); - } - - public NetworkTimeSuggestion[] newArray(int size) { - return new NetworkTimeSuggestion[size]; - } - }; - - @NonNull private final TimeSuggestionHelper mTimeSuggestionHelper; - - public NetworkTimeSuggestion(@NonNull TimestampedValue unixEpochTime) { - mTimeSuggestionHelper = new TimeSuggestionHelper( - NetworkTimeSuggestion.class, unixEpochTime); - } - - private NetworkTimeSuggestion(@NonNull TimeSuggestionHelper helper) { - mTimeSuggestionHelper = Objects.requireNonNull(helper); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - mTimeSuggestionHelper.handleWriteToParcel(dest, flags); - } - - @NonNull - public TimestampedValue getUnixEpochTime() { - return mTimeSuggestionHelper.getUnixEpochTime(); - } - - @NonNull - public List getDebugInfo() { - 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()}. - */ - public void addDebugInfo(String... debugInfos) { - mTimeSuggestionHelper.addDebugInfo(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 mTimeSuggestionHelper.handleEquals(that.mTimeSuggestionHelper); - } - - @Override - public int hashCode() { - return mTimeSuggestionHelper.hashCode(); - } - - @Override - public String toString() { - 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); - } -} diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java index abfba3a54a43d..c2d3f053127db 100644 --- a/core/java/android/app/timedetector/TimeDetector.java +++ b/core/java/android/app/timedetector/TimeDetector.java @@ -112,14 +112,6 @@ public interface TimeDetector { @RequiresPermission(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE) boolean suggestManualTime(@NonNull ManualTimeSuggestion timeSuggestion); - /** - * Suggests the time according to a network time source like NTP. - * - * @hide - */ - @RequiresPermission(android.Manifest.permission.SET_TIME) - void suggestNetworkTime(NetworkTimeSuggestion timeSuggestion); - /** * Suggests the time according to a gnss time source. * diff --git a/core/java/android/app/timedetector/TimeDetectorImpl.java b/core/java/android/app/timedetector/TimeDetectorImpl.java index b0aa3c8d45756..12eefdd7715a4 100644 --- a/core/java/android/app/timedetector/TimeDetectorImpl.java +++ b/core/java/android/app/timedetector/TimeDetectorImpl.java @@ -63,18 +63,6 @@ public final class TimeDetectorImpl implements TimeDetector { } } - @Override - public void suggestNetworkTime(NetworkTimeSuggestion timeSuggestion) { - if (DEBUG) { - Log.d(TAG, "suggestNetworkTime called: " + timeSuggestion); - } - try { - mITimeDetectorService.suggestNetworkTime(timeSuggestion); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - @Override public void suggestGnssTime(GnssTimeSuggestion timeSuggestion) { if (DEBUG) { diff --git a/core/java/android/app/timedetector/TimeSuggestionHelper.java b/core/java/android/app/timedetector/TimeSuggestionHelper.java index 9b99be61b3c8e..e89839c591d9b 100644 --- a/core/java/android/app/timedetector/TimeSuggestionHelper.java +++ b/core/java/android/app/timedetector/TimeSuggestionHelper.java @@ -201,7 +201,8 @@ public final class 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 "); + pw.println(" --reference_time - the elapsed realtime millis when" + + " unix epoch time was read"); pw.println(" --unix_epoch_time "); pw.println(); pw.println("See " + clazz.getName() + " for more information"); diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java index 4a3f772d3bc69..4036800129364 100644 --- a/core/java/android/util/NtpTrustedTime.java +++ b/core/java/android/util/NtpTrustedTime.java @@ -57,13 +57,13 @@ public class NtpTrustedTime implements TrustedTime { public static class TimeResult { private final long mUnixEpochTimeMillis; private final long mElapsedRealtimeMillis; - private final long mCertaintyMillis; + private final int mUncertaintyMillis; public TimeResult( - long unixEpochTimeMillis, long elapsedRealtimeMillis, long certaintyMillis) { + long unixEpochTimeMillis, long elapsedRealtimeMillis, int uncertaintyMillis) { mUnixEpochTimeMillis = unixEpochTimeMillis; mElapsedRealtimeMillis = elapsedRealtimeMillis; - mCertaintyMillis = certaintyMillis; + mUncertaintyMillis = uncertaintyMillis; } public long getTimeMillis() { @@ -74,8 +74,8 @@ public class NtpTrustedTime implements TrustedTime { return mElapsedRealtimeMillis; } - public long getCertaintyMillis() { - return mCertaintyMillis; + public int getUncertaintyMillis() { + return mUncertaintyMillis; } /** @@ -102,9 +102,9 @@ public class NtpTrustedTime implements TrustedTime { @Override public String toString() { return "TimeResult{" - + "mUnixEpochTimeMillis=" + Instant.ofEpochMilli(mUnixEpochTimeMillis) - + ", mElapsedRealtimeMillis=" + Duration.ofMillis(mElapsedRealtimeMillis) - + ", mCertaintyMillis=" + mCertaintyMillis + + "unixEpochTime=" + Instant.ofEpochMilli(mUnixEpochTimeMillis) + + ", elapsedRealtime=" + Duration.ofMillis(mElapsedRealtimeMillis) + + ", mUncertaintyMillis=" + mUncertaintyMillis + '}'; } } @@ -217,9 +217,9 @@ public class NtpTrustedTime implements TrustedTime { final int port = connectionInfo.getPort(); final int timeoutMillis = connectionInfo.getTimeoutMillis(); if (client.requestTime(serverName, port, timeoutMillis, network)) { - long ntpCertainty = client.getRoundTripTime() / 2; + int ntpUncertaintyMillis = saturatedCast(client.getRoundTripTime() / 2); mTimeResult = new TimeResult( - client.getNtpTime(), client.getNtpTimeReference(), ntpCertainty); + client.getNtpTime(), client.getNtpTimeReference(), ntpUncertaintyMillis); return true; } else { return false; @@ -227,6 +227,19 @@ public class NtpTrustedTime implements TrustedTime { } } + /** + * Casts a {@code long} to an {@code int}, clamping the value within the int range. + */ + private static int saturatedCast(long longValue) { + if (longValue > Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } + if (longValue < Integer.MIN_VALUE) { + return Integer.MIN_VALUE; + } + return (int) longValue; + } + /** * Only kept for UnsupportedAppUsage. * diff --git a/services/core/java/com/android/server/location/gnss/NtpTimeHelper.java b/services/core/java/com/android/server/location/gnss/NtpTimeHelper.java index 8732065ea0093..e4bc78827269f 100644 --- a/services/core/java/com/android/server/location/gnss/NtpTimeHelper.java +++ b/services/core/java/com/android/server/location/gnss/NtpTimeHelper.java @@ -29,8 +29,6 @@ import android.util.NtpTrustedTime; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import java.util.Date; - /** * Handles inject NTP time to GNSS. * @@ -194,18 +192,18 @@ class NtpTimeHelper { return false; } - long time = ntpResult.getTimeMillis(); - long timeReference = ntpResult.getElapsedRealtimeMillis(); - long certainty = ntpResult.getCertaintyMillis(); - + long unixEpochTimeMillis = ntpResult.getTimeMillis(); + long timeReferenceMillis = ntpResult.getElapsedRealtimeMillis(); + int uncertaintyMillis = ntpResult.getUncertaintyMillis(); if (DEBUG) { - long now = System.currentTimeMillis(); - Log.d(TAG, "NTP server returned: " + time + " (" + new Date(time) + ")" - + " ntpResult: " + ntpResult + " system time offset: " + (time - now)); + long currentTimeMillis = System.currentTimeMillis(); + Log.d(TAG, "NTP server returned: " + unixEpochTimeMillis + + " ntpResult: " + ntpResult + + " system time offset: " + (unixEpochTimeMillis - currentTimeMillis)); } - // Ok to cast to int, as can't rollover in practice - mHandler.post(() -> mCallback.injectTime(time, timeReference, (int) certainty)); + mHandler.post(() -> mCallback.injectTime(unixEpochTimeMillis, timeReferenceMillis, + uncertaintyMillis)); return true; } } diff --git a/services/core/java/com/android/server/timedetector/NetworkTimeSuggestion.java b/services/core/java/com/android/server/timedetector/NetworkTimeSuggestion.java new file mode 100644 index 0000000000000..3bdb082728d9f --- /dev/null +++ b/services/core/java/com/android/server/timedetector/NetworkTimeSuggestion.java @@ -0,0 +1,182 @@ +/* + * 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 com.android.server.timedetector; + +import android.annotation.NonNull; +import android.annotation.Nullable; +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 time signal from a network time source like NTP. + * + *

{@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. + * + *

{@code uncertaintyMillis} is an indication of error bounds associated the time. This is a + * positive value, and the correct Unix epoch time is likely to be within the bounds +/- + * the {@code uncertaintyMillis}. The Unix epoch time is not guaranteed to be within these bounds. + * + *

{@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)}. + */ +public final class NetworkTimeSuggestion { + + @NonNull private final TimestampedValue mUnixEpochTime; + private final int mUncertaintyMillis; + @Nullable private ArrayList mDebugInfo; + + /** + * Create a {@link NetworkTimeSuggestion} with the supplied property values. + * + *

See {@link NetworkTimeSuggestion} for property details. + */ + public NetworkTimeSuggestion( + @NonNull TimestampedValue unixEpochTime, int uncertaintyMillis) { + mUnixEpochTime = Objects.requireNonNull(unixEpochTime); + if (uncertaintyMillis < 0) { + throw new IllegalArgumentException("uncertaintyMillis < 0"); + } + mUncertaintyMillis = uncertaintyMillis; + } + + /** See {@link NetworkTimeSuggestion} for property details. */ + @NonNull + public TimestampedValue getUnixEpochTime() { + return mUnixEpochTime; + } + + /** See {@link NetworkTimeSuggestion} for property details. */ + public int getUncertaintyMillis() { + return mUncertaintyMillis; + } + + /** See {@link NetworkTimeSuggestion} for information about {@code debugInfo}. */ + @NonNull + public List 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 instanceof NetworkTimeSuggestion)) { + return false; + } + NetworkTimeSuggestion that = (NetworkTimeSuggestion) o; + return mUnixEpochTime.equals(that.mUnixEpochTime) + && mUncertaintyMillis == that.mUncertaintyMillis; + } + + @Override + public int hashCode() { + return Objects.hash(mUnixEpochTime, mUncertaintyMillis); + } + + @Override + public String toString() { + return "NetworkTimeSuggestion{" + + "mUnixEpochTime=" + mUnixEpochTime + + "mUncertaintyMillis=" + mUncertaintyMillis + + ", mDebugInfo=" + mDebugInfo + + '}'; + } + + /** Parses command line args to create a {@link NetworkTimeSuggestion}. */ + public static NetworkTimeSuggestion parseCommandLineArg(@NonNull ShellCommand cmd) + throws IllegalArgumentException { + Long referenceTimeMillis = null; + Long unixEpochTimeMillis = null; + Integer uncertaintyMillis = 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; + } + case "--uncertainty_millis": { + uncertaintyMillis = Integer.parseInt(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."); + } + if (uncertaintyMillis == null) { + throw new IllegalArgumentException("No uncertaintyMillis specified."); + } + + TimestampedValue timeSignal = + new TimestampedValue<>(referenceTimeMillis, unixEpochTimeMillis); + NetworkTimeSuggestion networkTimeSuggestion = + new NetworkTimeSuggestion(timeSignal, uncertaintyMillis); + networkTimeSuggestion.addDebugInfo("Command line injection"); + return networkTimeSuggestion; + } + + /** Prints the command line args needed to create a {@link NetworkTimeSuggestion}. */ + public static void printCommandLineOpts(PrintWriter pw) { + pw.printf("%s suggestion options:\n", "Network"); + pw.println(" --reference_time - the elapsed realtime millis when" + + " unix epoch time was read"); + pw.println(" --unix_epoch_time "); + pw.println(" --uncertainty_millis - a positive error bound (+/-)" + + " estimate for unix epoch time"); + pw.println(); + pw.println("See " + NetworkTimeSuggestion.class.getName() + " for more information"); + } +} diff --git a/services/core/java/com/android/server/timedetector/NetworkTimeUpdateService.java b/services/core/java/com/android/server/timedetector/NetworkTimeUpdateService.java index 53893252c94b2..25f3f6190b111 100644 --- a/services/core/java/com/android/server/timedetector/NetworkTimeUpdateService.java +++ b/services/core/java/com/android/server/timedetector/NetworkTimeUpdateService.java @@ -20,8 +20,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlarmManager; import android.app.PendingIntent; -import android.app.timedetector.NetworkTimeSuggestion; -import android.app.timedetector.TimeDetector; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -48,6 +46,7 @@ import android.util.NtpTrustedTime; import android.util.NtpTrustedTime.TimeResult; import com.android.internal.util.DumpUtils; +import com.android.server.LocalServices; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -80,7 +79,7 @@ public class NetworkTimeUpdateService extends Binder { private final Context mContext; private final NtpTrustedTime mTime; private final AlarmManager mAlarmManager; - private final TimeDetector mTimeDetector; + private final TimeDetectorInternal mTimeDetectorInternal; private final ConnectivityManager mCM; private final PendingIntent mPendingPollIntent; private final PowerManager.WakeLock mWakeLock; @@ -112,7 +111,7 @@ public class NetworkTimeUpdateService extends Binder { mContext = context; mTime = NtpTrustedTime.getInstance(context); mAlarmManager = mContext.getSystemService(AlarmManager.class); - mTimeDetector = mContext.getSystemService(TimeDetector.class); + mTimeDetectorInternal = LocalServices.getService(TimeDetectorInternal.class); mCM = mContext.getSystemService(ConnectivityManager.class); Intent pollIntent = new Intent(ACTION_POLL, null); @@ -272,9 +271,10 @@ public class NetworkTimeUpdateService extends Binder { private void makeNetworkTimeSuggestion(TimeResult ntpResult, String debugInfo) { TimestampedValue timeSignal = new TimestampedValue<>( ntpResult.getElapsedRealtimeMillis(), ntpResult.getTimeMillis()); - NetworkTimeSuggestion timeSuggestion = new NetworkTimeSuggestion(timeSignal); + NetworkTimeSuggestion timeSuggestion = + new NetworkTimeSuggestion(timeSignal, ntpResult.getUncertaintyMillis()); timeSuggestion.addDebugInfo(debugInfo); - mTimeDetector.suggestNetworkTime(timeSuggestion); + mTimeDetectorInternal.suggestNetworkTime(timeSuggestion); } /** diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorInternal.java b/services/core/java/com/android/server/timedetector/TimeDetectorInternal.java index 181f5adee55f6..e02032c85385f 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorInternal.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorInternal.java @@ -16,6 +16,8 @@ package com.android.server.timedetector; +import android.annotation.NonNull; + /** * The internal (in-process) system server API for the {@link * com.android.server.timedetector.TimeDetectorService}. @@ -25,4 +27,6 @@ package com.android.server.timedetector; */ public interface TimeDetectorInternal { -} + /** Used to pass new network time suggestions to the time detector. */ + void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSignal); +} \ No newline at end of file diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorInternalImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorInternalImpl.java index 1b47ebb3caaa5..ed5814d14cd74 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorInternalImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorInternalImpl.java @@ -39,4 +39,11 @@ public class TimeDetectorInternalImpl implements TimeDetectorInternal { mHandler = Objects.requireNonNull(handler); mTimeDetectorStrategy = Objects.requireNonNull(timeDetectorStrategy); } + + @Override + public void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSignal) { + Objects.requireNonNull(timeSignal); + + mHandler.post(() -> mTimeDetectorStrategy.suggestNetworkTime(timeSignal)); + } } diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java index 02d3487ac431d..a323031aa7fcb 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java @@ -27,7 +27,6 @@ import android.app.time.TimeConfiguration; import android.app.timedetector.GnssTimeSuggestion; import android.app.timedetector.ITimeDetectorService; import android.app.timedetector.ManualTimeSuggestion; -import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.TelephonyTimeSuggestion; import android.app.timedetector.TimePoint; import android.content.Context; @@ -295,8 +294,7 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub } } - @Override - public void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSignal) { + void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSignal) { enforceSuggestNetworkTimePermission(); Objects.requireNonNull(timeSignal); diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java b/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java index 211ebe4fbb095..d057c90ac099c 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java @@ -32,7 +32,6 @@ import android.app.time.ExternalTimeSuggestion; import android.app.time.TimeConfiguration; 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 android.os.UserHandle; diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java index cec383ce77bed..25488cdfc2e0f 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java @@ -22,7 +22,6 @@ import android.annotation.UserIdInt; 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.TimestampedValue; import android.util.IndentingPrintWriter; diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java index ecbf1f88c1206..c1999fb51a2b9 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java @@ -27,7 +27,6 @@ import android.app.AlarmManager; 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.content.Context; import android.os.Handler; diff --git a/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java b/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java index f4a6ef0bf25c0..8218fa5c4643b 100644 --- a/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java +++ b/services/core/java/com/android/server/timezonedetector/GeolocationTimeZoneSuggestion.java @@ -189,6 +189,10 @@ public final class GeolocationTimeZoneSuggestion { } } + if (zoneIdsString == null) { + throw new IllegalArgumentException("Missing --zone_ids"); + } + long elapsedRealtimeMillis = SystemClock.elapsedRealtime(); List zoneIds = parseZoneIdsArg(zoneIdsString); GeolocationTimeZoneSuggestion suggestion = diff --git a/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java index d016d7e3e748e..a4e689842844b 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java @@ -23,7 +23,6 @@ import android.annotation.UserIdInt; 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.util.IndentingPrintWriter; diff --git a/core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.java b/services/tests/servicestests/src/com/android/server/timedetector/NetworkTimeSuggestionTest.java similarity index 69% rename from core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.java rename to services/tests/servicestests/src/com/android/server/timedetector/NetworkTimeSuggestionTest.java index 0e09dd3e9aab7..f5a37b964508b 100644 --- a/core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/NetworkTimeSuggestionTest.java @@ -14,11 +14,9 @@ * limitations under the License. */ -package android.app.timedetector; +package com.android.server.timedetector; -import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable; -import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable; -import static android.app.timezonedetector.ShellCommandTestSupport.createShellCommandWithArgsAndOptions; +import static com.android.server.timezonedetector.ShellCommandTestSupport.createShellCommandWithArgsAndOptions; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; @@ -32,60 +30,66 @@ public class NetworkTimeSuggestionTest { private static final TimestampedValue ARBITRARY_TIME = new TimestampedValue<>(1111L, 2222L); + private static final int ARBITRARY_UNCERTAINTY_MILLIS = 3333; @Test public void testEquals() { - NetworkTimeSuggestion one = new NetworkTimeSuggestion(ARBITRARY_TIME); + NetworkTimeSuggestion one = new NetworkTimeSuggestion( + ARBITRARY_TIME, ARBITRARY_UNCERTAINTY_MILLIS); assertEquals(one, one); - NetworkTimeSuggestion two = new NetworkTimeSuggestion(ARBITRARY_TIME); + NetworkTimeSuggestion two = + new NetworkTimeSuggestion(ARBITRARY_TIME, ARBITRARY_UNCERTAINTY_MILLIS); assertEquals(one, two); assertEquals(two, one); TimestampedValue differentTime = new TimestampedValue<>( ARBITRARY_TIME.getReferenceTimeMillis() + 1, ARBITRARY_TIME.getValue()); - NetworkTimeSuggestion three = new NetworkTimeSuggestion(differentTime); + NetworkTimeSuggestion three = new NetworkTimeSuggestion( + differentTime, ARBITRARY_UNCERTAINTY_MILLIS); assertNotEquals(one, three); assertNotEquals(three, one); + int differentUncertainty = ARBITRARY_UNCERTAINTY_MILLIS + 1; + NetworkTimeSuggestion four = new NetworkTimeSuggestion( + ARBITRARY_TIME, differentUncertainty); + assertNotEquals(one, four); + assertNotEquals(four, 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()); - } - @Test(expected = IllegalArgumentException.class) public void testParseCommandLineArg_noReferenceTime() { ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( - "--unix_epoch_time 12345"); + "--unix_epoch_time 12345 --uncertainty_millis 111"); NetworkTimeSuggestion.parseCommandLineArg(testShellCommand); } @Test(expected = IllegalArgumentException.class) public void testParseCommandLineArg_noUnixEpochTime() { ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( - "--reference_time 54321"); + "--reference_time 54321 --uncertainty_millis 111"); + NetworkTimeSuggestion.parseCommandLineArg(testShellCommand); + } + + @Test(expected = IllegalArgumentException.class) + public void testParseCommandLineArg_noUncertaintyMillis() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( + "--reference_time 54321 --unix_epoch_time 12345"); NetworkTimeSuggestion.parseCommandLineArg(testShellCommand); } @Test public void testParseCommandLineArg_validSuggestion() { ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( - "--reference_time 54321 --unix_epoch_time 12345"); + "--reference_time 54321 --unix_epoch_time 12345 --uncertainty_millis 111"); TimestampedValue timeSignal = new TimestampedValue<>(54321L, 12345L); - NetworkTimeSuggestion expectedSuggestion = new NetworkTimeSuggestion(timeSignal); + NetworkTimeSuggestion expectedSuggestion = new NetworkTimeSuggestion(timeSignal, 111); NetworkTimeSuggestion actualSuggestion = NetworkTimeSuggestion.parseCommandLineArg(testShellCommand); assertEquals(expectedSuggestion, actualSuggestion); diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java index 702ebeb3a4f92..3a1c702083ed2 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java @@ -40,7 +40,6 @@ import android.app.time.ITimeDetectorListener; import android.app.time.TimeConfiguration; import android.app.timedetector.GnssTimeSuggestion; import android.app.timedetector.ManualTimeSuggestion; -import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.TelephonyTimeSuggestion; import android.app.timedetector.TimePoint; import android.content.Context; @@ -408,7 +407,7 @@ public class TimeDetectorServiceTest { @Test public void testLatestNetworkTime() { NtpTrustedTime.TimeResult latestNetworkTime = - new NtpTrustedTime.TimeResult(1234L, 54321L, 999L); + new NtpTrustedTime.TimeResult(1234L, 54321L, 999); when(mMockNtpTrustedTime.getCachedTimeResult()) .thenReturn(latestNetworkTime); TimePoint expected = new TimePoint(latestNetworkTime.getTimeMillis(), @@ -467,7 +466,7 @@ public class TimeDetectorServiceTest { private static NetworkTimeSuggestion createNetworkTimeSuggestion() { TimestampedValue timeValue = new TimestampedValue<>(100L, 1_000_000L); - return new NetworkTimeSuggestion(timeValue); + return new NetworkTimeSuggestion(timeValue, 123); } private static GnssTimeSuggestion createGnssTimeSuggestion() { diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java index 15a8996aef4c3..be67ff1e15cc2 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java @@ -31,7 +31,6 @@ import android.annotation.UserIdInt; 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.TimestampedValue; @@ -1573,7 +1572,7 @@ public class TimeDetectorStrategyImplTest { new TimestampedValue<>( mFakeEnvironment.peekElapsedRealtimeMillis(), suggestedTime.toEpochMilli()); - return new NetworkTimeSuggestion(unixEpochTime); + return new NetworkTimeSuggestion(unixEpochTime, 123); } /** diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java index 79f8b0e8d4c8d..0f667b3a690b2 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java @@ -16,8 +16,13 @@ package com.android.server.timezonedetector; +import static com.android.server.timezonedetector.ShellCommandTestSupport.createShellCommandWithArgsAndOptions; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; + +import android.os.ShellCommand; import org.junit.Test; @@ -66,4 +71,40 @@ public class GeolocationTimeZoneSuggestionTest { assertNotEquals(certain1v1, certain3); assertNotEquals(certain3, certain1v1); } + + @Test(expected = IllegalArgumentException.class) + public void testParseCommandLineArg_noZoneIdsArg() { + ShellCommand testShellCommand = + createShellCommandWithArgsAndOptions(Collections.emptyList()); + GeolocationTimeZoneSuggestion.parseCommandLineArg(testShellCommand); + } + + @Test + public void testParseCommandLineArg_zoneIdsUncertain() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( + "--zone_ids UNCERTAIN"); + assertNull(GeolocationTimeZoneSuggestion.parseCommandLineArg(testShellCommand) + .getZoneIds()); + } + + @Test + public void testParseCommandLineArg_zoneIdsEmpty() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions("--zone_ids EMPTY"); + assertEquals(Collections.emptyList(), + GeolocationTimeZoneSuggestion.parseCommandLineArg(testShellCommand).getZoneIds()); + } + + @Test + public void testParseCommandLineArg_zoneIdsPresent() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions( + "--zone_ids Europe/London,Europe/Paris"); + assertEquals(Arrays.asList("Europe/London", "Europe/Paris"), + GeolocationTimeZoneSuggestion.parseCommandLineArg(testShellCommand).getZoneIds()); + } + + @Test(expected = IllegalArgumentException.class) + public void testParseCommandLineArg_unknownArgument() { + ShellCommand testShellCommand = createShellCommandWithArgsAndOptions("--bad_arg 0"); + GeolocationTimeZoneSuggestion.parseCommandLineArg(testShellCommand); + } } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ShellCommandTestSupport.java b/services/tests/servicestests/src/com/android/server/timezonedetector/ShellCommandTestSupport.java new file mode 100644 index 0000000000000..b96c82fc396c0 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/ShellCommandTestSupport.java @@ -0,0 +1,65 @@ +/* + * 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 com.android.server.timezonedetector; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.os.ShellCommand; + +import org.mockito.stubbing.Answer; + +import java.util.Arrays; +import java.util.List; + +/** Utility methods related to {@link ShellCommand} objects used in several tests. */ +public final class ShellCommandTestSupport { + private ShellCommandTestSupport() {} + + public static ShellCommand createShellCommandWithArgsAndOptions(String argsWithSpaces) { + return createShellCommandWithArgsAndOptions(Arrays.asList(argsWithSpaces.split(" "))); + } + + public static ShellCommand createShellCommandWithArgsAndOptions(List args) { + ShellCommand command = mock(ShellCommand.class); + class ArgProvider { + private int mCount; + + String getNext() { + if (mCount >= args.size()) { + return null; + } + return args.get(mCount++); + } + + String getNextRequired() { + String next = getNext(); + if (next == null) { + throw new IllegalArgumentException("No next"); + } + return next; + } + } + ArgProvider argProvider = new ArgProvider(); + when(command.getNextArg()).thenAnswer( + (Answer) invocation -> argProvider.getNext()); + when(command.getNextOption()).thenAnswer( + (Answer) invocation -> argProvider.getNext()); + when(command.getNextArgRequired()).thenAnswer( + (Answer) invocation -> argProvider.getNextRequired()); + return command; + } +}