1 package org.robolectric.shadows; 2 3 import static android.os.Build.VERSION_CODES.Q; 4 import static java.time.ZoneOffset.UTC; 5 import static org.robolectric.shadows.ShadowLooper.assertLooperMode; 6 7 import android.os.SimpleClock; 8 import android.os.SystemClock; 9 import java.time.DateTimeException; 10 import java.time.Duration; 11 import java.util.concurrent.TimeUnit; 12 import org.robolectric.annotation.ClassName; 13 import org.robolectric.annotation.Implementation; 14 import org.robolectric.annotation.Implements; 15 import org.robolectric.annotation.LooperMode; 16 import org.robolectric.annotation.LooperMode.Mode; 17 18 /** 19 * The shadow API for {@link SystemClock}. 20 * 21 * <p>The behavior of SystemClock in Robolectric will differ based on the current {@link 22 * LooperMode}. See {@link ShadowLegacySystemClock} and {@link ShadowPausedSystemClock} for more 23 * details. 24 */ 25 @Implements(value = SystemClock.class, shadowPicker = ShadowSystemClock.Picker.class) 26 public abstract class ShadowSystemClock { 27 protected static boolean networkTimeAvailable = true; 28 private static boolean gnssTimeAvailable = true; 29 30 /** 31 * Implements {@link System#currentTimeMillis} through ShadowWrangler. 32 * 33 * @return Current time in millis. 34 */ 35 @SuppressWarnings("unused") currentTimeMillis()36 public static long currentTimeMillis() { 37 return ShadowLegacySystemClock.currentTimeMillis(); 38 } 39 40 /** 41 * Implements {@link System#nanoTime}. 42 * 43 * @return Current time with nanos. 44 * @deprecated Don't call this method directly; instead, use {@link System#nanoTime()}. 45 */ 46 @SuppressWarnings("unused") 47 @Deprecated nanoTime()48 public static long nanoTime() { 49 return ShadowSystem.nanoTime(); 50 } 51 52 /** 53 * Sets the value for {@link System#nanoTime()}. 54 * 55 * <p>May only be used for {@link LooperMode.Mode#LEGACY}. For {@link LooperMode.Mode#PAUSED}, 56 * {@param nanoTime} is calculated based on {@link SystemClock#uptimeMillis()} and can't be set 57 * explicitly. 58 */ setNanoTime(long nanoTime)59 public static void setNanoTime(long nanoTime) { 60 assertLooperMode(Mode.LEGACY); 61 ShadowLegacySystemClock.setNanoTime(nanoTime); 62 } 63 64 /** Sets whether network time is available. */ setNetworkTimeAvailable(boolean available)65 public static void setNetworkTimeAvailable(boolean available) { 66 networkTimeAvailable = available; 67 } 68 69 /** 70 * An alternate to {@link #advanceBy(Duration)} for older Android code bases where Duration is not 71 * available. 72 */ advanceBy(long time, TimeUnit unit)73 public static void advanceBy(long time, TimeUnit unit) { 74 SystemClock.setCurrentTimeMillis(SystemClock.uptimeMillis() + unit.toMillis(time)); 75 } 76 77 /** 78 * A convenience method for advancing the clock via {@link SystemClock#setCurrentTimeMillis(long)} 79 * 80 * @param duration The interval by which to advance. 81 */ advanceBy(Duration duration)82 public static void advanceBy(Duration duration) { 83 SystemClock.setCurrentTimeMillis(SystemClock.uptimeMillis() + duration.toMillis()); 84 } 85 86 /** 87 * In a deep sleep scenario, {@param elapsedRealtime} is advanced for this duration when in deep 88 * sleep whilst {@param uptime} maintains its original value. 89 * 90 * <p>May only be used for {@link LooperMode.Mode#PAUSED}. For {@link LooperMode.Mode#LEGACY}, 91 * {@param elapsedRealtime} is equal to {@param uptime}. 92 */ simulateDeepSleep(Duration duration)93 public static void simulateDeepSleep(Duration duration) { 94 assertLooperMode(Mode.PAUSED); 95 ShadowPausedSystemClock.deepSleep(duration.toMillis()); 96 } 97 98 @Implementation(minSdk = Q) currentGnssTimeClock()99 protected static @ClassName("java.time.Clock") Object currentGnssTimeClock() { 100 if (gnssTimeAvailable) { 101 return new SimpleClock(UTC) { 102 @Override 103 public long millis() { 104 return SystemClock.uptimeMillis(); 105 } 106 }; 107 } else { 108 throw new DateTimeException("Gnss based time is not available."); 109 } 110 } 111 112 /** Sets whether gnss location based time is available. */ 113 public static void setGnssTimeAvailable(boolean available) { 114 gnssTimeAvailable = available; 115 } 116 117 public static void reset() { 118 networkTimeAvailable = true; 119 gnssTimeAvailable = true; 120 } 121 122 public static class Picker extends LooperShadowPicker<ShadowSystemClock> { 123 124 public Picker() { 125 super(ShadowLegacySystemClock.class, ShadowPausedSystemClock.class); 126 } 127 } 128 } 129