From 28f3fe363b5f6eac42ebfc740f25ad697c32f4bf Mon Sep 17 00:00:00 2001 From: Neil Fuller Date: Tue, 28 Jun 2022 14:55:11 +0100 Subject: [PATCH] Switch NetworkTimeUpdateService to internal API Switch NetworkTimeUpdateService to using TimeDetectorInternal instead of the binder API. The NetworkTimeUpdateService runs in the system server, so it does not have to use binder to communicate with the time_detector. Also update NetworkTimeSuggestion to include uncertainty so that it contains all the information needed for future usecases associated with GnssLocationProvider. Bug: 219693030 Bug: 222295093 Test: atest services/tests/servicestests/src/com/android/server/timedetector/ Test: atest services/tests/servicestests/src/com/android/server/timezonedetector/ Test: atest core/tests/coretests/src/android/app/timedetector/ Change-Id: I4665da76429bc333b34769fc362221581472690e --- .../timedetector/ITimeDetectorService.aidl | 2 - .../timedetector/NetworkTimeSuggestion.aidl | 19 -- .../timedetector/NetworkTimeSuggestion.java | 124 ------------ .../app/timedetector/TimeDetector.java | 8 - .../app/timedetector/TimeDetectorImpl.java | 12 -- .../timedetector/TimeSuggestionHelper.java | 3 +- core/java/android/util/NtpTrustedTime.java | 33 +++- .../server/location/gnss/NtpTimeHelper.java | 20 +- .../timedetector/NetworkTimeSuggestion.java | 182 ++++++++++++++++++ .../NetworkTimeUpdateService.java | 12 +- .../timedetector/TimeDetectorInternal.java | 6 +- .../TimeDetectorInternalImpl.java | 7 + .../timedetector/TimeDetectorService.java | 4 +- .../TimeDetectorShellCommand.java | 1 - .../timedetector/TimeDetectorStrategy.java | 1 - .../TimeDetectorStrategyImpl.java | 1 - .../GeolocationTimeZoneSuggestion.java | 4 + .../FakeTimeDetectorStrategy.java | 1 - .../NetworkTimeSuggestionTest.java | 48 ++--- .../timedetector/TimeDetectorServiceTest.java | 5 +- .../TimeDetectorStrategyImplTest.java | 3 +- .../GeolocationTimeZoneSuggestionTest.java | 41 ++++ .../ShellCommandTestSupport.java | 65 +++++++ 23 files changed, 374 insertions(+), 228 deletions(-) delete mode 100644 core/java/android/app/timedetector/NetworkTimeSuggestion.aidl delete mode 100644 core/java/android/app/timedetector/NetworkTimeSuggestion.java create mode 100644 services/core/java/com/android/server/timedetector/NetworkTimeSuggestion.java rename {core/tests/coretests/src/android/app => services/tests/servicestests/src/com/android/server}/timedetector/NetworkTimeSuggestionTest.java (69%) create mode 100644 services/tests/servicestests/src/com/android/server/timezonedetector/ShellCommandTestSupport.java 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; + } +}