1 package org.robolectric.shadows;
2 
3 import static android.os.Build.VERSION_CODES.P;
4 
5 import android.os.SystemClock;
6 import java.time.DateTimeException;
7 import java.util.concurrent.TimeUnit;
8 import org.robolectric.RuntimeEnvironment;
9 import org.robolectric.annotation.HiddenApi;
10 import org.robolectric.annotation.Implementation;
11 import org.robolectric.annotation.Implements;
12 import org.robolectric.annotation.Resetter;
13 
14 /**
15  * A shadow SystemClock for {@link LooperMode.Mode.LEGACY}
16  *
17  * <p>In LEGACY LooperMode, Robolectric's concept of current time is base on the current time of the
18  * UI Scheduler for consistency with previous implementations. This is not ideal, since both
19  * schedulers (background and foreground), can see different values for the current time.
20  */
21 @Implements(
22     value = SystemClock.class,
23     shadowPicker = ShadowSystemClock.Picker.class,
24     // turn off shadowOf generation
25     isInAndroidSdk = false)
26 public class ShadowLegacySystemClock extends ShadowSystemClock {
27   private static long bootedAt = 0;
28   private static long nanoTime = 0;
29   private static final int MILLIS_PER_NANO = 1000000;
30 
now()31   static long now() {
32     return RuntimeEnvironment.getMasterScheduler().getCurrentTime();
33   }
34 
35   @Implementation
sleep(long millis)36   protected static void sleep(long millis) {
37     nanoTime = millis * MILLIS_PER_NANO;
38     RuntimeEnvironment.getMasterScheduler().advanceBy(millis, TimeUnit.MILLISECONDS);
39   }
40 
41   @Implementation
setCurrentTimeMillis(long millis)42   protected static boolean setCurrentTimeMillis(long millis) {
43     if (now() > millis) {
44       return false;
45     }
46     nanoTime = millis * MILLIS_PER_NANO;
47     RuntimeEnvironment.getMasterScheduler().advanceTo(millis);
48     return true;
49   }
50 
51   @Implementation
uptimeMillis()52   protected static long uptimeMillis() {
53     return now() - bootedAt;
54   }
55 
56   @Implementation
elapsedRealtime()57   protected static long elapsedRealtime() {
58     return uptimeMillis();
59   }
60 
61   @Implementation
elapsedRealtimeNanos()62   protected static long elapsedRealtimeNanos() {
63     return elapsedRealtime() * MILLIS_PER_NANO;
64   }
65 
66   @Implementation
currentThreadTimeMillis()67   protected static long currentThreadTimeMillis() {
68     return uptimeMillis();
69   }
70 
71   @HiddenApi
72   @Implementation
currentThreadTimeMicro()73   public static long currentThreadTimeMicro() {
74     return uptimeMillis() * 1000;
75   }
76 
77   @HiddenApi
78   @Implementation
currentTimeMicro()79   public static long currentTimeMicro() {
80     return now() * 1000;
81   }
82 
83   /**
84    * Implements {@link System#currentTimeMillis} through ShadowWrangler.
85    *
86    * @return Current time in millis.
87    */
88   @SuppressWarnings("unused")
currentTimeMillis()89   public static long currentTimeMillis() {
90     return nanoTime / MILLIS_PER_NANO;
91   }
92 
93   /**
94    * Implements {@link System#nanoTime} through ShadowWrangler.
95    *
96    * @return Current time with nanos.
97    */
98   @SuppressWarnings("unused")
nanoTime()99   public static long nanoTime() {
100     return nanoTime;
101   }
102 
setNanoTime(long nanoTime)103   public static void setNanoTime(long nanoTime) {
104     ShadowLegacySystemClock.nanoTime = nanoTime;
105   }
106 
107   @Implementation(minSdk = P)
108   @HiddenApi
currentNetworkTimeMillis()109   protected static long currentNetworkTimeMillis() {
110     if (networkTimeAvailable) {
111       return currentTimeMillis();
112     } else {
113       throw new DateTimeException("Network time not available");
114     }
115   }
116 
117   @Resetter
reset()118   public static void reset() {
119     ShadowSystemClock.reset();
120   }
121 }
122