1 package org.robolectric.shadows; 2 3 import static com.google.common.base.Preconditions.checkArgument; 4 5 import java.util.HashMap; 6 import java.util.Map; 7 import java.util.concurrent.ThreadLocalRandom; 8 import javax.annotation.concurrent.GuardedBy; 9 import org.robolectric.annotation.Implementation; 10 import org.robolectric.annotation.Implements; 11 import org.robolectric.annotation.Resetter; 12 13 @Implements(android.os.Process.class) 14 public class ShadowProcess { 15 private static int pid; 16 private static int uid = getRandomApplicationUid(); 17 private static int tid = getRandomApplicationUid(); 18 private static final Object threadPrioritiesLock = new Object(); 19 20 @GuardedBy("threadPrioritiesLock") 21 private static final Map<Integer, Integer> threadPriorities = new HashMap<Integer, Integer>(); 22 23 @Implementation myPid()24 protected static final int myPid() { 25 return pid; 26 } 27 28 /** 29 * Returns the identifier of this process's uid. Unlike Android UIDs are randomly initialized to 30 * prevent tests from depending on any given value. Tests should access the current process UID 31 * via {@link android.os.Process#myUid()}. You can override this value by calling {@link 32 * #setUid(int)}. 33 */ 34 @Implementation myUid()35 protected static final int myUid() { 36 return uid; 37 } 38 39 /** 40 * Returns the identifier ({@link java.lang.Thread#getId()}) of the current thread ({@link 41 * java.lang.Thread#currentThread()}). 42 */ 43 @Implementation myTid()44 protected static final int myTid() { 45 return (int) Thread.currentThread().getId(); 46 } 47 48 /** 49 * Stores priority for the current thread, but doesn't actually change it to not mess up with test 50 * runner. Unlike real implementation does not throw any exceptions. 51 */ 52 @Implementation setThreadPriority(int priority)53 protected static final void setThreadPriority(int priority) { 54 synchronized (threadPrioritiesLock) { 55 threadPriorities.put(ShadowProcess.myTid(), priority); 56 } 57 } 58 59 /** 60 * Stores priority for the given thread, but doesn't actually change it to not mess up with test 61 * runner. Unlike real implementation does not throw any exceptions. 62 * 63 * @param tid The identifier of the thread. If equals zero, the identifier of the calling thread 64 * will be used. 65 */ 66 @Implementation setThreadPriority(int tid, int priority)67 protected static final void setThreadPriority(int tid, int priority) { 68 checkArgument( 69 priority >= android.os.Process.THREAD_PRIORITY_URGENT_AUDIO 70 && priority <= android.os.Process.THREAD_PRIORITY_LOWEST, 71 "priority %s out of range. Use a Process.THREAD_PRIORITY_* constant.", 72 priority); 73 74 if (tid == 0) { 75 tid = ShadowProcess.myTid(); 76 } 77 synchronized (threadPrioritiesLock) { 78 threadPriorities.put(tid, priority); 79 } 80 } 81 82 /** 83 * Returns priority stored for the given thread. 84 * 85 * @param tid The identifier of the thread. If equals zero, the identifier of the calling thread 86 * will be used. 87 */ 88 @Implementation getThreadPriority(int tid)89 protected static final int getThreadPriority(int tid) { 90 if (tid == 0) { 91 tid = ShadowProcess.myTid(); 92 } 93 synchronized (threadPrioritiesLock) { 94 return threadPriorities.getOrDefault(tid, 0); 95 } 96 } 97 98 /** 99 * Sets the identifier of this process. 100 */ setUid(int uid)101 public static void setUid(int uid) { 102 ShadowProcess.uid = uid; 103 } 104 105 /** 106 * Sets the identifier of this process. 107 */ setPid(int pid)108 public static void setPid(int pid) { 109 ShadowProcess.pid = pid; 110 } 111 112 @Resetter reset()113 public static void reset() { 114 ShadowProcess.pid = 0; 115 synchronized (threadPrioritiesLock) { 116 threadPriorities.clear(); 117 } 118 // We cannot re-randomize uid, because it would break code that statically depends on 119 // android.os.Process.myUid(), which persists between tests. 120 } 121 getRandomApplicationUid()122 static int getRandomApplicationUid() { 123 // UIDs are randomly initialized to prevent tests from depending on any given value. Tests 124 // should access the current process UID via android.os.Process::myUid(). 125 return ThreadLocalRandom.current() 126 .nextInt( 127 android.os.Process.FIRST_APPLICATION_UID, android.os.Process.LAST_APPLICATION_UID + 1); 128 } 129 } 130