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.JELLY_BEAN_MR2;
5 import static android.os.Build.VERSION_CODES.KITKAT;
6 import static android.os.Build.VERSION_CODES.LOLLIPOP;
7 import static android.os.Build.VERSION_CODES.M;
8 import static android.os.Build.VERSION_CODES.N;
9 import static android.os.Build.VERSION_CODES.O;
10 import static org.robolectric.shadow.api.Shadow.directlyOn;
11 
12 import android.annotation.Nullable;
13 import android.app.ActivityThread;
14 import android.content.BroadcastReceiver;
15 import android.content.ComponentName;
16 import android.content.ContentResolver;
17 import android.content.Context;
18 import android.content.IContentProvider;
19 import android.content.Intent;
20 import android.content.IntentFilter;
21 import android.content.IntentSender;
22 import android.content.ServiceConnection;
23 import android.os.Build.VERSION_CODES;
24 import android.os.Bundle;
25 import android.os.Environment;
26 import android.os.Handler;
27 import android.os.UserHandle;
28 import java.io.File;
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.Map;
32 import java.util.Set;
33 import org.robolectric.RuntimeEnvironment;
34 import org.robolectric.annotation.Implementation;
35 import org.robolectric.annotation.Implements;
36 import org.robolectric.annotation.RealObject;
37 import org.robolectric.annotation.Resetter;
38 import org.robolectric.shadow.api.Shadow;
39 import org.robolectric.util.ReflectionHelpers;
40 import org.robolectric.util.ReflectionHelpers.ClassParameter;
41 
42 @Implements(className = ShadowContextImpl.CLASS_NAME)
43 public class ShadowContextImpl {
44 
45   public static final String CLASS_NAME = "android.app.ContextImpl";
46   private ContentResolver contentResolver;
47 
48   @RealObject private Context realContextImpl;
49 
50   private Map<String, Object> systemServices = new HashMap<String, Object>();
51   private final Set<String> removedSystemServices = new HashSet<>();
52 
53   /**
54    * Returns the handle to a system-level service by name. If the service is not available in
55    * Roboletric, or it is set to unavailable in {@link ShadowServiceManager#setServiceAvailability},
56    * {@code null} will be returned.
57    */
58   @Implementation
59   @Nullable
getSystemService(String name)60   protected Object getSystemService(String name) {
61     if (removedSystemServices.contains(name)) {
62       return null;
63     }
64     if (!systemServices.containsKey(name)) {
65       return directlyOn(
66           realContextImpl,
67           ShadowContextImpl.CLASS_NAME,
68           "getSystemService",
69           ClassParameter.from(String.class, name));
70     }
71     return systemServices.get(name);
72   }
73 
setSystemService(String key, Object service)74   public void setSystemService(String key, Object service) {
75     systemServices.put(key, service);
76   }
77 
78   /**
79    * Makes {@link #getSystemService(String)} return {@code null} for the given system service name,
80    * mimicking a device that doesn't have that system service.
81    */
removeSystemService(String name)82   public void removeSystemService(String name) {
83     removedSystemServices.add(name);
84   }
85 
86   @Implementation
startIntentSender( IntentSender intent, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options)87   protected void startIntentSender(
88       IntentSender intent,
89       Intent fillInIntent,
90       int flagsMask,
91       int flagsValues,
92       int extraFlags,
93       Bundle options)
94       throws IntentSender.SendIntentException {
95     intent.sendIntent(realContextImpl, 0, fillInIntent, null, null, null);
96   }
97 
98   @Implementation
getClassLoader()99   protected ClassLoader getClassLoader() {
100     return this.getClass().getClassLoader();
101   }
102 
103   @Implementation
checkCallingPermission(String permission)104   protected int checkCallingPermission(String permission) {
105     return checkPermission(permission, android.os.Process.myPid(), android.os.Process.myUid());
106   }
107 
108   @Implementation
checkCallingOrSelfPermission(String permission)109   protected int checkCallingOrSelfPermission(String permission) {
110     return checkCallingPermission(permission);
111   }
112 
113   @Implementation
getContentResolver()114   protected ContentResolver getContentResolver() {
115     if (contentResolver == null) {
116       contentResolver =
117           new ContentResolver(realContextImpl) {
118             @Override
119             protected IContentProvider acquireProvider(Context c, String name) {
120               return null;
121             }
122 
123             @Override
124             public boolean releaseProvider(IContentProvider icp) {
125               return false;
126             }
127 
128             @Override
129             protected IContentProvider acquireUnstableProvider(Context c, String name) {
130               return null;
131             }
132 
133             @Override
134             public boolean releaseUnstableProvider(IContentProvider icp) {
135               return false;
136             }
137 
138             @Override
139             public void unstableProviderDied(IContentProvider icp) {}
140           };
141     }
142     return contentResolver;
143   }
144 
145   @Implementation
sendBroadcast(Intent intent)146   protected void sendBroadcast(Intent intent) {
147     getShadowInstrumentation().sendBroadcastWithPermission(intent, null, realContextImpl);
148   }
149 
150   @Implementation
sendBroadcast(Intent intent, String receiverPermission)151   protected void sendBroadcast(Intent intent, String receiverPermission) {
152     getShadowInstrumentation()
153         .sendBroadcastWithPermission(intent, receiverPermission, realContextImpl);
154   }
155 
156   @Implementation
sendOrderedBroadcast(Intent intent, String receiverPermission)157   protected void sendOrderedBroadcast(Intent intent, String receiverPermission) {
158     getShadowInstrumentation()
159         .sendOrderedBroadcastWithPermission(intent, receiverPermission, realContextImpl);
160   }
161 
162   @Implementation
sendOrderedBroadcast( Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)163   protected void sendOrderedBroadcast(
164       Intent intent,
165       String receiverPermission,
166       BroadcastReceiver resultReceiver,
167       Handler scheduler,
168       int initialCode,
169       String initialData,
170       Bundle initialExtras) {
171     getShadowInstrumentation()
172         .sendOrderedBroadcast(
173             intent,
174             receiverPermission,
175             resultReceiver,
176             scheduler,
177             initialCode,
178             initialData,
179             initialExtras,
180             realContextImpl);
181   }
182 
183   /** Behaves as {@link #sendOrderedBroadcast} and currently ignores userHandle. */
184   @Implementation(minSdk = KITKAT)
sendOrderedBroadcastAsUser( Intent intent, UserHandle userHandle, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)185   protected void sendOrderedBroadcastAsUser(
186           Intent intent,
187           UserHandle userHandle,
188           String receiverPermission,
189           BroadcastReceiver resultReceiver,
190           Handler scheduler,
191           int initialCode,
192           String initialData,
193           Bundle initialExtras) {
194     sendOrderedBroadcast(
195             intent,
196             receiverPermission,
197             resultReceiver,
198             scheduler,
199             initialCode,
200             initialData,
201             initialExtras
202     );
203   }
204 
205   /** Behaves as {@link #sendOrderedBroadcast}. Currently ignores userHandle, appOp, and options. */
206   @Implementation(minSdk = M)
sendOrderedBroadcastAsUser( Intent intent, UserHandle userHandle, String receiverPermission, int appOp, Bundle options, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)207   protected void sendOrderedBroadcastAsUser(
208           Intent intent,
209           UserHandle userHandle,
210           String receiverPermission,
211           int appOp,
212           Bundle options,
213           BroadcastReceiver resultReceiver,
214           Handler scheduler,
215           int initialCode,
216           String initialData,
217           Bundle initialExtras) {
218     sendOrderedBroadcast(
219             intent,
220             receiverPermission,
221             resultReceiver,
222             scheduler,
223             initialCode,
224             initialData,
225             initialExtras
226     );
227   }
228 
229   @Implementation
sendStickyBroadcast(Intent intent)230   protected void sendStickyBroadcast(Intent intent) {
231     getShadowInstrumentation().sendStickyBroadcast(intent, realContextImpl);
232   }
233 
234   @Implementation
checkPermission(String permission, int pid, int uid)235   protected int checkPermission(String permission, int pid, int uid) {
236     return getShadowInstrumentation().checkPermission(permission, pid, uid);
237   }
238 
239   @Implementation
registerReceiver(BroadcastReceiver receiver, IntentFilter filter)240   protected Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
241     return getShadowInstrumentation().registerReceiver(receiver, filter, realContextImpl);
242   }
243 
244   @Implementation
registerReceiver( BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler)245   protected Intent registerReceiver(
246       BroadcastReceiver receiver,
247       IntentFilter filter,
248       String broadcastPermission,
249       Handler scheduler) {
250     return getShadowInstrumentation()
251         .registerReceiver(receiver, filter, broadcastPermission, scheduler, realContextImpl);
252   }
253 
254   @Implementation(minSdk = JELLY_BEAN_MR1)
registerReceiverAsUser( BroadcastReceiver receiver, UserHandle user, IntentFilter filter, String broadcastPermission, Handler scheduler)255   protected Intent registerReceiverAsUser(
256       BroadcastReceiver receiver,
257       UserHandle user,
258       IntentFilter filter,
259       String broadcastPermission,
260       Handler scheduler) {
261     return getShadowInstrumentation()
262         .registerReceiverWithContext(
263             receiver, filter, broadcastPermission, scheduler, realContextImpl);
264   }
265 
266   @Implementation
unregisterReceiver(BroadcastReceiver broadcastReceiver)267   protected void unregisterReceiver(BroadcastReceiver broadcastReceiver) {
268     getShadowInstrumentation().unregisterReceiver(broadcastReceiver);
269   }
270 
271   @Implementation
startService(Intent service)272   protected ComponentName startService(Intent service) {
273     return getShadowInstrumentation().startService(service);
274   }
275 
276   @Implementation(minSdk = O)
startForegroundService(Intent service)277   protected ComponentName startForegroundService(Intent service) {
278     return getShadowInstrumentation().startService(service);
279   }
280 
281   @Implementation
stopService(Intent name)282   protected boolean stopService(Intent name) {
283     return getShadowInstrumentation().stopService(name);
284   }
285 
286   @Implementation
bindService(Intent intent, final ServiceConnection serviceConnection, int i)287   protected boolean bindService(Intent intent, final ServiceConnection serviceConnection, int i) {
288     return getShadowInstrumentation().bindService(intent, serviceConnection, i);
289   }
290 
291   /** Binds to a service but ignores the given UserHandle. */
292   @Implementation(minSdk = LOLLIPOP)
bindServiceAsUser( Intent intent, final ServiceConnection serviceConnection, int i, UserHandle userHandle)293   protected boolean bindServiceAsUser(
294       Intent intent, final ServiceConnection serviceConnection, int i, UserHandle userHandle) {
295     return getShadowInstrumentation().bindService(intent, serviceConnection, i);
296   }
297 
298   @Implementation
unbindService(final ServiceConnection serviceConnection)299   protected void unbindService(final ServiceConnection serviceConnection) {
300     getShadowInstrumentation().unbindService(serviceConnection);
301   }
302 
303   /**
304    * Behaves as {@link #startActivity}. The user parameter is ignored.
305    */
306   @Implementation(minSdk = LOLLIPOP)
startActivityAsUser(Intent intent, Bundle options, UserHandle user)307   protected void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
308     // TODO: Remove this once {@link com.android.server.wmActivityTaskManagerService} is
309     // properly shadowed.
310     directlyOn(
311         realContextImpl,
312         ShadowContextImpl.CLASS_NAME,
313         "startActivity",
314         ClassParameter.from(Intent.class, intent),
315         ClassParameter.from(Bundle.class, options)
316     );
317   }
318 
319   @Implementation(minSdk = JELLY_BEAN_MR1)
getUserId()320   protected int getUserId() {
321     return 0;
322   }
323 
324   @Implementation
getExternalCacheDir()325   protected File getExternalCacheDir() {
326     return Environment.getExternalStorageDirectory();
327   }
328 
329   @Implementation(maxSdk = JELLY_BEAN_MR2)
getExternalFilesDir(String type)330   protected File getExternalFilesDir(String type) {
331     return Environment.getExternalStoragePublicDirectory(type);
332   }
333 
334   @Implementation(minSdk = KITKAT)
getExternalFilesDirs(String type)335   protected File[] getExternalFilesDirs(String type) {
336     return new File[] {Environment.getExternalStoragePublicDirectory(type)};
337   }
338 
339   @Resetter
reset()340   public static void reset() {
341     String prefsCacheFieldName =
342         RuntimeEnvironment.getApiLevel() >= N ? "sSharedPrefsCache" : "sSharedPrefs";
343     Object prefsDefaultValue = RuntimeEnvironment.getApiLevel() >= KITKAT ? null : new HashMap<>();
344     Class<?> contextImplClass =
345         ReflectionHelpers.loadClass(
346             ShadowContextImpl.class.getClassLoader(), "android.app.ContextImpl");
347     ReflectionHelpers.setStaticField(contextImplClass, prefsCacheFieldName, prefsDefaultValue);
348 
349     if (RuntimeEnvironment.getApiLevel() <= VERSION_CODES.LOLLIPOP_MR1) {
350       HashMap<String, Object> fetchers =
351           ReflectionHelpers.getStaticField(contextImplClass, "SYSTEM_SERVICE_MAP");
352       Class staticServiceFetcherClass =
353           ReflectionHelpers.loadClass(
354               ShadowContextImpl.class.getClassLoader(),
355               "android.app.ContextImpl$StaticServiceFetcher");
356 
357       for (Object o : fetchers.values()) {
358         if (staticServiceFetcherClass.isInstance(o)) {
359           ReflectionHelpers.setField(staticServiceFetcherClass, o, "mCachedInstance", null);
360         }
361       }
362 
363       if (RuntimeEnvironment.getApiLevel() >= KITKAT) {
364         Class serviceFetcherClass =
365             ReflectionHelpers.loadClass(
366                 ShadowContextImpl.class.getClassLoader(), "android.app.ContextImpl$ServiceFetcher");
367 
368         Object windowServiceFetcher = fetchers.get(Context.WINDOW_SERVICE);
369         ReflectionHelpers.setField(
370             windowServiceFetcher.getClass(), windowServiceFetcher, "mDefaultDisplay", null);
371       }
372     }
373   }
374 
getShadowInstrumentation()375   private ShadowInstrumentation getShadowInstrumentation() {
376     ActivityThread activityThread = (ActivityThread) RuntimeEnvironment.getActivityThread();
377     return Shadow.extract(activityThread.getInstrumentation());
378   }
379 }
380