• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric.shadows;
2 
3 import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
4 import static android.os.Build.VERSION_CODES.P;
5 
6 import android.os.SystemClock;
7 import java.time.DateTimeException;
8 import java.util.List;
9 import java.util.concurrent.CopyOnWriteArrayList;
10 import javax.annotation.concurrent.GuardedBy;
11 import org.robolectric.annotation.HiddenApi;
12 import org.robolectric.annotation.Implementation;
13 import org.robolectric.annotation.Implements;
14 import org.robolectric.annotation.LooperMode;
15 import org.robolectric.annotation.Resetter;
16 
17 /**
18  * A shadow SystemClock used when {@link LooperMode.Mode#PAUSED} is active.
19  *
20  * <p>In this variant, there is just one global system time controlled by this class. The current
21  * time is fixed in place, and manually advanced by calling {@link
22  * SystemClock#setCurrentTimeMillis(long)}
23  *
24  * <p>{@link SystemClock#uptimeMillis()} and {@link SystemClock#currentThreadTimeMillis()} are
25  * identical.
26  *
27  * <p>This class should not be referenced directly. Use ShadowSystemClock instead.
28  */
29 @Implements(
30     value = SystemClock.class,
31     isInAndroidSdk = false,
32     shadowPicker = ShadowSystemClock.Picker.class)
33 public class ShadowPausedSystemClock extends ShadowSystemClock {
34   private static final long INITIAL_TIME = 100;
35   private static final int MILLIS_PER_NANO = 1000000;
36 
37   @GuardedBy("ShadowPausedSystemClock.class")
38   private static long currentTimeMillis = INITIAL_TIME;
39 
40   private static final List<Listener> listeners = new CopyOnWriteArrayList<>();
41   // hopefully temporary list of clock listeners that are NOT cleared between tests
42   // This is needed to accomodate Loopers which are not reset between tests
43   private static final List<Listener> staticListeners = new CopyOnWriteArrayList<>();
44 
45   /**
46    * Callback for clock updates
47    */
48   interface Listener {
onClockAdvanced()49     void onClockAdvanced();
50   }
51 
addListener(Listener listener)52   static void addListener(Listener listener) {
53     listeners.add(listener);
54   }
55 
removeListener(Listener listener)56   static void removeListener(Listener listener) {
57     listeners.remove(listener);
58     staticListeners.remove(listener);
59   }
60 
addStaticListener(Listener listener)61   static void addStaticListener(Listener listener) {
62     staticListeners.add(listener);
63   }
64 
65   /** Advances the current time by given millis, without sleeping the current thread/ */
66   @Implementation
sleep(long millis)67   protected static void sleep(long millis) {
68     synchronized (ShadowPausedSystemClock.class) {
69       currentTimeMillis += millis;
70     }
71     informListeners();
72   }
73 
informListeners()74   private static void informListeners() {
75     for (Listener listener : listeners) {
76       listener.onClockAdvanced();
77     }
78     for (Listener listener : staticListeners) {
79       listener.onClockAdvanced();
80     }
81   }
82 
83   /**
84    * Sets the current wall time.
85    *
86    * <p>Currently does not perform any permission checks.
87    *
88    * @return false if specified time is less than current time.
89    */
90   @Implementation
setCurrentTimeMillis(long millis)91   protected static boolean setCurrentTimeMillis(long millis) {
92     synchronized (ShadowPausedSystemClock.class) {
93       if (currentTimeMillis > millis) {
94         return false;
95       } else if (currentTimeMillis == millis) {
96         return true;
97       } else {
98         currentTimeMillis = millis;
99       }
100     }
101     informListeners();
102     return true;
103   }
104 
105   @Implementation
uptimeMillis()106   protected static synchronized long uptimeMillis() {
107     return currentTimeMillis;
108   }
109 
110   @Implementation
elapsedRealtime()111   protected static long elapsedRealtime() {
112     return uptimeMillis();
113   }
114 
115   @Implementation(minSdk = JELLY_BEAN_MR1)
elapsedRealtimeNanos()116   protected static long elapsedRealtimeNanos() {
117     return elapsedRealtime() * MILLIS_PER_NANO;
118   }
119 
120   @Implementation
currentThreadTimeMillis()121   protected static long currentThreadTimeMillis() {
122     return uptimeMillis();
123   }
124 
125   @HiddenApi
126   @Implementation
currentThreadTimeMicro()127   protected static long currentThreadTimeMicro() {
128     return uptimeMillis() * 1000;
129   }
130 
131   @HiddenApi
132   @Implementation
currentTimeMicro()133   protected static long currentTimeMicro() {
134     return currentThreadTimeMicro();
135   }
136 
137   @Implementation(minSdk = P)
138   @HiddenApi
currentNetworkTimeMillis()139   protected static synchronized long currentNetworkTimeMillis() {
140     if (networkTimeAvailable) {
141       return currentTimeMillis;
142     } else {
143       throw new DateTimeException("Network time not available");
144     }
145   }
146 
147   @Resetter
reset()148   public static synchronized void reset() {
149     currentTimeMillis = INITIAL_TIME;
150     ShadowSystemClock.reset();
151     listeners.clear();
152   }
153 }
154