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