diff options
| author | 2022-09-16 17:22:56 +0100 | |
|---|---|---|
| committer | 2022-10-03 14:52:44 +0100 | |
| commit | c4ba2d9ce2d5cb4bf98e652ec3803068dcdd31d6 (patch) | |
| tree | 08c99579758102c80d842bf97d62c5b176df08f6 | |
| parent | c18a5de5e233c00c0ef0321793f2b99c58c0a0ca (diff) | |
Introduce system clock time confidence
Add metadata for system clock time that indicates confidence in the
time. Any explicit action to set the time will set the confidence high,
but this information is (deliberately) lost during a power cycle as the
clock is (sometimes) volatile.
This confidence will be used initially to support setup wizard and other
initialization flows that benefit from knowing whether the time needs to
be checked by the user. This lays the groundwork for future
improvements.
As part of this change, bug report logs can now contain information
about how the device's time was set by things other than the time
detector. This will help with debugging.
Bug: 236612872
Test: atest services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
Test: atest services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
Change-Id: I1104e64778f57ef3d1716e9aba1736c4fd001ef7
14 files changed, 899 insertions, 271 deletions
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 c0f45d6fef1e..045856547a4e 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -39,6 +39,7 @@ import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROU import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED; import static android.os.UserHandle.USER_SYSTEM; +import static com.android.server.SystemClockTime.TIME_CONFIDENCE_HIGH; import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH; import static com.android.server.SystemTimeZone.getTimeZoneId; import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX; @@ -58,6 +59,8 @@ import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_R import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_UNDEFINED; import android.Manifest; +import android.annotation.CurrentTimeMillisLong; +import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.Activity; @@ -87,7 +90,6 @@ import android.os.BatteryManager; import android.os.Binder; import android.os.Build; import android.os.Bundle; -import android.os.Environment; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -101,7 +103,6 @@ import android.os.ServiceManager; import android.os.ShellCallback; import android.os.ShellCommand; import android.os.SystemClock; -import android.os.SystemProperties; import android.os.ThreadLocalWorkSource; import android.os.Trace; import android.os.UserHandle; @@ -145,6 +146,8 @@ import com.android.server.DeviceIdleInternal; import com.android.server.EventLogTags; import com.android.server.JobSchedulerBackgroundThread; import com.android.server.LocalServices; +import com.android.server.SystemClockTime; +import com.android.server.SystemClockTime.TimeConfidence; import com.android.server.SystemService; import com.android.server.SystemServiceManager; import com.android.server.SystemTimeZone; @@ -1414,7 +1417,7 @@ public class AlarmManagerService extends SystemService { private long convertToElapsed(long when, int type) { if (isRtc(type)) { - when -= mInjector.getCurrentTimeMillis() - mInjector.getElapsedRealtime(); + when -= mInjector.getCurrentTimeMillis() - mInjector.getElapsedRealtimeMillis(); } return when; } @@ -1574,7 +1577,7 @@ public class AlarmManagerService extends SystemService { alarmsToDeliver = alarmsForUid; mPendingBackgroundAlarms.remove(uid); } - deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, mInjector.getElapsedRealtime()); + deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, mInjector.getElapsedRealtimeMillis()); } /** @@ -1591,7 +1594,8 @@ public class AlarmManagerService extends SystemService { mPendingBackgroundAlarms, alarmsToDeliver, this::isBackgroundRestricted); if (alarmsToDeliver.size() > 0) { - deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, mInjector.getElapsedRealtime()); + deliverPendingBackgroundAlarmsLocked( + alarmsToDeliver, mInjector.getElapsedRealtimeMillis()); } } @@ -1905,17 +1909,8 @@ public class AlarmManagerService extends SystemService { mInjector.syncKernelTimeZoneOffset(); } - // Ensure that we're booting with a halfway sensible current time. Use the - // most recent of Build.TIME, the root file system's timestamp, and the - // value of the ro.build.date.utc system property (which is in seconds). - final long systemBuildTime = Long.max( - 1000L * SystemProperties.getLong("ro.build.date.utc", -1L), - Long.max(Environment.getRootDirectory().lastModified(), Build.TIME)); - if (mInjector.getCurrentTimeMillis() < systemBuildTime) { - Slog.i(TAG, "Current time only " + mInjector.getCurrentTimeMillis() - + ", advancing to build time " + systemBuildTime); - mInjector.setKernelTime(systemBuildTime); - } + // Ensure that we're booting with a halfway sensible current time. + mInjector.initializeTimeIfRequired(); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); // Determine SysUI's uid @@ -2128,21 +2123,18 @@ public class AlarmManagerService extends SystemService { } } - boolean setTimeImpl(long millis) { - if (!mInjector.isAlarmDriverPresent()) { - Slog.w(TAG, "Not setting time since no alarm driver is available."); - return false; - } - + boolean setTimeImpl( + @CurrentTimeMillisLong long newSystemClockTimeMillis, @TimeConfidence int confidence, + @NonNull String logMsg) { synchronized (mLock) { - final long currentTimeMillis = mInjector.getCurrentTimeMillis(); - mInjector.setKernelTime(millis); + final long oldSystemClockTimeMillis = mInjector.getCurrentTimeMillis(); + mInjector.setCurrentTimeMillis(newSystemClockTimeMillis, confidence, logMsg); if (KERNEL_TIME_ZONE_SYNC_ENABLED) { // Changing the time may cross a DST transition; sync the kernel offset if needed. final TimeZone timeZone = TimeZone.getTimeZone(SystemTimeZone.getTimeZoneId()); - final int currentTzOffset = timeZone.getOffset(currentTimeMillis); - final int newTzOffset = timeZone.getOffset(millis); + final int currentTzOffset = timeZone.getOffset(oldSystemClockTimeMillis); + final int newTzOffset = timeZone.getOffset(newSystemClockTimeMillis); if (currentTzOffset != newTzOffset) { Slog.i(TAG, "Timezone offset has changed, updating kernel timezone"); mInjector.setKernelTimeZoneOffset(newTzOffset); @@ -2255,7 +2247,7 @@ public class AlarmManagerService extends SystemService { triggerAtTime = 0; } - final long nowElapsed = mInjector.getElapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); final long nominalTrigger = convertToElapsed(triggerAtTime, type); // Try to prevent spamming by making sure apps aren't firing alarms in the immediate future final long minTrigger = nowElapsed @@ -2378,7 +2370,7 @@ public class AlarmManagerService extends SystemService { // No need to fuzz as this is already earlier than the coming wake-from-idle. return changedBeforeFuzz; } - final long nowElapsed = mInjector.getElapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); final long futurity = upcomingWakeFromIdle - nowElapsed; if (futurity <= mConstants.MIN_DEVICE_IDLE_FUZZ) { @@ -2400,7 +2392,7 @@ public class AlarmManagerService extends SystemService { * @return {@code true} if the alarm delivery time was updated. */ private boolean adjustDeliveryTimeBasedOnBatterySaver(Alarm alarm) { - final long nowElapsed = mInjector.getElapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); if (isExemptFromBatterySaver(alarm)) { return false; } @@ -2467,7 +2459,7 @@ public class AlarmManagerService extends SystemService { * @return {@code true} if the alarm delivery time was updated. */ private boolean adjustDeliveryTimeBasedOnDeviceIdle(Alarm alarm) { - final long nowElapsed = mInjector.getElapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); if (mPendingIdleUntil == null || mPendingIdleUntil == alarm) { return alarm.setPolicyElapsed(DEVICE_IDLE_POLICY_INDEX, nowElapsed); } @@ -2522,7 +2514,7 @@ public class AlarmManagerService extends SystemService { * adjustments made in this call. */ private boolean adjustDeliveryTimeBasedOnBucketLocked(Alarm alarm) { - final long nowElapsed = mInjector.getElapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); if (mConstants.USE_TARE_POLICY || isExemptFromAppStandby(alarm) || mAppStandbyParole) { return alarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, nowElapsed); } @@ -2582,7 +2574,7 @@ public class AlarmManagerService extends SystemService { * adjustments made in this call. */ private boolean adjustDeliveryTimeBasedOnTareLocked(Alarm alarm) { - final long nowElapsed = mInjector.getElapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); if (!mConstants.USE_TARE_POLICY || isExemptFromTare(alarm) || hasEnoughWealthLocked(alarm)) { return alarm.setPolicyElapsed(TARE_POLICY_INDEX, nowElapsed); @@ -2638,7 +2630,7 @@ public class AlarmManagerService extends SystemService { ent.pkg = a.sourcePackage; ent.tag = a.statsTag; ent.op = "START IDLE"; - ent.elapsedRealtime = mInjector.getElapsedRealtime(); + ent.elapsedRealtime = mInjector.getElapsedRealtimeMillis(); ent.argRealtime = a.getWhenElapsed(); mAllowWhileIdleDispatches.add(ent); } @@ -2714,6 +2706,13 @@ public class AlarmManagerService extends SystemService { } @Override + public void setTime( + @CurrentTimeMillisLong long unixEpochTimeMillis, int confidence, + String logMsg) { + setTimeImpl(unixEpochTimeMillis, confidence, logMsg); + } + + @Override public void registerInFlightListener(InFlightListener callback) { synchronized (mLock) { mInFlightListeners.add(callback); @@ -2982,12 +2981,16 @@ public class AlarmManagerService extends SystemService { } @Override - public boolean setTime(long millis) { + public boolean setTime(@CurrentTimeMillisLong long millis) { getContext().enforceCallingOrSelfPermission( "android.permission.SET_TIME", "setTime"); - return setTimeImpl(millis); + // The public API (and the shell command that also uses this method) have no concept + // of confidence, but since the time should come either from apps working on behalf of + // the user or a developer, confidence is assumed "high". + final int timeConfidence = TIME_CONFIDENCE_HIGH; + return setTimeImpl(millis, timeConfidence, "AlarmManager.setTime() called"); } @Override @@ -3137,7 +3140,7 @@ public class AlarmManagerService extends SystemService { pw.println(); } - final long nowELAPSED = mInjector.getElapsedRealtime(); + final long nowELAPSED = mInjector.getElapsedRealtimeMillis(); final long nowUPTIME = SystemClock.uptimeMillis(); final long nowRTC = mInjector.getCurrentTimeMillis(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); @@ -3613,7 +3616,7 @@ public class AlarmManagerService extends SystemService { synchronized (mLock) { final long nowRTC = mInjector.getCurrentTimeMillis(); - final long nowElapsed = mInjector.getElapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); proto.write(AlarmManagerServiceDumpProto.CURRENT_TIME, nowRTC); proto.write(AlarmManagerServiceDumpProto.ELAPSED_REALTIME, nowElapsed); proto.write(AlarmManagerServiceDumpProto.LAST_TIME_CHANGE_CLOCK_TIME, @@ -3951,7 +3954,7 @@ public class AlarmManagerService extends SystemService { void rescheduleKernelAlarmsLocked() { // Schedule the next upcoming wakeup alarm. If there is a deliverable batch // prior to that which contains no wakeups, we schedule that as well. - final long nowElapsed = mInjector.getElapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); long nextNonWakeup = 0; if (mAlarmStore.size() > 0) { final long firstWakeup = mAlarmStore.getNextWakeupDeliveryTime(); @@ -4064,7 +4067,7 @@ public class AlarmManagerService extends SystemService { @GuardedBy("mLock") private void removeAlarmsInternalLocked(Predicate<Alarm> whichAlarms, int reason) { final long nowRtc = mInjector.getCurrentTimeMillis(); - final long nowElapsed = mInjector.getElapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); final ArrayList<Alarm> removedAlarms = mAlarmStore.remove(whichAlarms); final boolean removedFromStore = !removedAlarms.isEmpty(); @@ -4203,7 +4206,7 @@ public class AlarmManagerService extends SystemService { void interactiveStateChangedLocked(boolean interactive) { if (mInteractive != interactive) { mInteractive = interactive; - final long nowELAPSED = mInjector.getElapsedRealtime(); + final long nowELAPSED = mInjector.getElapsedRealtimeMillis(); if (interactive) { if (mPendingNonWakeupAlarms.size() > 0) { final long thisDelayTime = nowELAPSED - mStartCurrentDelayTime; @@ -4305,7 +4308,6 @@ public class AlarmManagerService extends SystemService { private static native void close(long nativeData); private static native int set(long nativeData, int type, long seconds, long nanoseconds); private static native int waitForAlarm(long nativeData); - private static native int setKernelTime(long nativeData, long millis); /* * b/246256335: The @Keep ensures that the native definition is kept even when the optimizer can @@ -4352,7 +4354,7 @@ public class AlarmManagerService extends SystemService { ent.pkg = alarm.sourcePackage; ent.tag = alarm.statsTag; ent.op = "END IDLE"; - ent.elapsedRealtime = mInjector.getElapsedRealtime(); + ent.elapsedRealtime = mInjector.getElapsedRealtimeMillis(); ent.argRealtime = alarm.getWhenElapsed(); mAllowWhileIdleDispatches.add(ent); } @@ -4597,20 +4599,27 @@ public class AlarmManagerService extends SystemService { setKernelTimeZoneOffset(utcOffsetMillis); } - void setKernelTime(long millis) { - if (mNativeData != 0) { - AlarmManagerService.setKernelTime(mNativeData, millis); - } + void initializeTimeIfRequired() { + SystemClockTime.initializeIfRequired(); + } + + void setCurrentTimeMillis( + @CurrentTimeMillisLong long unixEpochMillis, + @TimeConfidence int confidence, + @NonNull String logMsg) { + SystemClockTime.setTimeAndConfidence(unixEpochMillis, confidence, logMsg); } void close() { AlarmManagerService.close(mNativeData); } - long getElapsedRealtime() { + @ElapsedRealtimeLong + long getElapsedRealtimeMillis() { return SystemClock.elapsedRealtime(); } + @CurrentTimeMillisLong long getCurrentTimeMillis() { return System.currentTimeMillis(); } @@ -4660,7 +4669,7 @@ public class AlarmManagerService extends SystemService { while (true) { int result = mInjector.waitForAlarm(); final long nowRTC = mInjector.getCurrentTimeMillis(); - final long nowELAPSED = mInjector.getElapsedRealtime(); + final long nowELAPSED = mInjector.getElapsedRealtimeMillis(); synchronized (mLock) { mLastWakeup = nowELAPSED; } @@ -4908,7 +4917,7 @@ public class AlarmManagerService extends SystemService { // this way, so WAKE_UP alarms will be delivered only when the device is awake. ArrayList<Alarm> triggerList = new ArrayList<Alarm>(); synchronized (mLock) { - final long nowELAPSED = mInjector.getElapsedRealtime(); + final long nowELAPSED = mInjector.getElapsedRealtimeMillis(); triggerAlarmsLocked(triggerList, nowELAPSED); updateNextAlarmClockLocked(); } @@ -5085,7 +5094,7 @@ public class AlarmManagerService extends SystemService { flags |= mConstants.TIME_TICK_ALLOWED_WHILE_IDLE ? FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED : 0; - setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0, + setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtimeMillis() + tickEventDelay, 0, 0, null, mTimeTickTrigger, TIME_TICK_TAG, flags, workSource, null, Process.myUid(), "android", null, EXACT_ALLOW_REASON_ALLOW_LIST); @@ -5272,7 +5281,7 @@ public class AlarmManagerService extends SystemService { } synchronized (mLock) { mTemporaryQuotaReserve.replenishQuota(packageName, userId, quotaBump, - mInjector.getElapsedRealtime()); + mInjector.getElapsedRealtimeMillis()); } mHandler.obtainMessage(AlarmHandler.TEMPORARY_QUOTA_CHANGED, userId, -1, packageName).sendToTarget(); @@ -5434,7 +5443,7 @@ public class AlarmManagerService extends SystemService { } private void updateStatsLocked(InFlight inflight) { - final long nowELAPSED = mInjector.getElapsedRealtime(); + final long nowELAPSED = mInjector.getElapsedRealtimeMillis(); BroadcastStats bs = inflight.mBroadcastStats; bs.nesting--; if (bs.nesting <= 0) { diff --git a/apex/jobscheduler/service/jni/com_android_server_alarm_AlarmManagerService.cpp b/apex/jobscheduler/service/jni/com_android_server_alarm_AlarmManagerService.cpp index 8f9e187a7a93..b2ed4d47adf4 100644 --- a/apex/jobscheduler/service/jni/com_android_server_alarm_AlarmManagerService.cpp +++ b/apex/jobscheduler/service/jni/com_android_server_alarm_AlarmManagerService.cpp @@ -76,19 +76,17 @@ typedef std::array<int, N_ANDROID_TIMERFDS> TimerFds; class AlarmImpl { public: - AlarmImpl(const TimerFds &fds, int epollfd, const std::string &rtc_dev) - : fds{fds}, epollfd{epollfd}, rtc_dev{rtc_dev} {} + AlarmImpl(const TimerFds &fds, int epollfd) + : fds{fds}, epollfd{epollfd} {} ~AlarmImpl(); int set(int type, struct timespec *ts); - int setTime(struct timeval *tv); int waitForAlarm(); int getTime(int type, struct itimerspec *spec); private: const TimerFds fds; const int epollfd; - std::string rtc_dev; }; AlarmImpl::~AlarmImpl() @@ -131,43 +129,6 @@ int AlarmImpl::getTime(int type, struct itimerspec *spec) return timerfd_gettime(fds[type], spec); } -int AlarmImpl::setTime(struct timeval *tv) -{ - if (settimeofday(tv, NULL) == -1) { - ALOGV("settimeofday() failed: %s", strerror(errno)); - return -1; - } - - android::base::unique_fd fd{open(rtc_dev.c_str(), O_RDWR)}; - if (!fd.ok()) { - ALOGE("Unable to open %s: %s", rtc_dev.c_str(), strerror(errno)); - return -1; - } - - struct tm tm; - if (!gmtime_r(&tv->tv_sec, &tm)) { - ALOGV("gmtime_r() failed: %s", strerror(errno)); - return -1; - } - - struct rtc_time rtc = {}; - rtc.tm_sec = tm.tm_sec; - rtc.tm_min = tm.tm_min; - rtc.tm_hour = tm.tm_hour; - rtc.tm_mday = tm.tm_mday; - rtc.tm_mon = tm.tm_mon; - rtc.tm_year = tm.tm_year; - rtc.tm_wday = tm.tm_wday; - rtc.tm_yday = tm.tm_yday; - rtc.tm_isdst = tm.tm_isdst; - if (ioctl(fd, RTC_SET_TIME, &rtc) == -1) { - ALOGV("RTC_SET_TIME ioctl failed: %s", strerror(errno)); - return -1; - } - - return 0; -} - int AlarmImpl::waitForAlarm() { epoll_event events[N_ANDROID_TIMERFDS]; @@ -198,28 +159,6 @@ int AlarmImpl::waitForAlarm() return result; } -static jint android_server_alarm_AlarmManagerService_setKernelTime(JNIEnv*, jobject, jlong nativeData, jlong millis) -{ - AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(nativeData); - - if (millis <= 0 || millis / 1000LL >= std::numeric_limits<time_t>::max()) { - return -1; - } - - struct timeval tv; - tv.tv_sec = (millis / 1000LL); - tv.tv_usec = ((millis % 1000LL) * 1000LL); - - ALOGD("Setting time of day to sec=%ld", tv.tv_sec); - - int ret = impl->setTime(&tv); - if (ret < 0) { - ALOGW("Unable to set rtc to %ld: %s", tv.tv_sec, strerror(errno)); - ret = -1; - } - return ret; -} - static jint android_server_alarm_AlarmManagerService_setKernelTimezone(JNIEnv*, jobject, jlong, jint minswest) { struct timezone tz; @@ -287,19 +226,7 @@ static jlong android_server_alarm_AlarmManagerService_init(JNIEnv*, jobject) } } - // Find the wall clock RTC. We expect this always to be /dev/rtc0, but - // check the /dev/rtc symlink first so that legacy devices that don't use - // rtc0 can add a symlink rather than need to carry a local patch to this - // code. - // - // TODO: if you're reading this in a world where all devices are using the - // GKI, you can remove the readlink and just assume /dev/rtc0. - std::string dev_rtc; - if (!android::base::Readlink("/dev/rtc", &dev_rtc)) { - dev_rtc = "/dev/rtc0"; - } - - std::unique_ptr<AlarmImpl> alarm{new AlarmImpl(fds, epollfd, dev_rtc)}; + std::unique_ptr<AlarmImpl> alarm{new AlarmImpl(fds, epollfd)}; for (size_t i = 0; i < fds.size(); i++) { epoll_event event; @@ -392,7 +319,6 @@ static const JNINativeMethod sMethods[] = { {"close", "(J)V", (void*)android_server_alarm_AlarmManagerService_close}, {"set", "(JIJJ)I", (void*)android_server_alarm_AlarmManagerService_set}, {"waitForAlarm", "(J)I", (void*)android_server_alarm_AlarmManagerService_waitForAlarm}, - {"setKernelTime", "(JJ)I", (void*)android_server_alarm_AlarmManagerService_setKernelTime}, {"setKernelTimezone", "(JI)I", (void*)android_server_alarm_AlarmManagerService_setKernelTimezone}, {"getNextAlarm", "(JI)J", (void*)android_server_alarm_AlarmManagerService_getNextAlarm}, }; diff --git a/services/core/java/com/android/server/AlarmManagerInternal.java b/services/core/java/com/android/server/AlarmManagerInternal.java index b2abdbd144b8..67aa2b9a89bb 100644 --- a/services/core/java/com/android/server/AlarmManagerInternal.java +++ b/services/core/java/com/android/server/AlarmManagerInternal.java @@ -16,8 +16,10 @@ package com.android.server; +import android.annotation.CurrentTimeMillisLong; import android.app.PendingIntent; +import com.android.server.SystemClockTime.TimeConfidence; import com.android.server.SystemTimeZone.TimeZoneConfidence; public interface AlarmManagerInternal { @@ -59,4 +61,15 @@ public interface AlarmManagerInternal { * for details */ void setTimeZone(String tzId, @TimeZoneConfidence int confidence); + + /** + * Sets the device's current time and time confidence. + * + * @param unixEpochTimeMillis the time + * @param confidence the confidence that {@code unixEpochTimeMillis} is correct, see {@link + * TimeConfidence} for details + * @param logMsg the reason the time is being changed, for bug report logging + */ + void setTime(@CurrentTimeMillisLong long unixEpochTimeMillis, @TimeConfidence int confidence, + String logMsg); } diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS index 6ff8e36a1f82..5dbdb9b3b77c 100644 --- a/services/core/java/com/android/server/OWNERS +++ b/services/core/java/com/android/server/OWNERS @@ -35,6 +35,8 @@ per-file NetIdManager.java = file:/services/core/java/com/android/server/net/OWN per-file PackageWatchdog.java, RescueParty.java = file:/services/core/java/com/android/server/rollback/OWNERS per-file PinnerService.java = file:/apct-tests/perftests/OWNERS per-file RescueParty.java = fdunlap@google.com, shuc@google.com +per-file SystemClockTime.java = file:/services/core/java/com/android/server/timedetector/OWNERS +per-file SystemTimeZone.java = file:/services/core/java/com/android/server/timezonedetector/OWNERS per-file TelephonyRegistry.java = file:/telephony/OWNERS per-file UiModeManagerService.java = file:/packages/SystemUI/OWNERS per-file VcnManagementService.java = file:/services/core/java/com/android/server/vcn/OWNERS diff --git a/services/core/java/com/android/server/SystemClockTime.java b/services/core/java/com/android/server/SystemClockTime.java new file mode 100644 index 000000000000..46fbbb290ed2 --- /dev/null +++ b/services/core/java/com/android/server/SystemClockTime.java @@ -0,0 +1,174 @@ +/* + * 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; + +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.annotation.CurrentTimeMillisLong; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Build; +import android.os.Environment; +import android.os.SystemProperties; +import android.util.LocalLog; +import android.util.Slog; + +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * A set of static methods that encapsulate knowledge of how the system clock time and associated + * metadata are stored on Android. + */ +public final class SystemClockTime { + + private static final String TAG = "SystemClockTime"; + + /** + * A log that records the decisions / decision metadata that affected the device's system clock + * time. This is logged in bug reports to assist with debugging issues with time. + */ + @NonNull + private static final LocalLog sTimeDebugLog = + new LocalLog(30, false /* useLocalTimestamps */); + + + /** + * An annotation that indicates a "time confidence" value is expected. + * + * <p>The confidence indicates whether the time is expected to be correct. The confidence can be + * upgraded or downgraded over time. It can be used to decide whether a user could / should be + * asked to confirm the time. For example, during device set up low confidence would describe a + * time that has been initialized by default. The user may then be asked to confirm the time, + * moving it to a high confidence. + */ + @Retention(SOURCE) + @Target(TYPE_USE) + @IntDef(prefix = "TIME_CONFIDENCE_", + value = { TIME_CONFIDENCE_LOW, TIME_CONFIDENCE_HIGH }) + public @interface TimeConfidence { + } + + /** Used when confidence is low and would (ideally) be confirmed by a user. */ + public static final @TimeConfidence int TIME_CONFIDENCE_LOW = 0; + + /** + * Used when confidence in the time is high and does not need to be confirmed by a user. + */ + public static final @TimeConfidence int TIME_CONFIDENCE_HIGH = 100; + + /** + * The confidence in the current time. Android's time confidence is held in memory because RTC + * hardware can forget / corrupt the time while the device is powered off. Therefore, on boot + * we can't assume the time is good, and so default it to "low" confidence until it is confirmed + * or explicitly set. + */ + private static @TimeConfidence int sTimeConfidence = TIME_CONFIDENCE_LOW; + + private static final long sNativeData = init(); + + private SystemClockTime() { + } + + /** + * Sets the system clock time to a reasonable lower bound. Used during boot-up to ensure the + * device has a time that is better than a default like 1970-01-01. + */ + public static void initializeIfRequired() { + // Use the most recent of Build.TIME, the root file system's timestamp, and the + // value of the ro.build.date.utc system property (which is in seconds). + final long systemBuildTime = Long.max( + 1000L * SystemProperties.getLong("ro.build.date.utc", -1L), + Long.max(Environment.getRootDirectory().lastModified(), Build.TIME)); + long currentTimeMillis = getCurrentTimeMillis(); + if (currentTimeMillis < systemBuildTime) { + String logMsg = "Current time only " + currentTimeMillis + + ", advancing to build time " + systemBuildTime; + Slog.i(TAG, logMsg); + setTimeAndConfidence(systemBuildTime, TIME_CONFIDENCE_LOW, logMsg); + } + } + + /** + * Sets the system clock time and confidence. See also {@link #setConfidence(int, String)} for + * an alternative that only sets the confidence. + * + * @param unixEpochMillis the time to set + * @param confidence the confidence in {@code unixEpochMillis}. See {@link TimeConfidence} for + * details. + * @param logMsg a log message that can be included in bug reports that explains the update + */ + public static void setTimeAndConfidence( + @CurrentTimeMillisLong long unixEpochMillis, int confidence, @NonNull String logMsg) { + synchronized (SystemClockTime.class) { + setTime(sNativeData, unixEpochMillis); + sTimeConfidence = confidence; + sTimeDebugLog.log(logMsg); + } + } + + /** + * Sets the system clock confidence. See also {@link #setTimeAndConfidence(long, int, String)} + * for an alternative that sets the time and confidence. + * + * @param confidence the confidence in the system clock time. See {@link TimeConfidence} for + * details. + * @param logMsg a log message that can be included in bug reports that explains the update + */ + public static void setConfidence(@TimeConfidence int confidence, @NonNull String logMsg) { + synchronized (SystemClockTime.class) { + sTimeConfidence = confidence; + sTimeDebugLog.log(logMsg); + } + } + + /** + * Returns the system clock time. The same as {@link System#currentTimeMillis()}. + */ + private static @CurrentTimeMillisLong long getCurrentTimeMillis() { + return System.currentTimeMillis(); + } + + /** + * Returns the system clock confidence. See {@link TimeConfidence} for details. + */ + public static @TimeConfidence int getTimeConfidence() { + synchronized (SystemClockTime.class) { + return sTimeConfidence; + } + } + + /** + * Adds an entry to the system time debug log that is included in bug reports. This method is + * intended to be used to record event that may lead to a time change, e.g. config or mode + * changes. + */ + public static void addDebugLogEntry(@NonNull String logMsg) { + sTimeDebugLog.log(logMsg); + } + + /** + * Dumps information about recent time / confidence changes to the supplied writer. + */ + public static void dump(PrintWriter writer) { + sTimeDebugLog.dump(writer); + } + + private static native long init(); + private static native int setTime(long nativeData, @CurrentTimeMillisLong long millis); +} diff --git a/services/core/java/com/android/server/timedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timedetector/ConfigurationInternal.java index 372bcc6b07ca..46f335ef9fa2 100644 --- a/services/core/java/com/android/server/timedetector/ConfigurationInternal.java +++ b/services/core/java/com/android/server/timedetector/ConfigurationInternal.java @@ -46,6 +46,7 @@ public final class ConfigurationInternal { private final boolean mAutoDetectionSupported; private final int mSystemClockUpdateThresholdMillis; + private final int mSystemClockConfidenceUpgradeThresholdMillis; private final Instant mAutoSuggestionLowerBound; private final Instant mManualSuggestionLowerBound; private final Instant mSuggestionUpperBound; @@ -57,6 +58,8 @@ public final class ConfigurationInternal { private ConfigurationInternal(Builder builder) { mAutoDetectionSupported = builder.mAutoDetectionSupported; mSystemClockUpdateThresholdMillis = builder.mSystemClockUpdateThresholdMillis; + mSystemClockConfidenceUpgradeThresholdMillis = + builder.mSystemClockConfidenceUpgradeThresholdMillis; mAutoSuggestionLowerBound = Objects.requireNonNull(builder.mAutoSuggestionLowerBound); mManualSuggestionLowerBound = Objects.requireNonNull(builder.mManualSuggestionLowerBound); mSuggestionUpperBound = Objects.requireNonNull(builder.mSuggestionUpperBound); @@ -82,6 +85,17 @@ public final class ConfigurationInternal { } /** + * Return the absolute threshold at/below which the system clock confidence can be upgraded. + * i.e. if the detector receives a high-confidence time and the current system clock is +/- this + * value from that time and the confidence in the time is low, then the device's confidence in + * the current system clock time can be upgraded. This needs to be an amount users would + * consider "close enough". + */ + public int getSystemClockConfidenceUpgradeThresholdMillis() { + return mSystemClockConfidenceUpgradeThresholdMillis; + } + + /** * Returns the lower bound for valid automatic time suggestions. It is guaranteed to be in the * past, i.e. it is unrelated to the current system clock time. * It holds no other meaning; it could be related to when the device system image was built, @@ -242,6 +256,8 @@ public final class ConfigurationInternal { return "ConfigurationInternal{" + "mAutoDetectionSupported=" + mAutoDetectionSupported + ", mSystemClockUpdateThresholdMillis=" + mSystemClockUpdateThresholdMillis + + ", mSystemClockConfidenceUpgradeThresholdMillis=" + + mSystemClockConfidenceUpgradeThresholdMillis + ", mAutoSuggestionLowerBound=" + mAutoSuggestionLowerBound + "(" + mAutoSuggestionLowerBound.toEpochMilli() + ")" + ", mManualSuggestionLowerBound=" + mManualSuggestionLowerBound @@ -258,6 +274,7 @@ public final class ConfigurationInternal { static final class Builder { private boolean mAutoDetectionSupported; private int mSystemClockUpdateThresholdMillis; + private int mSystemClockConfidenceUpgradeThresholdMillis; @NonNull private Instant mAutoSuggestionLowerBound; @NonNull private Instant mManualSuggestionLowerBound; @NonNull private Instant mSuggestionUpperBound; @@ -286,68 +303,55 @@ public final class ConfigurationInternal { this.mAutoDetectionEnabledSetting = toCopy.mAutoDetectionEnabledSetting; } - /** - * Sets whether the user is allowed to configure time settings on this device. - */ + /** See {@link ConfigurationInternal#isUserConfigAllowed()}. */ Builder setUserConfigAllowed(boolean userConfigAllowed) { mUserConfigAllowed = userConfigAllowed; return this; } - /** - * Sets whether automatic time detection is supported on this device. - */ + /** See {@link ConfigurationInternal#isAutoDetectionSupported()}. */ public Builder setAutoDetectionSupported(boolean supported) { mAutoDetectionSupported = supported; return this; } - /** - * Sets the absolute threshold below which the system clock need not be updated. i.e. if - * setting the system clock would adjust it by less than this (either backwards or forwards) - * then it need not be set. - */ + /** See {@link ConfigurationInternal#getSystemClockUpdateThresholdMillis()}. */ public Builder setSystemClockUpdateThresholdMillis(int systemClockUpdateThresholdMillis) { mSystemClockUpdateThresholdMillis = systemClockUpdateThresholdMillis; return this; } - /** - * Sets the lower bound for valid automatic time suggestions. - */ + /** See {@link ConfigurationInternal#getSystemClockConfidenceUpgradeThresholdMillis()}. */ + public Builder setSystemClockConfidenceUpgradeThresholdMillis(int thresholdMillis) { + mSystemClockConfidenceUpgradeThresholdMillis = thresholdMillis; + return this; + } + + /** See {@link ConfigurationInternal#getAutoSuggestionLowerBound()}. */ public Builder setAutoSuggestionLowerBound(@NonNull Instant autoSuggestionLowerBound) { mAutoSuggestionLowerBound = Objects.requireNonNull(autoSuggestionLowerBound); return this; } - /** - * Sets the lower bound for valid manual time suggestions. - */ + /** See {@link ConfigurationInternal#getManualSuggestionLowerBound()}. */ public Builder setManualSuggestionLowerBound(@NonNull Instant manualSuggestionLowerBound) { mManualSuggestionLowerBound = Objects.requireNonNull(manualSuggestionLowerBound); return this; } - /** - * Sets the upper bound for valid time suggestions (manual and automatic). - */ + /** See {@link ConfigurationInternal#getSuggestionUpperBound()}. */ public Builder setSuggestionUpperBound(@NonNull Instant suggestionUpperBound) { mSuggestionUpperBound = Objects.requireNonNull(suggestionUpperBound); return this; } - /** - * Sets the order to look at time suggestions when automatically detecting time. - * See {@code #ORIGIN_} constants - */ + /** See {@link ConfigurationInternal#getAutoOriginPriorities()}. */ public Builder setOriginPriorities(@NonNull @Origin int... originPriorities) { mOriginPriorities = Objects.requireNonNull(originPriorities); return this; } - /** - * Sets the value of the automatic time detection enabled setting for this device. - */ + /** See {@link ConfigurationInternal#getAutoDetectionEnabledSetting()}. */ Builder setAutoDetectionEnabledSetting(boolean autoDetectionEnabledSetting) { mAutoDetectionEnabledSetting = autoDetectionEnabledSetting; return this; diff --git a/services/core/java/com/android/server/timedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timedetector/EnvironmentImpl.java index 3e02b463284d..4972412472f1 100644 --- a/services/core/java/com/android/server/timedetector/EnvironmentImpl.java +++ b/services/core/java/com/android/server/timedetector/EnvironmentImpl.java @@ -16,16 +16,21 @@ package com.android.server.timedetector; +import android.annotation.CurrentTimeMillisLong; import android.annotation.NonNull; -import android.app.AlarmManager; import android.content.Context; import android.os.Handler; import android.os.PowerManager; import android.os.SystemClock; import android.util.Slog; +import com.android.server.AlarmManagerInternal; +import com.android.server.LocalServices; +import com.android.server.SystemClockTime; +import com.android.server.SystemClockTime.TimeConfidence; import com.android.server.timezonedetector.ConfigurationChangeListener; +import java.io.PrintWriter; import java.util.Objects; /** @@ -38,7 +43,7 @@ final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environment { @NonNull private final Handler mHandler; @NonNull private final ServiceConfigAccessor mServiceConfigAccessor; @NonNull private final PowerManager.WakeLock mWakeLock; - @NonNull private final AlarmManager mAlarmManager; + @NonNull private final AlarmManagerInternal mAlarmManagerInternal; EnvironmentImpl(@NonNull Context context, @NonNull Handler handler, @NonNull ServiceConfigAccessor serviceConfigAccessor) { @@ -49,7 +54,8 @@ final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environment { mWakeLock = Objects.requireNonNull( powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG)); - mAlarmManager = Objects.requireNonNull(context.getSystemService(AlarmManager.class)); + mAlarmManagerInternal = Objects.requireNonNull( + LocalServices.getService(AlarmManagerInternal.class)); } @Override @@ -84,9 +90,22 @@ final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environment { } @Override - public void setSystemClock(long newTimeMillis) { + public @TimeConfidence int systemClockConfidence() { + return SystemClockTime.getTimeConfidence(); + } + + @Override + public void setSystemClock( + @CurrentTimeMillisLong long newTimeMillis, @TimeConfidence int confidence, + @NonNull String logMsg) { + checkWakeLockHeld(); + mAlarmManagerInternal.setTime(newTimeMillis, confidence, logMsg); + } + + @Override + public void setSystemClockConfidence(@TimeConfidence int confidence, @NonNull String logMsg) { checkWakeLockHeld(); - mAlarmManager.setTime(newTimeMillis); + SystemClockTime.setConfidence(confidence, logMsg); } @Override @@ -100,4 +119,13 @@ final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environment { Slog.wtf(LOG_TAG, "WakeLock " + mWakeLock + " not held"); } } -} + + @Override + public void addDebugLogEntry(@NonNull String logMsg) { + SystemClockTime.addDebugLogEntry(logMsg); + } + + @Override + public void dumpDebugLog(@NonNull PrintWriter printWriter) { + SystemClockTime.dump(printWriter); + }} diff --git a/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java b/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java index 888304aa199b..0ea5f7a105d1 100644 --- a/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java +++ b/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java @@ -68,6 +68,15 @@ final class ServiceConfigAccessorImpl implements ServiceConfigAccessor { private static final int SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT = 2 * 1000; /** + * An absolute threshold at/below which the system clock confidence can be upgraded. i.e. if the + * detector receives a high-confidence time and the current system clock is +/- this value from + * that time and the confidence in the time is low, then the device's confidence in the current + * system clock time can be upgraded. This needs to be an amount users would consider + * "close enough". + */ + private static final int SYSTEM_CLOCK_CONFIRMATION_THRESHOLD_MILLIS = 1000; + + /** * By default telephony and network only suggestions are accepted and telephony takes * precedence over network. */ @@ -236,6 +245,8 @@ final class ServiceConfigAccessorImpl implements ServiceConfigAccessor { .setAutoDetectionSupported(isAutoDetectionSupported()) .setAutoDetectionEnabledSetting(getAutoDetectionEnabledSetting()) .setSystemClockUpdateThresholdMillis(getSystemClockUpdateThresholdMillis()) + .setSystemClockConfidenceUpgradeThresholdMillis( + getSystemClockConfidenceUpgradeThresholdMillis()) .setAutoSuggestionLowerBound(getAutoSuggestionLowerBound()) .setManualSuggestionLowerBound(timeDetectorHelper.getManualSuggestionLowerBound()) .setSuggestionUpperBound(timeDetectorHelper.getSuggestionUpperBound()) @@ -285,6 +296,10 @@ final class ServiceConfigAccessorImpl implements ServiceConfigAccessor { return mSystemClockUpdateThresholdMillis; } + private int getSystemClockConfidenceUpgradeThresholdMillis() { + return SYSTEM_CLOCK_CONFIRMATION_THRESHOLD_MILLIS; + } + @NonNull private Instant getAutoSuggestionLowerBound() { return mServerFlags.getOptionalInstant(KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE) diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java index 547cf9d32aa1..fe2760e9ce10 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java @@ -16,6 +16,7 @@ package com.android.server.timedetector; +import static com.android.server.SystemClockTime.TIME_CONFIDENCE_HIGH; import static com.android.server.timedetector.TimeDetectorStrategy.originToString; import android.annotation.CurrentTimeMillisLong; @@ -23,7 +24,6 @@ import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; -import android.app.AlarmManager; import android.app.time.ExternalTimeSuggestion; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.TelephonyTimeSuggestion; @@ -31,24 +31,24 @@ import android.content.Context; import android.os.Handler; import android.os.TimestampedValue; import android.util.IndentingPrintWriter; -import android.util.LocalLog; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.SystemClockTime; +import com.android.server.SystemClockTime.TimeConfidence; import com.android.server.timezonedetector.ArrayMapWithHistory; import com.android.server.timezonedetector.ConfigurationChangeListener; import com.android.server.timezonedetector.ReferenceWithHistory; +import java.io.PrintWriter; import java.time.Duration; import java.time.Instant; import java.util.Arrays; import java.util.Objects; /** - * An implementation of {@link TimeDetectorStrategy} that passes telephony and manual suggestions to - * {@link AlarmManager}. When there are multiple telephony sources, the one with the lowest ID is - * used unless the data becomes too stale. + * The real implementation of {@link TimeDetectorStrategy}. * * <p>Most public methods are marked synchronized to ensure thread safety around internal state. */ @@ -84,13 +84,6 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { */ private static final int KEEP_SUGGESTION_HISTORY_SIZE = 10; - /** - * A log that records the decisions / decision metadata that affected the device's system clock - * time. This is logged in bug reports to assist with debugging issues with detection. - */ - @NonNull - private final LocalLog mTimeChangesLog = new LocalLog(30, false /* useLocalTimestamps */); - @NonNull private final Environment mEnvironment; @@ -157,11 +150,30 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { @CurrentTimeMillisLong long systemClockMillis(); - /** Sets the device system clock. The WakeLock must be held. */ - void setSystemClock(@CurrentTimeMillisLong long newTimeMillis); + /** Returns the system clock confidence value. */ + @TimeConfidence int systemClockConfidence(); + + /** Sets the device system clock and confidence. The WakeLock must be held. */ + void setSystemClock( + @CurrentTimeMillisLong long newTimeMillis, @TimeConfidence int confidence, + @NonNull String logMsg); + + /** Sets the device system clock confidence. The WakeLock must be held. */ + void setSystemClockConfidence(@TimeConfidence int confidence, @NonNull String logMsg); /** Release the wake lock acquired by a call to {@link #acquireWakeLock()}. */ void releaseWakeLock(); + + + /** + * Adds a standalone entry to the time debug log. + */ + void addDebugLogEntry(@NonNull String logMsg); + + /** + * Dumps the time debug log to the supplied {@link PrintWriter}. + */ + void dumpDebugLog(PrintWriter printWriter); } static TimeDetectorStrategy create( @@ -250,7 +262,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } String cause = "Manual time suggestion received: suggestion=" + suggestion; - return setSystemClockIfRequired(ORIGIN_MANUAL, newUnixEpochTime, cause); + return setSystemClockAndConfidenceIfRequired(ORIGIN_MANUAL, newUnixEpochTime, cause); } @Override @@ -318,7 +330,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { String logMsg = "handleConfigurationInternalChanged:" + " oldConfiguration=" + mCurrentConfigurationInternal + ", newConfiguration=" + currentUserConfig; - logTimeDetectorChange(logMsg); + addDebugLogEntry(logMsg); mCurrentConfigurationInternal = currentUserConfig; boolean autoDetectionEnabled = @@ -335,11 +347,11 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } } - private void logTimeDetectorChange(@NonNull String logMsg) { + private void addDebugLogEntry(@NonNull String logMsg) { if (DBG) { Slog.d(LOG_TAG, logMsg); } - mTimeChangesLog.log(logMsg); + mEnvironment.addDebugLogEntry(logMsg); } @Override @@ -356,10 +368,11 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { long systemClockMillis = mEnvironment.systemClockMillis(); ipw.printf("mEnvironment.systemClockMillis()=%s (%s)\n", Instant.ofEpochMilli(systemClockMillis), systemClockMillis); + ipw.println("mEnvironment.systemClockConfidence()=" + mEnvironment.systemClockConfidence()); ipw.println("Time change log:"); ipw.increaseIndent(); // level 2 - mTimeChangesLog.dump(ipw); + SystemClockTime.dump(ipw); ipw.decreaseIndent(); // level 2 ipw.println("Telephony suggestion history:"); @@ -494,11 +507,6 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { @GuardedBy("this") private void doAutoTimeDetection(@NonNull String detectionReason) { - if (!mCurrentConfigurationInternal.getAutoDetectionEnabledBehavior()) { - // Avoid doing unnecessary work with this (race-prone) check. - return; - } - // Try the different origins one at a time. int[] originPriorities = mCurrentConfigurationInternal.getAutoOriginPriorities(); for (int origin : originPriorities) { @@ -544,7 +552,14 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { // Update the system clock if a good suggestion has been found. if (newUnixEpochTime != null) { - setSystemClockIfRequired(origin, newUnixEpochTime, cause); + if (mCurrentConfigurationInternal.getAutoDetectionEnabledBehavior()) { + setSystemClockAndConfidenceIfRequired(origin, newUnixEpochTime, cause); + } else { + // An automatically detected time can be used to raise the confidence in the + // current time even if the device is set to only allow user input for the time + // itself. + upgradeSystemClockConfidenceIfRequired(newUnixEpochTime, cause); + } return; } } @@ -718,14 +733,18 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } @GuardedBy("this") - private boolean setSystemClockIfRequired( + private boolean setSystemClockAndConfidenceIfRequired( @Origin int origin, @NonNull TimestampedValue<Long> time, @NonNull String cause) { + // Any time set through this class is inherently high confidence. Either it came directly + // from a user, or it was detected automatically. + @TimeConfidence final int newTimeConfidence = TIME_CONFIDENCE_HIGH; boolean isOriginAutomatic = isOriginAutomatic(origin); if (isOriginAutomatic) { if (!mCurrentConfigurationInternal.getAutoDetectionEnabledBehavior()) { if (DBG) { - Slog.d(LOG_TAG, "Auto time detection is not enabled." + Slog.d(LOG_TAG, + "Auto time detection is not enabled / no confidence update is needed." + " origin=" + originToString(origin) + ", time=" + time + ", cause=" + cause); @@ -746,7 +765,57 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { mEnvironment.acquireWakeLock(); try { - return setSystemClockUnderWakeLock(origin, time, cause); + return setSystemClockAndConfidenceUnderWakeLock(origin, time, newTimeConfidence, cause); + } finally { + mEnvironment.releaseWakeLock(); + } + } + + /** + * Upgrades the system clock confidence if the current time matches the supplied auto-detected + * time. The method never changes the system clock and it never lowers the confidence. It only + * raises the confidence if the supplied time is within the configured threshold of the current + * system clock time. + */ + @GuardedBy("this") + private void upgradeSystemClockConfidenceIfRequired( + @NonNull TimestampedValue<Long> autoDetectedUnixEpochTime, @NonNull String cause) { + @TimeConfidence int newTimeConfidence = TIME_CONFIDENCE_HIGH; + @TimeConfidence int currentTimeConfidence = mEnvironment.systemClockConfidence(); + boolean timeNeedsConfirmation = currentTimeConfidence < newTimeConfidence; + if (!timeNeedsConfirmation) { + return; + } + + // All system clock calculation take place under a wake lock. + mEnvironment.acquireWakeLock(); + try { + // Check if the specified time matches the current system clock time (closely + // enough) to raise the confidence. + long elapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis(); + long actualSystemClockMillis = mEnvironment.systemClockMillis(); + long adjustedAutoDetectedUnixEpochMillis = TimeDetectorStrategy.getTimeAt( + autoDetectedUnixEpochTime, elapsedRealtimeMillis); + long absTimeDifferenceMillis = + Math.abs(adjustedAutoDetectedUnixEpochMillis - actualSystemClockMillis); + int confidenceUpgradeThresholdMillis = + mCurrentConfigurationInternal.getSystemClockConfidenceUpgradeThresholdMillis(); + boolean updateConfidenceRequired = + absTimeDifferenceMillis <= confidenceUpgradeThresholdMillis; + if (updateConfidenceRequired) { + String logMsg = "Upgrade system clock confidence." + + " autoDetectedUnixEpochTime=" + autoDetectedUnixEpochTime + + " newTimeConfidence=" + newTimeConfidence + + " cause=" + cause + + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + + " (old) actualSystemClockMillis=" + actualSystemClockMillis + + " currentTimeConfidence=" + currentTimeConfidence; + if (DBG) { + Slog.d(LOG_TAG, logMsg); + } + + mEnvironment.setSystemClockConfidence(newTimeConfidence, logMsg); + } } finally { mEnvironment.releaseWakeLock(); } @@ -757,8 +826,9 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } @GuardedBy("this") - private boolean setSystemClockUnderWakeLock( - @Origin int origin, @NonNull TimestampedValue<Long> newTime, @NonNull String cause) { + private boolean setSystemClockAndConfidenceUnderWakeLock( + @Origin int origin, @NonNull TimestampedValue<Long> newTime, + @TimeConfidence int newTimeConfidence, @NonNull String cause) { long elapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis(); boolean isOriginAutomatic = isOriginAutomatic(origin); @@ -776,6 +846,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { "System clock has not tracked elapsed real time clock. A clock may" + " be inaccurate or something unexpectedly set the system" + " clock." + + " origin=" + originToString(origin) + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + " expectedTimeMillis=" + expectedTimeMillis + " actualTimeMillis=" + actualSystemClockMillis @@ -784,44 +855,72 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } } + // If the new signal would make sufficient difference to the system clock or mean a change + // in confidence then system state must be updated. + // Adjust for the time that has elapsed since the signal was received. long newSystemClockMillis = TimeDetectorStrategy.getTimeAt(newTime, elapsedRealtimeMillis); - - // Check if the new signal would make sufficient difference to the system clock. If it's - // below the threshold then ignore it. long absTimeDifference = Math.abs(newSystemClockMillis - actualSystemClockMillis); long systemClockUpdateThreshold = mCurrentConfigurationInternal.getSystemClockUpdateThresholdMillis(); - if (absTimeDifference < systemClockUpdateThreshold) { + boolean updateSystemClockRequired = absTimeDifference >= systemClockUpdateThreshold; + + @TimeConfidence int currentTimeConfidence = mEnvironment.systemClockConfidence(); + boolean updateConfidenceRequired = newTimeConfidence > currentTimeConfidence; + + if (updateSystemClockRequired) { + String logMsg = "Set system clock & confidence." + + " origin=" + originToString(origin) + + " newTime=" + newTime + + " newTimeConfidence=" + newTimeConfidence + + " cause=" + cause + + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + + " (old) actualSystemClockMillis=" + actualSystemClockMillis + + " newSystemClockMillis=" + newSystemClockMillis + + " currentTimeConfidence=" + currentTimeConfidence; + mEnvironment.setSystemClock(newSystemClockMillis, newTimeConfidence, logMsg); if (DBG) { - Slog.d(LOG_TAG, "Not setting system clock. New time and" - + " system clock are close enough." - + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + Slog.d(LOG_TAG, logMsg); + } + + // CLOCK_PARANOIA : Record the last time this class set the system clock due to an + // auto-time signal, or clear the record it is being done manually. + if (isOriginAutomatic(origin)) { + mLastAutoSystemClockTimeSet = newTime; + } else { + mLastAutoSystemClockTimeSet = null; + } + } else if (updateConfidenceRequired) { + // Only the confidence needs updating. This path is separate from a system clock update + // to deliberately avoid touching the system clock's value when it's not needed. Doing + // so could introduce inaccuracies or cause unnecessary wear in RTC hardware or + // associated storage. + String logMsg = "Set system clock confidence." + + " origin=" + originToString(origin) + + " newTime=" + newTime + + " newTimeConfidence=" + newTimeConfidence + + " cause=" + cause + + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + + " (old) actualSystemClockMillis=" + actualSystemClockMillis + + " newSystemClockMillis=" + newSystemClockMillis + + " currentTimeConfidence=" + currentTimeConfidence; + if (DBG) { + Slog.d(LOG_TAG, logMsg); + } + mEnvironment.setSystemClockConfidence(newTimeConfidence, logMsg); + } else { + // Neither clock nor confidence need updating. + if (DBG) { + Slog.d(LOG_TAG, "Not setting system clock or confidence." + + " origin=" + originToString(origin) + " newTime=" + newTime + + " newTimeConfidence=" + newTimeConfidence + " cause=" + cause + + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + " systemClockUpdateThreshold=" + systemClockUpdateThreshold - + " absTimeDifference=" + absTimeDifference); + + " absTimeDifference=" + absTimeDifference + + " currentTimeConfidence=" + currentTimeConfidence); } - return true; - } - - mEnvironment.setSystemClock(newSystemClockMillis); - String logMsg = "Set system clock using time=" + newTime - + " cause=" + cause - + " elapsedRealtimeMillis=" + elapsedRealtimeMillis - + " (old) actualSystemClockMillis=" + actualSystemClockMillis - + " newSystemClockMillis=" + newSystemClockMillis; - if (DBG) { - Slog.d(LOG_TAG, logMsg); - } - mTimeChangesLog.log(logMsg); - - // CLOCK_PARANOIA : Record the last time this class set the system clock due to an auto-time - // signal, or clear the record it is being done manually. - if (isOriginAutomatic(origin)) { - mLastAutoSystemClockTimeSet = newTime; - } else { - mLastAutoSystemClockTimeSet = null; } return true; } diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 5d6ffd8b8ead..7e93d623f3e0 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -53,6 +53,7 @@ cc_library_static { "com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker.cpp", "com_android_server_stats_pull_StatsPullAtomService.cpp", "com_android_server_storage_AppFuseBridge.cpp", + "com_android_server_SystemClockTime.cpp", "com_android_server_SystemServer.cpp", "com_android_server_tv_TvUinputBridge.cpp", "com_android_server_tv_TvInputHal.cpp", diff --git a/services/core/jni/com_android_server_SystemClockTime.cpp b/services/core/jni/com_android_server_SystemClockTime.cpp new file mode 100644 index 000000000000..9db4c429f0c7 --- /dev/null +++ b/services/core/jni/com_android_server_SystemClockTime.cpp @@ -0,0 +1,126 @@ +/* + * 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. + */ + +#define LOG_TAG "SystemClockTime" + +#include <android-base/file.h> +#include <android-base/unique_fd.h> +#include <linux/rtc.h> +#include <nativehelper/JNIHelp.h> +#include <utils/Log.h> +#include <utils/String8.h> + +#include "jni.h" + +namespace android { + +class SystemClockImpl { +public: + SystemClockImpl(const std::string &rtc_dev) : rtc_dev{rtc_dev} {} + + int setTime(struct timeval *tv); + +private: + std::string rtc_dev; +}; + +int SystemClockImpl::setTime(struct timeval *tv) { + if (settimeofday(tv, NULL) == -1) { + ALOGV("settimeofday() failed: %s", strerror(errno)); + return -1; + } + + android::base::unique_fd fd{open(rtc_dev.c_str(), O_RDWR)}; + if (!fd.ok()) { + ALOGE("Unable to open %s: %s", rtc_dev.c_str(), strerror(errno)); + return -1; + } + + struct tm tm; + if (!gmtime_r(&tv->tv_sec, &tm)) { + ALOGV("gmtime_r() failed: %s", strerror(errno)); + return -1; + } + + struct rtc_time rtc = {}; + rtc.tm_sec = tm.tm_sec; + rtc.tm_min = tm.tm_min; + rtc.tm_hour = tm.tm_hour; + rtc.tm_mday = tm.tm_mday; + rtc.tm_mon = tm.tm_mon; + rtc.tm_year = tm.tm_year; + rtc.tm_wday = tm.tm_wday; + rtc.tm_yday = tm.tm_yday; + rtc.tm_isdst = tm.tm_isdst; + if (ioctl(fd, RTC_SET_TIME, &rtc) == -1) { + ALOGV("RTC_SET_TIME ioctl failed: %s", strerror(errno)); + return -1; + } + + return 0; +} + +static jlong com_android_server_SystemClockTime_init(JNIEnv *, jobject) { + // Find the wall clock RTC. We expect this always to be /dev/rtc0, but + // check the /dev/rtc symlink first so that legacy devices that don't use + // rtc0 can add a symlink rather than need to carry a local patch to this + // code. + // + // TODO: if you're reading this in a world where all devices are using the + // GKI, you can remove the readlink and just assume /dev/rtc0. + std::string dev_rtc; + if (!android::base::Readlink("/dev/rtc", &dev_rtc)) { + dev_rtc = "/dev/rtc0"; + } + + std::unique_ptr<SystemClockImpl> system_clock{new SystemClockImpl(dev_rtc)}; + return reinterpret_cast<jlong>(system_clock.release()); +} + +static jint com_android_server_SystemClockTime_setTime(JNIEnv *, jobject, jlong nativeData, + jlong millis) { + SystemClockImpl *impl = reinterpret_cast<SystemClockImpl *>(nativeData); + + if (millis <= 0 || millis / 1000LL >= std::numeric_limits<time_t>::max()) { + return -1; + } + + struct timeval tv; + tv.tv_sec = (millis / 1000LL); + tv.tv_usec = ((millis % 1000LL) * 1000LL); + + ALOGD("Setting time of day to sec=%ld", tv.tv_sec); + + int ret = impl->setTime(&tv); + if (ret < 0) { + ALOGW("Unable to set rtc to %ld: %s", tv.tv_sec, strerror(errno)); + ret = -1; + } + return ret; +} + +static const JNINativeMethod sMethods[] = { + /* name, signature, funcPtr */ + {"init", "()J", (void *)com_android_server_SystemClockTime_init}, + {"setTime", "(JJ)I", (void *)com_android_server_SystemClockTime_setTime}, +}; + +int register_com_android_server_SystemClockTime(JNIEnv *env) { + return jniRegisterNativeMethods(env, "com/android/server/SystemClockTime", sMethods, + NELEM(sMethods)); +} + +} /* namespace android */ diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index 00bef0935308..00f851f9f4ff 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -65,6 +65,7 @@ int register_android_server_companion_virtual_InputController(JNIEnv* env); int register_android_server_app_GameManagerService(JNIEnv* env); int register_com_android_server_wm_TaskFpsCallbackController(JNIEnv* env); int register_com_android_server_display_DisplayControl(JNIEnv* env); +int register_com_android_server_SystemClockTime(JNIEnv* env); }; using namespace android; @@ -122,5 +123,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_app_GameManagerService(env); register_com_android_server_wm_TaskFpsCallbackController(env); register_com_android_server_display_DisplayControl(env); + register_com_android_server_SystemClockTime(env); return JNI_VERSION_1_4; } diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java index 446317e8829f..dc33ebf9bb49 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -168,6 +168,7 @@ import com.android.server.AppStateTracker; import com.android.server.AppStateTrackerImpl; import com.android.server.DeviceIdleInternal; import com.android.server.LocalServices; +import com.android.server.SystemClockTime.TimeConfidence; import com.android.server.SystemService; import com.android.server.pm.permission.PermissionManagerService; import com.android.server.pm.permission.PermissionManagerServiceInternal; @@ -345,10 +346,6 @@ public class AlarmManagerServiceTest { } @Override - void setKernelTime(long millis) { - } - - @Override int getSystemUiUid(PackageManagerInternal unused) { return SYSTEM_UI_UID; } @@ -360,7 +357,18 @@ public class AlarmManagerServiceTest { } @Override - long getElapsedRealtime() { + void initializeTimeIfRequired() { + // No-op + } + + @Override + void setCurrentTimeMillis(long unixEpochMillis, + @TimeConfidence int confidence, String logMsg) { + mNowRtcTest = unixEpochMillis; + } + + @Override + long getElapsedRealtimeMillis() { return mNowElapsedTest; } 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 1aea6727d3a1..060c31f2985e 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java @@ -16,6 +16,8 @@ package com.android.server.timedetector; +import static com.android.server.SystemClockTime.TIME_CONFIDENCE_HIGH; +import static com.android.server.SystemClockTime.TIME_CONFIDENCE_LOW; import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_EXTERNAL; import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_GNSS; import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_NETWORK; @@ -35,6 +37,7 @@ import android.os.TimestampedValue; import androidx.test.runner.AndroidJUnit4; +import com.android.server.SystemClockTime.TimeConfidence; import com.android.server.timedetector.TimeDetectorStrategy.Origin; import com.android.server.timezonedetector.ConfigurationChangeListener; @@ -42,6 +45,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.io.PrintWriter; import java.time.Duration; import java.time.Instant; import java.time.LocalDateTime; @@ -86,6 +90,7 @@ public class TimeDetectorStrategyImplTest { .setAutoDetectionSupported(true) .setSystemClockUpdateThresholdMillis( ARBITRARY_SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS) + .setSystemClockUpdateThresholdMillis(TIME_CONFIDENCE_HIGH) .setAutoSuggestionLowerBound(DEFAULT_SUGGESTION_LOWER_BOUND) .setManualSuggestionLowerBound(DEFAULT_SUGGESTION_LOWER_BOUND) .setSuggestionUpperBound(DEFAULT_SUGGESTION_UPPER_BOUND) @@ -99,6 +104,7 @@ public class TimeDetectorStrategyImplTest { .setAutoDetectionSupported(true) .setSystemClockUpdateThresholdMillis( ARBITRARY_SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS) + .setSystemClockUpdateThresholdMillis(TIME_CONFIDENCE_HIGH) .setAutoSuggestionLowerBound(DEFAULT_SUGGESTION_LOWER_BOUND) .setManualSuggestionLowerBound(DEFAULT_SUGGESTION_LOWER_BOUND) .setSuggestionUpperBound(DEFAULT_SUGGESTION_UPPER_BOUND) @@ -112,12 +118,14 @@ public class TimeDetectorStrategyImplTest { public void setUp() { mFakeEnvironment = new FakeEnvironment(); mFakeEnvironment.initializeConfig(CONFIG_AUTO_DISABLED); - mFakeEnvironment.initializeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO); + mFakeEnvironment.initializeFakeClocks( + ARBITRARY_CLOCK_INITIALIZATION_INFO, TIME_CONFIDENCE_LOW); } @Test public void testSuggestTelephonyTime_autoTimeEnabled() { - Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED); + Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); int slotIndex = ARBITRARY_SLOT_INDEX; Instant testTime = ARBITRARY_TEST_TIME; @@ -129,18 +137,21 @@ public class TimeDetectorStrategyImplTest { long expectedSystemClockMillis = script.calculateTimeInMillisForNow(timeSuggestion.getUnixEpochTime()); - script.verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis) + script.verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) + .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis) .assertLatestTelephonySuggestion(slotIndex, timeSuggestion); } @Test public void testSuggestTelephonyTime_emptySuggestionIgnored() { - Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED); + Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); int slotIndex = ARBITRARY_SLOT_INDEX; TelephonyTimeSuggestion timeSuggestion = script.generateTelephonyTimeSuggestion(slotIndex, null); script.simulateTelephonyTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking() .assertLatestTelephonySuggestion(slotIndex, null); } @@ -278,17 +289,115 @@ public class TimeDetectorStrategyImplTest { } } + /** + * If an auto suggested time matches the current system clock, the confidence in the current + * system clock is raised even when auto time is disabled. The system clock itself must not be + * changed. + */ @Test - public void testSuggestTelephonyTime_autoTimeDisabled() { - Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_DISABLED); + public void testSuggestTelephonyTime_autoTimeDisabled_suggestionMatchesSystemClock() { + TimestampedValue<Instant> initialClockTime = ARBITRARY_CLOCK_INITIALIZATION_INFO; + final int confidenceUpgradeThresholdMillis = 1000; + ConfigurationInternal configInternal = + new ConfigurationInternal.Builder(CONFIG_AUTO_DISABLED) + .setSystemClockConfidenceUpgradeThresholdMillis( + confidenceUpgradeThresholdMillis) + .build(); + Script script = new Script() + .pokeFakeClocks(initialClockTime, TIME_CONFIDENCE_LOW) + .simulateConfigurationInternalChange(configInternal); int slotIndex = ARBITRARY_SLOT_INDEX; - TelephonyTimeSuggestion timeSuggestion = - script.generateTelephonyTimeSuggestion(slotIndex, ARBITRARY_TEST_TIME); - script.simulateTimePassing() - .simulateTelephonyTimeSuggestion(timeSuggestion) - .verifySystemClockWasNotSetAndResetCallTracking() - .assertLatestTelephonySuggestion(slotIndex, timeSuggestion); + + script.simulateTimePassing(); + long timeElapsedMillis = + script.peekElapsedRealtimeMillis() - initialClockTime.getReferenceTimeMillis(); + + // Create a suggestion time that approximately matches the current system clock. + Instant suggestionInstant = initialClockTime.getValue() + .plusMillis(timeElapsedMillis) + .plusMillis(confidenceUpgradeThresholdMillis); + TimestampedValue<Long> matchingClockTime = new TimestampedValue<>( + script.peekElapsedRealtimeMillis(), + suggestionInstant.toEpochMilli()); + TelephonyTimeSuggestion timeSuggestion = new TelephonyTimeSuggestion.Builder(slotIndex) + .setUnixEpochTime(matchingClockTime) + .build(); + script.simulateTelephonyTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) + .verifySystemClockWasNotSetAndResetCallTracking(); + } + + /** + * If an auto suggested time doesn't match the current system clock, the confidence in the + * current system clock will stay where it is. The system clock itself must not be changed. + */ + @Test + public void testSuggestTelephonyTime_autoTimeDisabled_suggestionMismatchesSystemClock() { + TimestampedValue<Instant> initialClockTime = ARBITRARY_CLOCK_INITIALIZATION_INFO; + final int confidenceUpgradeThresholdMillis = 1000; + ConfigurationInternal configInternal = + new ConfigurationInternal.Builder(CONFIG_AUTO_DISABLED) + .setSystemClockConfidenceUpgradeThresholdMillis( + confidenceUpgradeThresholdMillis) + .build(); + Script script = new Script().pokeFakeClocks(initialClockTime, TIME_CONFIDENCE_LOW) + .simulateConfigurationInternalChange(configInternal); + + int slotIndex = ARBITRARY_SLOT_INDEX; + + script.simulateTimePassing(); + long timeElapsedMillis = + script.peekElapsedRealtimeMillis() - initialClockTime.getReferenceTimeMillis(); + + // Create a suggestion time that doesn't match the current system clock closely enough. + Instant suggestionInstant = initialClockTime.getValue() + .plusMillis(timeElapsedMillis) + .plusMillis(confidenceUpgradeThresholdMillis + 1); + TimestampedValue<Long> mismatchingClockTime = new TimestampedValue<>( + script.peekElapsedRealtimeMillis(), + suggestionInstant.toEpochMilli()); + TelephonyTimeSuggestion timeSuggestion = new TelephonyTimeSuggestion.Builder(slotIndex) + .setUnixEpochTime(mismatchingClockTime) + .build(); + script.simulateTelephonyTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) + .verifySystemClockWasNotSetAndResetCallTracking(); + } + + /** + * If a suggested time doesn't match the current system clock, the confidence in the current + * system clock will not drop. + */ + @Test + public void testSuggestTelephonyTime_autoTimeDisabled_suggestionMismatchesSystemClock2() { + TimestampedValue<Instant> initialClockTime = ARBITRARY_CLOCK_INITIALIZATION_INFO; + final int confidenceUpgradeThresholdMillis = 1000; + ConfigurationInternal configInternal = + new ConfigurationInternal.Builder(CONFIG_AUTO_DISABLED) + .setSystemClockConfidenceUpgradeThresholdMillis( + confidenceUpgradeThresholdMillis) + .build(); + Script script = new Script().pokeFakeClocks(initialClockTime, TIME_CONFIDENCE_HIGH) + .simulateConfigurationInternalChange(configInternal); + + int slotIndex = ARBITRARY_SLOT_INDEX; + + script.simulateTimePassing(); + long timeElapsedMillis = + script.peekElapsedRealtimeMillis() - initialClockTime.getReferenceTimeMillis(); + + // Create a suggestion time that doesn't closely match the current system clock. + Instant initialClockInstant = initialClockTime.getValue(); + TimestampedValue<Long> mismatchingClockTime = new TimestampedValue<>( + script.peekElapsedRealtimeMillis(), + initialClockInstant.plusMillis(timeElapsedMillis + 1_000_000).toEpochMilli()); + TelephonyTimeSuggestion timeSuggestion = new TelephonyTimeSuggestion.Builder(slotIndex) + .setUnixEpochTime(mismatchingClockTime) + .build(); + script.simulateTelephonyTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) + .verifySystemClockWasNotSetAndResetCallTracking(); } @Test @@ -298,7 +407,8 @@ public class TimeDetectorStrategyImplTest { new ConfigurationInternal.Builder(CONFIG_AUTO_ENABLED) .setSystemClockUpdateThresholdMillis(systemClockUpdateThresholdMillis) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant testTime = ARBITRARY_TEST_TIME; int slotIndex = ARBITRARY_SLOT_INDEX; @@ -311,6 +421,7 @@ public class TimeDetectorStrategyImplTest { script.simulateTimePassing(); long expectedSystemClockMillis1 = script.calculateTimeInMillisForNow(unixEpochTime1); script.simulateTelephonyTimeSuggestion(timeSuggestion1) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1) .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1); @@ -327,6 +438,7 @@ public class TimeDetectorStrategyImplTest { TelephonyTimeSuggestion timeSuggestion2 = createTelephonyTimeSuggestion(slotIndex, unixEpochTime2); script.simulateTelephonyTimeSuggestion(timeSuggestion2) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasNotSetAndResetCallTracking() .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1); @@ -339,6 +451,7 @@ public class TimeDetectorStrategyImplTest { TelephonyTimeSuggestion timeSuggestion3 = createTelephonyTimeSuggestion(slotIndex, unixEpochTime3); script.simulateTelephonyTimeSuggestion(timeSuggestion3) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasNotSetAndResetCallTracking() .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1); @@ -350,6 +463,7 @@ public class TimeDetectorStrategyImplTest { TelephonyTimeSuggestion timeSuggestion4 = createTelephonyTimeSuggestion(slotIndex, unixEpochTime4); script.simulateTelephonyTimeSuggestion(timeSuggestion4) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis4) .assertLatestTelephonySuggestion(slotIndex, timeSuggestion4); } @@ -362,7 +476,8 @@ public class TimeDetectorStrategyImplTest { new ConfigurationInternal.Builder(CONFIG_AUTO_DISABLED) .setSystemClockUpdateThresholdMillis(systemClockUpdateThresholdMillis) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); int slotIndex = ARBITRARY_SLOT_INDEX; Instant testTime = ARBITRARY_TEST_TIME; @@ -376,6 +491,7 @@ public class TimeDetectorStrategyImplTest { // Simulate the time signal being received. It should not be used because auto time // detection is off but it should be recorded. script.simulateTelephonyTimeSuggestion(timeSuggestion1) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking() .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1); @@ -386,11 +502,13 @@ public class TimeDetectorStrategyImplTest { // Turn on auto time detection. script.simulateAutoTimeDetectionToggle() + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1) .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1); // Turn off auto time detection. script.simulateAutoTimeDetectionToggle() + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasNotSetAndResetCallTracking() .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1); @@ -408,18 +526,21 @@ public class TimeDetectorStrategyImplTest { // The new time, though valid, should not be set in the system clock because auto time is // disabled. script.simulateTelephonyTimeSuggestion(timeSuggestion2) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasNotSetAndResetCallTracking() .assertLatestTelephonySuggestion(slotIndex, timeSuggestion2); // Turn on auto time detection. script.simulateAutoTimeDetectionToggle() + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis2) .assertLatestTelephonySuggestion(slotIndex, timeSuggestion2); } @Test public void testSuggestTelephonyTime_maxSuggestionAge() { - Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED); + Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); int slotIndex = ARBITRARY_SLOT_INDEX; Instant testTime = ARBITRARY_TEST_TIME; @@ -431,6 +552,7 @@ public class TimeDetectorStrategyImplTest { long expectedSystemClockMillis = script.calculateTimeInMillisForNow(telephonySuggestion.getUnixEpochTime()); script.simulateTelephonyTimeSuggestion(telephonySuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking( expectedSystemClockMillis /* expectedNetworkBroadcast */) .assertLatestTelephonySuggestion(slotIndex, telephonySuggestion); @@ -442,7 +564,7 @@ public class TimeDetectorStrategyImplTest { script.simulateTimePassing(TimeDetectorStrategyImpl.MAX_SUGGESTION_TIME_AGE_MILLIS); // Look inside and check what the strategy considers the current best telephony suggestion. - // It should still be the, it's just no longer used. + // It should still be there, it's just no longer used. assertNull(script.peekBestTelephonySuggestion()); script.assertLatestTelephonySuggestion(slotIndex, telephonySuggestion); } @@ -454,12 +576,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_TELEPHONY) .setAutoSuggestionLowerBound(TEST_SUGGESTION_LOWER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant belowLowerBound = TEST_SUGGESTION_LOWER_BOUND.minusSeconds(1); TelephonyTimeSuggestion timeSuggestion = script.generateTelephonyTimeSuggestion(ARBITRARY_SLOT_INDEX, belowLowerBound); script.simulateTelephonyTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -470,12 +594,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_TELEPHONY) .setAutoSuggestionLowerBound(TEST_SUGGESTION_LOWER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant aboveLowerBound = TEST_SUGGESTION_LOWER_BOUND.plusSeconds(1); TelephonyTimeSuggestion timeSuggestion = script.generateTelephonyTimeSuggestion(ARBITRARY_SLOT_INDEX, aboveLowerBound); script.simulateTelephonyTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(aboveLowerBound.toEpochMilli()); } @@ -486,12 +612,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_TELEPHONY) .setSuggestionUpperBound(TEST_SUGGESTION_UPPER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant aboveUpperBound = TEST_SUGGESTION_UPPER_BOUND.plusSeconds(1); TelephonyTimeSuggestion timeSuggestion = script.generateTelephonyTimeSuggestion(ARBITRARY_SLOT_INDEX, aboveUpperBound); script.simulateTelephonyTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -502,18 +630,21 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_TELEPHONY) .setSuggestionUpperBound(TEST_SUGGESTION_UPPER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant belowUpperBound = TEST_SUGGESTION_UPPER_BOUND.minusSeconds(1); TelephonyTimeSuggestion timeSuggestion = script.generateTelephonyTimeSuggestion(ARBITRARY_SLOT_INDEX, belowUpperBound); script.simulateTelephonyTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(belowUpperBound.toEpochMilli()); } @Test public void testSuggestManualTime_autoTimeDisabled() { - Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_DISABLED); + Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_DISABLED) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); ManualTimeSuggestion timeSuggestion = script.generateManualTimeSuggestion(ARBITRARY_TEST_TIME); @@ -524,12 +655,14 @@ public class TimeDetectorStrategyImplTest { script.calculateTimeInMillisForNow(timeSuggestion.getUnixEpochTime()); script.simulateManualTimeSuggestion( ARBITRARY_USER_ID, timeSuggestion, true /* expectedResult */) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis); } @Test public void testSuggestManualTime_retainsAutoSignal() { - Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED); + Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); int slotIndex = ARBITRARY_SLOT_INDEX; @@ -544,6 +677,7 @@ public class TimeDetectorStrategyImplTest { long expectedAutoClockMillis = script.calculateTimeInMillisForNow(telephonyTimeSuggestion.getUnixEpochTime()); script.simulateTelephonyTimeSuggestion(telephonyTimeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(expectedAutoClockMillis) .assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion); @@ -552,6 +686,7 @@ public class TimeDetectorStrategyImplTest { // Switch to manual. script.simulateAutoTimeDetectionToggle() + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasNotSetAndResetCallTracking() .assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion); @@ -568,6 +703,7 @@ public class TimeDetectorStrategyImplTest { script.calculateTimeInMillisForNow(manualTimeSuggestion.getUnixEpochTime()); script.simulateManualTimeSuggestion( ARBITRARY_USER_ID, manualTimeSuggestion, true /* expectedResult */) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(expectedManualClockMillis) .assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion); @@ -580,17 +716,20 @@ public class TimeDetectorStrategyImplTest { expectedAutoClockMillis = script.calculateTimeInMillisForNow(telephonyTimeSuggestion.getUnixEpochTime()); script.verifySystemClockWasSetAndResetCallTracking(expectedAutoClockMillis) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion); // Switch back to manual - nothing should happen to the clock. script.simulateAutoTimeDetectionToggle() + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasNotSetAndResetCallTracking() .assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion); } @Test public void testSuggestManualTime_isIgnored_whenAutoTimeEnabled() { - Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED); + Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); ManualTimeSuggestion timeSuggestion = script.generateManualTimeSuggestion(ARBITRARY_TEST_TIME); @@ -598,6 +737,7 @@ public class TimeDetectorStrategyImplTest { script.simulateTimePassing() .simulateManualTimeSuggestion( ARBITRARY_USER_ID, timeSuggestion, false /* expectedResult */) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -607,12 +747,14 @@ public class TimeDetectorStrategyImplTest { new ConfigurationInternal.Builder(CONFIG_AUTO_DISABLED) .setSuggestionUpperBound(TEST_SUGGESTION_UPPER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant aboveUpperBound = TEST_SUGGESTION_UPPER_BOUND.plusSeconds(1); ManualTimeSuggestion timeSuggestion = script.generateManualTimeSuggestion(aboveUpperBound); script.simulateManualTimeSuggestion( ARBITRARY_USER_ID, timeSuggestion, false /* expectedResult */) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -622,12 +764,14 @@ public class TimeDetectorStrategyImplTest { new ConfigurationInternal.Builder(CONFIG_AUTO_DISABLED) .setSuggestionUpperBound(TEST_SUGGESTION_UPPER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant belowUpperBound = TEST_SUGGESTION_UPPER_BOUND.minusSeconds(1); ManualTimeSuggestion timeSuggestion = script.generateManualTimeSuggestion(belowUpperBound); script.simulateManualTimeSuggestion( ARBITRARY_USER_ID, timeSuggestion, true /* expectedResult */) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(belowUpperBound.toEpochMilli()); } @@ -637,12 +781,14 @@ public class TimeDetectorStrategyImplTest { new ConfigurationInternal.Builder(CONFIG_AUTO_DISABLED) .setManualSuggestionLowerBound(TEST_SUGGESTION_LOWER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant belowLowerBound = TEST_SUGGESTION_LOWER_BOUND.minusSeconds(1); ManualTimeSuggestion timeSuggestion = script.generateManualTimeSuggestion(belowLowerBound); script.simulateManualTimeSuggestion( ARBITRARY_USER_ID, timeSuggestion, false /* expectedResult */) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -652,12 +798,14 @@ public class TimeDetectorStrategyImplTest { new ConfigurationInternal.Builder(CONFIG_AUTO_DISABLED) .setManualSuggestionLowerBound(TEST_SUGGESTION_LOWER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant aboveLowerBound = TEST_SUGGESTION_LOWER_BOUND.plusSeconds(1); ManualTimeSuggestion timeSuggestion = script.generateManualTimeSuggestion(aboveLowerBound); script.simulateManualTimeSuggestion( ARBITRARY_USER_ID, timeSuggestion, true /* expectedResult */) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(aboveLowerBound.toEpochMilli()); } @@ -667,7 +815,8 @@ public class TimeDetectorStrategyImplTest { new ConfigurationInternal.Builder(CONFIG_AUTO_ENABLED) .setOriginPriorities(ORIGIN_NETWORK) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); NetworkTimeSuggestion timeSuggestion = script.generateNetworkTimeSuggestion(ARBITRARY_TEST_TIME); @@ -677,6 +826,7 @@ public class TimeDetectorStrategyImplTest { long expectedSystemClockMillis = script.calculateTimeInMillisForNow(timeSuggestion.getUnixEpochTime()); script.simulateNetworkTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis); } @@ -703,12 +853,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_NETWORK) .setAutoSuggestionLowerBound(TEST_SUGGESTION_LOWER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant belowLowerBound = TEST_SUGGESTION_LOWER_BOUND.minusSeconds(1); NetworkTimeSuggestion timeSuggestion = script.generateNetworkTimeSuggestion(belowLowerBound); script.simulateNetworkTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -719,12 +871,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_NETWORK) .setAutoSuggestionLowerBound(TEST_SUGGESTION_LOWER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant aboveLowerBound = TEST_SUGGESTION_LOWER_BOUND.plusSeconds(1); NetworkTimeSuggestion timeSuggestion = script.generateNetworkTimeSuggestion(aboveLowerBound); script.simulateNetworkTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(aboveLowerBound.toEpochMilli()); } @@ -735,12 +889,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_NETWORK) .setSuggestionUpperBound(TEST_SUGGESTION_UPPER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant aboveUpperBound = TEST_SUGGESTION_UPPER_BOUND.plusSeconds(1); NetworkTimeSuggestion timeSuggestion = script.generateNetworkTimeSuggestion(aboveUpperBound); script.simulateNetworkTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -751,12 +907,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_NETWORK) .setSuggestionUpperBound(TEST_SUGGESTION_UPPER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant belowUpperBound = TEST_SUGGESTION_UPPER_BOUND.minusSeconds(1); NetworkTimeSuggestion timeSuggestion = script.generateNetworkTimeSuggestion(belowUpperBound); script.simulateNetworkTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(belowUpperBound.toEpochMilli()); } @@ -766,7 +924,8 @@ public class TimeDetectorStrategyImplTest { new ConfigurationInternal.Builder(CONFIG_AUTO_ENABLED) .setOriginPriorities(ORIGIN_GNSS) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); GnssTimeSuggestion timeSuggestion = script.generateGnssTimeSuggestion(ARBITRARY_TEST_TIME); @@ -776,6 +935,7 @@ public class TimeDetectorStrategyImplTest { long expectedSystemClockMillis = script.calculateTimeInMillisForNow(timeSuggestion.getUnixEpochTime()); script.simulateGnssTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis); } @@ -802,12 +962,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_GNSS) .setAutoSuggestionLowerBound(TEST_SUGGESTION_LOWER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant belowLowerBound = TEST_SUGGESTION_LOWER_BOUND.minusSeconds(1); GnssTimeSuggestion timeSuggestion = script.generateGnssTimeSuggestion(belowLowerBound); script.simulateGnssTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -818,12 +980,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_GNSS) .setAutoSuggestionLowerBound(TEST_SUGGESTION_LOWER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant aboveLowerBound = TEST_SUGGESTION_LOWER_BOUND.plusSeconds(1); GnssTimeSuggestion timeSuggestion = script.generateGnssTimeSuggestion(aboveLowerBound); script.simulateGnssTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(aboveLowerBound.toEpochMilli()); } @@ -834,12 +998,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_GNSS) .setSuggestionUpperBound(TEST_SUGGESTION_UPPER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant aboveUpperBound = TEST_SUGGESTION_UPPER_BOUND.plusSeconds(1); GnssTimeSuggestion timeSuggestion = script.generateGnssTimeSuggestion(aboveUpperBound); script.simulateGnssTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -850,12 +1016,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_GNSS) .setSuggestionUpperBound(TEST_SUGGESTION_UPPER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant belowUpperBound = TEST_SUGGESTION_UPPER_BOUND.minusSeconds(1); GnssTimeSuggestion timeSuggestion = script.generateGnssTimeSuggestion(belowUpperBound); script.simulateGnssTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(belowUpperBound.toEpochMilli()); } @@ -865,7 +1033,8 @@ public class TimeDetectorStrategyImplTest { new ConfigurationInternal.Builder(CONFIG_AUTO_ENABLED) .setOriginPriorities(ORIGIN_EXTERNAL) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); ExternalTimeSuggestion timeSuggestion = script.generateExternalTimeSuggestion(ARBITRARY_TEST_TIME); @@ -875,6 +1044,7 @@ public class TimeDetectorStrategyImplTest { long expectedSystemClockMillis = script.calculateTimeInMillisForNow(timeSuggestion.getUnixEpochTime()); script.simulateExternalTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis); } @@ -901,12 +1071,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_EXTERNAL) .setAutoSuggestionLowerBound(TEST_SUGGESTION_LOWER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant belowLowerBound = TEST_SUGGESTION_LOWER_BOUND.minusSeconds(1); ExternalTimeSuggestion timeSuggestion = script.generateExternalTimeSuggestion(belowLowerBound); script.simulateExternalTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -917,12 +1089,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_EXTERNAL) .setAutoSuggestionLowerBound(TEST_SUGGESTION_LOWER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant aboveLowerBound = TEST_SUGGESTION_LOWER_BOUND.plusSeconds(1); ExternalTimeSuggestion timeSuggestion = script.generateExternalTimeSuggestion(aboveLowerBound); script.simulateExternalTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(aboveLowerBound.toEpochMilli()); } @@ -933,12 +1107,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_EXTERNAL) .setSuggestionUpperBound(TEST_SUGGESTION_UPPER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant aboveUpperBound = TEST_SUGGESTION_UPPER_BOUND.plusSeconds(1); ExternalTimeSuggestion timeSuggestion = script.generateExternalTimeSuggestion(aboveUpperBound); script.simulateExternalTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -949,12 +1125,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_EXTERNAL) .setSuggestionUpperBound(TEST_SUGGESTION_UPPER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant belowUpperBound = TEST_SUGGESTION_UPPER_BOUND.minusSeconds(1); ExternalTimeSuggestion timeSuggestion = script.generateExternalTimeSuggestion(belowUpperBound); script.simulateExternalTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(belowUpperBound.toEpochMilli()); } @@ -1472,6 +1650,7 @@ public class TimeDetectorStrategyImplTest { private boolean mWakeLockAcquired; private long mElapsedRealtimeMillis; private long mSystemClockMillis; + private int mSystemClockConfidence = TIME_CONFIDENCE_LOW; private ConfigurationChangeListener mConfigurationInternalChangeListener; // Tracking operations. @@ -1481,9 +1660,10 @@ public class TimeDetectorStrategyImplTest { mConfigurationInternal = configurationInternal; } - public void initializeFakeClocks(TimestampedValue<Instant> timeInfo) { + public void initializeFakeClocks( + TimestampedValue<Instant> timeInfo, @TimeConfidence int timeConfidence) { pokeElapsedRealtimeMillis(timeInfo.getReferenceTimeMillis()); - pokeSystemClockMillis(timeInfo.getValue().toEpochMilli()); + pokeSystemClockMillis(timeInfo.getValue().toEpochMilli(), timeConfidence); } @Override @@ -1515,10 +1695,23 @@ public class TimeDetectorStrategyImplTest { } @Override - public void setSystemClock(long newTimeMillis) { + public @TimeConfidence int systemClockConfidence() { + return mSystemClockConfidence; + } + + @Override + public void setSystemClock( + long newTimeMillis, @TimeConfidence int confidence, String logMsg) { assertWakeLockAcquired(); mSystemClockWasSet = true; mSystemClockMillis = newTimeMillis; + mSystemClockConfidence = confidence; + } + + @Override + public void setSystemClockConfidence(@TimeConfidence int confidence, String logMsg) { + assertWakeLockAcquired(); + mSystemClockConfidence = confidence; } @Override @@ -1527,6 +1720,16 @@ public class TimeDetectorStrategyImplTest { mWakeLockAcquired = false; } + @Override + public void addDebugLogEntry(String logMsg) { + // No-op for tests + } + + @Override + public void dumpDebugLog(PrintWriter printWriter) { + // No-op for tests + } + // Methods below are for managing the fake's behavior. void simulateConfigurationInternalChange(ConfigurationInternal configurationInternal) { @@ -1538,8 +1741,9 @@ public class TimeDetectorStrategyImplTest { mElapsedRealtimeMillis = elapsedRealtimeMillis; } - void pokeSystemClockMillis(long systemClockMillis) { + void pokeSystemClockMillis(long systemClockMillis, @TimeConfidence int timeConfidence) { mSystemClockMillis = systemClockMillis; + mSystemClockConfidence = timeConfidence; } long peekElapsedRealtimeMillis() { @@ -1567,6 +1771,10 @@ public class TimeDetectorStrategyImplTest { assertEquals(expectedSystemClockMillis, mSystemClockMillis); } + public void verifySystemClockConfidence(@TimeConfidence int expectedConfidence) { + assertEquals(expectedConfidence, mSystemClockConfidence); + } + void resetCallTracking() { mSystemClockWasSet = false; } @@ -1589,6 +1797,14 @@ public class TimeDetectorStrategyImplTest { mTimeDetectorStrategy = new TimeDetectorStrategyImpl(mFakeEnvironment); } + Script pokeFakeClocks(TimestampedValue<Instant> initialClockTime, + @TimeConfidence int timeConfidence) { + mFakeEnvironment.pokeElapsedRealtimeMillis(initialClockTime.getReferenceTimeMillis()); + mFakeEnvironment.pokeSystemClockMillis( + initialClockTime.getValue().toEpochMilli(), timeConfidence); + return this; + } + long peekElapsedRealtimeMillis() { return mFakeEnvironment.peekElapsedRealtimeMillis(); } @@ -1675,6 +1891,11 @@ public class TimeDetectorStrategyImplTest { return this; } + Script verifySystemClockConfidence(@TimeConfidence int expectedConfidence) { + mFakeEnvironment.verifySystemClockConfidence(expectedConfidence); + return this; + } + /** * White box test info: Asserts the latest suggestion for the slotIndex is as expected. */ |