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