summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl1
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java14
-rw-r--r--core/java/android/app/timedetector/ITimeDetectorService.aidl5
-rw-r--r--core/java/android/app/timedetector/TimePoint.aidl19
-rw-r--r--core/java/android/app/timedetector/TimePoint.java103
-rw-r--r--core/java/android/os/SystemClock.java65
-rw-r--r--core/java/android/util/NtpTrustedTime.java17
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorService.java24
-rw-r--r--services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java25
9 files changed, 227 insertions, 46 deletions
diff --git a/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl b/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl
index 9d11ca470397..25caf4b695bb 100644
--- a/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl
+++ b/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl
@@ -40,7 +40,6 @@ interface IAlarmManager {
long getNextWakeFromIdleTime();
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
AlarmManager.AlarmClockInfo getNextAlarmClock(int userId);
- long currentNetworkTimeMillis();
boolean canScheduleExactAlarms(String packageName);
boolean hasScheduleExactAlarm(String packageName, int userId);
int getConfigVersion();
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 296c89c66e4c..c053b2ed5adb 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -90,7 +90,6 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.os.ParcelableException;
import android.os.PowerExemptionManager;
import android.os.PowerManager;
import android.os.Process;
@@ -116,7 +115,6 @@ import android.util.EventLog;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.LongArrayQueue;
-import android.util.NtpTrustedTime;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -161,7 +159,6 @@ import libcore.util.EmptyArray;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
-import java.time.DateTimeException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -3008,17 +3005,6 @@ public class AlarmManagerService extends SystemService {
}
@Override
- public long currentNetworkTimeMillis() {
- final NtpTrustedTime time = NtpTrustedTime.getInstance(getContext());
- NtpTrustedTime.TimeResult ntpResult = time.getCachedTimeResult();
- if (ntpResult != null) {
- return ntpResult.currentTimeMillis();
- } else {
- throw new ParcelableException(new DateTimeException("Missing NTP fix"));
- }
- }
-
- @Override
public int getConfigVersion() {
getContext().enforceCallingOrSelfPermission(Manifest.permission.DUMP,
"getConfigVersion");
diff --git a/core/java/android/app/timedetector/ITimeDetectorService.aidl b/core/java/android/app/timedetector/ITimeDetectorService.aidl
index fc7afb483142..b441359b1614 100644
--- a/core/java/android/app/timedetector/ITimeDetectorService.aidl
+++ b/core/java/android/app/timedetector/ITimeDetectorService.aidl
@@ -24,6 +24,7 @@ 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;
/**
* System private API to communicate with time detector service.
@@ -45,9 +46,11 @@ interface ITimeDetectorService {
boolean updateConfiguration(in TimeConfiguration timeConfiguration);
- void suggestExternalTime( in ExternalTimeSuggestion timeSuggestion);
+ 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/TimePoint.aidl b/core/java/android/app/timedetector/TimePoint.aidl
new file mode 100644
index 000000000000..80d4bc1c34b5
--- /dev/null
+++ b/core/java/android/app/timedetector/TimePoint.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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;
+
+parcelable TimePoint;
diff --git a/core/java/android/app/timedetector/TimePoint.java b/core/java/android/app/timedetector/TimePoint.java
new file mode 100644
index 000000000000..aa079a9b1159
--- /dev/null
+++ b/core/java/android/app/timedetector/TimePoint.java
@@ -0,0 +1,103 @@
+/*
+ * 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.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Data class for passing a Unix epoch time anchored to the elapsed realtime clock.
+ *
+ * @hide
+ */
+public final class TimePoint implements Parcelable {
+
+ private final long mUnixEpochTimeMillis;
+ private final long mElapsedRealtimeMillis;
+
+ public TimePoint(long unixEpochTimeMillis, long elapsedRealtimeMillis) {
+ mUnixEpochTimeMillis = unixEpochTimeMillis;
+ mElapsedRealtimeMillis = elapsedRealtimeMillis;
+ }
+
+ /**
+ * The current Unix epoch time, according to the external source.
+ */
+ public long getUnixEpochTimeMillis() {
+ return mUnixEpochTimeMillis;
+ }
+
+ /**
+ * The elapsed millis since boot when {@link #getUnixEpochTimeMillis} was computed.
+ */
+ public long getElapsedRealtimeMillis() {
+ return mElapsedRealtimeMillis;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(mUnixEpochTimeMillis);
+ out.writeLong(mElapsedRealtimeMillis);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof TimePoint)) {
+ return false;
+ }
+ TimePoint timePoint = (TimePoint) o;
+ return mUnixEpochTimeMillis == timePoint.mUnixEpochTimeMillis
+ && mElapsedRealtimeMillis == timePoint.mElapsedRealtimeMillis;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mUnixEpochTimeMillis, mElapsedRealtimeMillis);
+ }
+
+ @Override
+ public String toString() {
+ return "TimePoint{"
+ + "mUnixEpochTimeMillis=" + mUnixEpochTimeMillis
+ + ", mElapsedRealtimeMillis=" + mElapsedRealtimeMillis
+ + '}';
+ }
+
+ public static final @NonNull Creator<TimePoint> CREATOR =
+ new Creator<TimePoint>() {
+ public TimePoint createFromParcel(Parcel in) {
+ long unixEpochTime = in.readLong();
+ long elapsedRealtimeMillis = in.readLong();
+ return new TimePoint(unixEpochTime, elapsedRealtimeMillis);
+ }
+
+ public TimePoint[] newArray(int size) {
+ return new TimePoint[size];
+ }
+ };
+}
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index 7379443877b7..ecea054db216 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -18,6 +18,8 @@ package android.os;
import android.annotation.NonNull;
import android.app.IAlarmManager;
+import android.app.timedetector.ITimeDetectorService;
+import android.app.timedetector.TimePoint;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.location.ILocationManager;
@@ -170,6 +172,14 @@ public final class SystemClock {
return false;
}
+ private static IAlarmManager getIAlarmManager() {
+ if (sIAlarmManager == null) {
+ sIAlarmManager = IAlarmManager.Stub
+ .asInterface(ServiceManager.getService(Context.ALARM_SERVICE));
+ }
+ return sIAlarmManager;
+ }
+
/**
* Returns milliseconds since boot, not counting time spent in deep sleep.
*
@@ -269,56 +279,71 @@ public final class SystemClock {
* <p>
* While the time returned by {@link System#currentTimeMillis()} can be
* adjusted by the user, the time returned by this method cannot be adjusted
- * by the user. Note that synchronization may occur using an insecure
- * network protocol, so the returned time should not be used for security
- * purposes.
+ * by the user.
* <p>
* This performs no blocking network operations and returns values based on
* a recent successful synchronization event; it will either return a valid
* time or throw.
+ * <p>
+ * Note that synchronization may occur using an insecure network protocol,
+ * so the returned time should not be used for security purposes.
+ * The device may resynchronize with the same or different network source
+ * at any time. Due to network delays, variations between servers, or local
+ * (client side) clock drift, the accuracy of the returned times cannot be
+ * guaranteed. In extreme cases, consecutive calls to {@link
+ * #currentNetworkTimeMillis()} could return times that are out of order.
*
- * @throws DateTimeException when no accurate network time can be provided.
+ * @throws DateTimeException when no network time can be provided.
* @hide
*/
public static long currentNetworkTimeMillis() {
- final IAlarmManager mgr = getIAlarmManager();
- if (mgr != null) {
+ ITimeDetectorService timeDetectorService = ITimeDetectorService.Stub
+ .asInterface(ServiceManager.getService(Context.TIME_DETECTOR_SERVICE));
+ if (timeDetectorService != null) {
+ TimePoint time;
try {
- return mgr.currentNetworkTimeMillis();
+ time = timeDetectorService.latestNetworkTime();
} catch (ParcelableException e) {
e.maybeRethrow(DateTimeException.class);
throw new RuntimeException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+
+ if (time == null) {
+ // This is not expected.
+ throw new DateTimeException("Network based time is not available.");
+ }
+ long currentMillis = elapsedRealtime();
+ long deltaMs = currentMillis - time.getElapsedRealtimeMillis();
+ return time.getUnixEpochTimeMillis() + deltaMs;
} else {
throw new RuntimeException(new DeadSystemException());
}
}
- private static IAlarmManager getIAlarmManager() {
- if (sIAlarmManager == null) {
- sIAlarmManager = IAlarmManager.Stub
- .asInterface(ServiceManager.getService(Context.ALARM_SERVICE));
- }
- return sIAlarmManager;
- }
-
- /**
+ /**
* Returns a {@link Clock} that starts at January 1, 1970 00:00:00.0 UTC,
* synchronized using a remote network source outside the device.
* <p>
* While the time returned by {@link System#currentTimeMillis()} can be
* adjusted by the user, the time returned by this method cannot be adjusted
- * by the user. Note that synchronization may occur using an insecure
- * network protocol, so the returned time should not be used for security
- * purposes.
+ * by the user.
* <p>
* This performs no blocking network operations and returns values based on
* a recent successful synchronization event; it will either return a valid
* time or throw.
+ * <p>
+ * Note that synchronization may occur using an insecure network protocol,
+ * so the returned time should not be used for security purposes.
+ * The device may resynchronize with the same or different network source
+ * at any time. Due to network delays, variations between servers, or local
+ * (client side) clock drift, the accuracy of the returned times cannot be
+ * guaranteed. In extreme cases, consecutive calls to {@link
+ * Clock#millis()} on the returned {@link Clock}could return times that are
+ * out of order.
*
- * @throws DateTimeException when no accurate network time can be provided.
+ * @throws DateTimeException when no network time can be provided.
*/
public static @NonNull Clock currentNetworkTimeClock() {
return new SimpleClock(ZoneOffset.UTC) {
diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java
index 4e7b3a51d758..4a3f772d3bc6 100644
--- a/core/java/android/util/NtpTrustedTime.java
+++ b/core/java/android/util/NtpTrustedTime.java
@@ -55,18 +55,19 @@ public class NtpTrustedTime implements TrustedTime {
* @hide
*/
public static class TimeResult {
- private final long mTimeMillis;
+ private final long mUnixEpochTimeMillis;
private final long mElapsedRealtimeMillis;
private final long mCertaintyMillis;
- public TimeResult(long timeMillis, long elapsedRealtimeMillis, long certaintyMillis) {
- mTimeMillis = timeMillis;
+ public TimeResult(
+ long unixEpochTimeMillis, long elapsedRealtimeMillis, long certaintyMillis) {
+ mUnixEpochTimeMillis = unixEpochTimeMillis;
mElapsedRealtimeMillis = elapsedRealtimeMillis;
mCertaintyMillis = certaintyMillis;
}
public long getTimeMillis() {
- return mTimeMillis;
+ return mUnixEpochTimeMillis;
}
public long getElapsedRealtimeMillis() {
@@ -77,9 +78,11 @@ public class NtpTrustedTime implements TrustedTime {
return mCertaintyMillis;
}
- /** Calculates and returns the current time accounting for the age of this result. */
+ /**
+ * Calculates and returns the current Unix epoch time accounting for the age of this result.
+ */
public long currentTimeMillis() {
- return mTimeMillis + getAgeMillis();
+ return mUnixEpochTimeMillis + getAgeMillis();
}
/** Calculates and returns the age of this result. */
@@ -99,7 +102,7 @@ public class NtpTrustedTime implements TrustedTime {
@Override
public String toString() {
return "TimeResult{"
- + "mTimeMillis=" + Instant.ofEpochMilli(mTimeMillis)
+ + "mUnixEpochTimeMillis=" + Instant.ofEpochMilli(mUnixEpochTimeMillis)
+ ", mElapsedRealtimeMillis=" + Duration.ofMillis(mElapsedRealtimeMillis)
+ ", mCertaintyMillis=" + mCertaintyMillis
+ '}';
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 105cd78f2a07..b3ec27b4f0f2 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -29,15 +29,18 @@ 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;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
+import android.os.ParcelableException;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
+import android.util.NtpTrustedTime;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -49,6 +52,7 @@ import com.android.server.timezonedetector.CallerIdentityInjector;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.time.DateTimeException;
import java.util.Objects;
/**
@@ -93,6 +97,7 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub
@NonNull private final CallerIdentityInjector mCallerIdentityInjector;
@NonNull private final ServiceConfigAccessor mServiceConfigAccessor;
@NonNull private final TimeDetectorStrategy mTimeDetectorStrategy;
+ @NonNull private final NtpTrustedTime mNtpTrustedTime;
/**
* Holds the listeners. The key is the {@link IBinder} associated with the listener, the value
@@ -107,19 +112,21 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub
@NonNull ServiceConfigAccessor serviceConfigAccessor,
@NonNull TimeDetectorStrategy timeDetectorStrategy) {
this(context, handler, serviceConfigAccessor, timeDetectorStrategy,
- CallerIdentityInjector.REAL);
+ CallerIdentityInjector.REAL, NtpTrustedTime.getInstance(context));
}
@VisibleForTesting
public TimeDetectorService(@NonNull Context context, @NonNull Handler handler,
@NonNull ServiceConfigAccessor serviceConfigAccessor,
@NonNull TimeDetectorStrategy timeDetectorStrategy,
- @NonNull CallerIdentityInjector callerIdentityInjector) {
+ @NonNull CallerIdentityInjector callerIdentityInjector,
+ @NonNull NtpTrustedTime ntpTrustedTime) {
mContext = Objects.requireNonNull(context);
mHandler = Objects.requireNonNull(handler);
mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor);
mTimeDetectorStrategy = Objects.requireNonNull(timeDetectorStrategy);
mCallerIdentityInjector = Objects.requireNonNull(callerIdentityInjector);
+ mNtpTrustedTime = Objects.requireNonNull(ntpTrustedTime);
// Wire up a change listener so that ITimeZoneDetectorListeners can be notified when
// the configuration changes for any reason.
@@ -308,6 +315,19 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub
}
@Override
+ public TimePoint latestNetworkTime() {
+ // TODO(b/222295093): Return the latest network time from mTimeDetectorStrategy once we can
+ // be sure that all uses of NtpTrustedTime results in a suggestion being made to the time
+ // detector. mNtpTrustedTime can be removed once this happens.
+ NtpTrustedTime.TimeResult ntpResult = mNtpTrustedTime.getCachedTimeResult();
+ if (ntpResult != null) {
+ return new TimePoint(ntpResult.getTimeMillis(), ntpResult.getElapsedRealtimeMillis());
+ } else {
+ throw new ParcelableException(new DateTimeException("Missing network time fix"));
+ }
+ }
+
+ @Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
@Nullable String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
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 e9617e9d973a..2570158f50b2 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -19,6 +19,7 @@ package com.android.server.timedetector;
import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_NETWORK;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
@@ -41,12 +42,15 @@ 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;
import android.content.pm.PackageManager;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.ParcelableException;
import android.os.TimestampedValue;
import android.util.IndentingPrintWriter;
+import android.util.NtpTrustedTime;
import androidx.test.runner.AndroidJUnit4;
@@ -77,6 +81,7 @@ public class TimeDetectorServiceTest {
private TestHandler mTestHandler;
private TestCallerIdentityInjector mTestCallerIdentityInjector;
private FakeServiceConfigAccessor mFakeServiceConfigAccessor;
+ private NtpTrustedTime mMockNtpTrustedTime;
private StubbedTimeDetectorStrategy mStubbedTimeDetectorStrategy;
@@ -94,10 +99,11 @@ public class TimeDetectorServiceTest {
mStubbedTimeDetectorStrategy = new StubbedTimeDetectorStrategy();
mFakeServiceConfigAccessor = new FakeServiceConfigAccessor();
+ mMockNtpTrustedTime = mock(NtpTrustedTime.class);
mTimeDetectorService = new TimeDetectorService(
mMockContext, mTestHandler, mFakeServiceConfigAccessor,
- mStubbedTimeDetectorStrategy, mTestCallerIdentityInjector);
+ mStubbedTimeDetectorStrategy, mTestCallerIdentityInjector, mMockNtpTrustedTime);
}
@After
@@ -400,6 +406,23 @@ public class TimeDetectorServiceTest {
}
@Test
+ public void testLatestNetworkTime() {
+ NtpTrustedTime.TimeResult latestNetworkTime =
+ new NtpTrustedTime.TimeResult(1234L, 54321L, 999L);
+ when(mMockNtpTrustedTime.getCachedTimeResult())
+ .thenReturn(latestNetworkTime);
+ TimePoint expected = new TimePoint(latestNetworkTime.getTimeMillis(),
+ latestNetworkTime.getElapsedRealtimeMillis());
+ assertEquals(expected, mTimeDetectorService.latestNetworkTime());
+ }
+
+ @Test
+ public void testLatestNetworkTime_noTimeAvailable() {
+ when(mMockNtpTrustedTime.getCachedTimeResult()).thenReturn(null);
+ assertThrows(ParcelableException.class, () -> mTimeDetectorService.latestNetworkTime());
+ }
+
+ @Test
public void testDump() {
when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP))
.thenReturn(PackageManager.PERMISSION_GRANTED);