diff options
13 files changed, 897 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 37fb746fa16f..232d3c9aaa53 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; @@ -1417,7 +1420,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; } @@ -1577,7 +1580,7 @@ public class AlarmManagerService extends SystemService { alarmsToDeliver = alarmsForUid; mPendingBackgroundAlarms.remove(uid); } - deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, mInjector.getElapsedRealtime()); + deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, mInjector.getElapsedRealtimeMillis()); } /** @@ -1594,7 +1597,8 @@ public class AlarmManagerService extends SystemService { mPendingBackgroundAlarms, alarmsToDeliver, this::isBackgroundRestricted); if (alarmsToDeliver.size() > 0) { - deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, mInjector.getElapsedRealtime()); + deliverPendingBackgroundAlarmsLocked( + alarmsToDeliver, mInjector.getElapsedRealtimeMillis()); } } @@ -1908,17 +1912,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 @@ -2133,21 +2128,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); @@ -2260,7 +2252,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 @@ -2383,7 +2375,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) { @@ -2405,7 +2397,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; } @@ -2472,7 +2464,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); } @@ -2527,7 +2519,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); } @@ -2587,7 +2579,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); @@ -2643,7 +2635,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); } @@ -2719,6 +2711,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); @@ -2987,12 +2986,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 @@ -3142,7 +3145,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"); @@ -3618,7 +3621,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, @@ -3956,7 +3959,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(); @@ -4069,7 +4072,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(); @@ -4208,7 +4211,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; @@ -4310,7 +4313,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 @@ -4357,7 +4359,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); } @@ -4602,20 +4604,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(); } @@ -4665,7 +4674,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; } @@ -4913,7 +4922,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(); } @@ -5090,7 +5099,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); @@ -5277,7 +5286,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(); @@ -5439,7 +5448,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/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 1b9282d426d0..830e2ac029c2 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; @@ -346,10 +347,6 @@ public class AlarmManagerServiceTest { } @Override - void setKernelTime(long millis) { - } - - @Override int getSystemUiUid(PackageManagerInternal unused) { return SYSTEM_UI_UID; } @@ -361,7 +358,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. */ |