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.KITKAT;
5 import static android.os.Build.VERSION_CODES.LOLLIPOP;
6 import static android.os.Build.VERSION_CODES.O;
7 import static org.robolectric.shadow.api.Shadow.directlyOn;
8 
9 import android.app.ActivityManager;
10 import android.app.IActivityManager;
11 import android.content.Context;
12 import android.content.pm.ConfigurationInfo;
13 import android.os.Build.VERSION_CODES;
14 import android.os.Process;
15 import java.util.List;
16 import java.util.concurrent.CopyOnWriteArrayList;
17 import org.robolectric.RuntimeEnvironment;
18 import org.robolectric.annotation.HiddenApi;
19 import org.robolectric.annotation.Implementation;
20 import org.robolectric.annotation.Implements;
21 import org.robolectric.annotation.RealObject;
22 import org.robolectric.annotation.Resetter;
23 import org.robolectric.shadow.api.Shadow;
24 import org.robolectric.util.ReflectionHelpers;
25 
26 @Implements(ActivityManager.class)
27 public class ShadowActivityManager {
28   private int memoryClass = 16;
29   private String backgroundPackage;
30   private ActivityManager.MemoryInfo memoryInfo;
31   private final List<ActivityManager.AppTask> appTasks = new CopyOnWriteArrayList<>();
32   private final List<ActivityManager.RunningTaskInfo> tasks = new CopyOnWriteArrayList<>();
33   private final List<ActivityManager.RunningServiceInfo> services = new CopyOnWriteArrayList<>();
34   private static List<ActivityManager.RunningAppProcessInfo> processes =
35       new CopyOnWriteArrayList<>();
36   @RealObject private ActivityManager realObject;
37   private Boolean isLowRamDeviceOverride = null;
38   private int lockTaskModeState = ActivityManager.LOCK_TASK_MODE_NONE;
39 
ShadowActivityManager()40   public ShadowActivityManager() {
41     ActivityManager.RunningAppProcessInfo processInfo = new ActivityManager.RunningAppProcessInfo();
42     fillInProcessInfo(processInfo);
43     processInfo.processName = RuntimeEnvironment.application.getPackageName();
44     processInfo.pkgList = new String[] {RuntimeEnvironment.application.getPackageName()};
45     processes.add(processInfo);
46   }
47 
48   @Implementation
getMemoryClass()49   protected int getMemoryClass() {
50     return memoryClass;
51   }
52 
53   @Implementation
isUserAMonkey()54   protected static boolean isUserAMonkey() {
55     return false;
56   }
57 
58   @Implementation
getRunningTasks(int maxNum)59   protected List<ActivityManager.RunningTaskInfo> getRunningTasks(int maxNum) {
60     return tasks;
61   }
62 
63   /**
64    * For tests, returns the list of {@link android.app.ActivityManager.AppTask} set using {@link
65    * #setAppTasks(List)}. Returns empty list if nothing is set.
66    *
67    * @see #setAppTasks(List)
68    * @return List of current AppTask.
69    */
70   @Implementation(minSdk = LOLLIPOP)
getAppTasks()71   protected List<ActivityManager.AppTask> getAppTasks() {
72     return appTasks;
73   }
74 
75   @Implementation
getRunningServices(int maxNum)76   protected List<ActivityManager.RunningServiceInfo> getRunningServices(int maxNum) {
77     return services;
78   }
79 
80   @Implementation
getRunningAppProcesses()81   protected List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses() {
82     // This method is explicitly documented not to return an empty list
83     if (processes.isEmpty()) {
84       return null;
85     }
86     return processes;
87   }
88 
89   /** Returns information seeded by {@link #setProcesses}. */
90   @Implementation
getMyMemoryState(ActivityManager.RunningAppProcessInfo inState)91   protected static void getMyMemoryState(ActivityManager.RunningAppProcessInfo inState) {
92     fillInProcessInfo(inState);
93     for (ActivityManager.RunningAppProcessInfo info : processes) {
94       if (info.pid == Process.myPid()) {
95         inState.importance = info.importance;
96         inState.lru = info.lru;
97         inState.importanceReasonCode = info.importanceReasonCode;
98         inState.importanceReasonPid = info.importanceReasonPid;
99         inState.lastTrimLevel = info.lastTrimLevel;
100         inState.pkgList = info.pkgList;
101         inState.processName = info.processName;
102       }
103     }
104   }
105 
fillInProcessInfo(ActivityManager.RunningAppProcessInfo processInfo)106   private static void fillInProcessInfo(ActivityManager.RunningAppProcessInfo processInfo) {
107     processInfo.pid = Process.myPid();
108     processInfo.uid = Process.myUid();
109   }
110 
111   @HiddenApi
112   @Implementation(minSdk = JELLY_BEAN_MR1)
switchUser(int userid)113   protected boolean switchUser(int userid) {
114     ShadowUserManager shadowUserManager =
115         Shadow.extract(RuntimeEnvironment.application.getSystemService(Context.USER_SERVICE));
116     shadowUserManager.switchUser(userid);
117     return true;
118   }
119 
120   @Implementation
killBackgroundProcesses(String packageName)121   protected void killBackgroundProcesses(String packageName) {
122     backgroundPackage = packageName;
123   }
124 
125   @Implementation
getMemoryInfo(ActivityManager.MemoryInfo outInfo)126   protected void getMemoryInfo(ActivityManager.MemoryInfo outInfo) {
127     if (memoryInfo != null) {
128       outInfo.availMem = memoryInfo.availMem;
129       outInfo.lowMemory = memoryInfo.lowMemory;
130       outInfo.threshold = memoryInfo.threshold;
131       outInfo.totalMem = memoryInfo.totalMem;
132     }
133   }
134 
135   @Implementation
getDeviceConfigurationInfo()136   protected android.content.pm.ConfigurationInfo getDeviceConfigurationInfo() {
137     return new ConfigurationInfo();
138   }
139 
140   /**
141    * @param tasks List of running tasks.
142    */
setTasks(List<ActivityManager.RunningTaskInfo> tasks)143   public void setTasks(List<ActivityManager.RunningTaskInfo> tasks) {
144     this.tasks.clear();
145     this.tasks.addAll(tasks);
146   }
147 
148   /**
149    * Sets the values to be returned by {@link #getAppTasks()}.
150    *
151    * @see #getAppTasks()
152    * @param tasks List of app tasks.
153    */
setAppTasks(List<ActivityManager.AppTask> appTasks)154   public void setAppTasks(List<ActivityManager.AppTask> appTasks) {
155     this.appTasks.clear();
156     this.appTasks.addAll(appTasks);
157   }
158 
159   /**
160    * @param services List of running services.
161    */
setServices(List<ActivityManager.RunningServiceInfo> services)162   public void setServices(List<ActivityManager.RunningServiceInfo> services) {
163     this.services.clear();
164     this.services.addAll(services);
165   }
166 
167   /**
168    * @param processes List of running processes.
169    */
setProcesses(List<ActivityManager.RunningAppProcessInfo> processes)170   public void setProcesses(List<ActivityManager.RunningAppProcessInfo> processes) {
171     ShadowActivityManager.processes.clear();
172     ShadowActivityManager.processes.addAll(processes);
173   }
174 
175   /**
176    * @return Get the package name of the last background processes killed.
177    */
getBackgroundPackage()178   public String getBackgroundPackage() {
179     return backgroundPackage;
180   }
181 
182   /**
183    * @param memoryClass Set the application's memory class.
184    */
setMemoryClass(int memoryClass)185   public void setMemoryClass(int memoryClass) {
186     this.memoryClass = memoryClass;
187   }
188 
189   /**
190    * @param memoryInfo Set the application's memory info.
191    */
setMemoryInfo(ActivityManager.MemoryInfo memoryInfo)192   public void setMemoryInfo(ActivityManager.MemoryInfo memoryInfo) {
193     this.memoryInfo = memoryInfo;
194   }
195 
196   @Implementation(minSdk = O)
getService()197   protected static IActivityManager getService() {
198     return ReflectionHelpers.createNullProxy(IActivityManager.class);
199   }
200 
201   @Implementation(minSdk = KITKAT)
isLowRamDevice()202   protected boolean isLowRamDevice() {
203     if (isLowRamDeviceOverride != null) {
204       return isLowRamDeviceOverride;
205     }
206     return directlyOn(realObject, ActivityManager.class, "isLowRamDevice");
207   }
208 
209   /**
210    * Override the return value of isLowRamDevice().
211    */
setIsLowRamDevice(boolean isLowRamDevice)212   public void setIsLowRamDevice(boolean isLowRamDevice) {
213     isLowRamDeviceOverride = isLowRamDevice;
214   }
215 
216   @Implementation(minSdk = VERSION_CODES.M)
getLockTaskModeState()217   protected int getLockTaskModeState() {
218     return lockTaskModeState;
219   }
220 
221   @Implementation(minSdk = VERSION_CODES.LOLLIPOP)
isInLockTaskMode()222   protected boolean isInLockTaskMode() {
223     return getLockTaskModeState() != ActivityManager.LOCK_TASK_MODE_NONE;
224   }
225 
226   /**
227    * Sets lock task mode state to be reported by {@link ActivityManager#getLockTaskModeState}, but
228    * has no effect otherwise.
229    */
setLockTaskModeState(int lockTaskModeState)230   public void setLockTaskModeState(int lockTaskModeState) {
231     this.lockTaskModeState = lockTaskModeState;
232   }
233 
234   @Resetter
reset()235   public static void reset() {
236     processes.clear();
237   }
238 }
239