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 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 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 */ 81 public void removeSystemService(String name) { 82 removedSystemServices.add(name); 83 } 84 85 @Implementation 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 98 protected ClassLoader getClassLoader() { 99 return this.getClass().getClassLoader(); 100 } 101 102 @Implementation 103 protected int checkCallingPermission(String permission) { 104 return checkPermission(permission, android.os.Process.myPid(), android.os.Process.myUid()); 105 } 106 107 @Implementation 108 protected int checkCallingOrSelfPermission(String permission) { 109 return checkCallingPermission(permission); 110 } 111 112 @Implementation 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 145 protected void sendBroadcast(Intent intent) { 146 getShadowInstrumentation().sendBroadcastWithPermission(intent, null, realContextImpl); 147 } 148 149 @Implementation 150 protected void sendBroadcast(Intent intent, String receiverPermission) { 151 getShadowInstrumentation() 152 .sendBroadcastWithPermission(intent, receiverPermission, realContextImpl); 153 } 154 155 @Implementation 156 protected void sendOrderedBroadcast(Intent intent, String receiverPermission) { 157 getShadowInstrumentation() 158 .sendOrderedBroadcastWithPermission(intent, receiverPermission, realContextImpl); 159 } 160 161 @Implementation 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 183 protected void sendStickyBroadcast(Intent intent) { 184 getShadowInstrumentation().sendStickyBroadcast(intent, realContextImpl); 185 } 186 187 @Implementation 188 protected int checkPermission(String permission, int pid, int uid) { 189 return getShadowInstrumentation().checkPermission(permission, pid, uid); 190 } 191 192 @Implementation 193 protected Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { 194 return getShadowInstrumentation().registerReceiver(receiver, filter, realContextImpl); 195 } 196 197 @Implementation 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) 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 220 protected void unregisterReceiver(BroadcastReceiver broadcastReceiver) { 221 getShadowInstrumentation().unregisterReceiver(broadcastReceiver); 222 } 223 224 @Implementation 225 protected ComponentName startService(Intent service) { 226 return getShadowInstrumentation().startService(service); 227 } 228 229 @Implementation(minSdk = O) 230 protected ComponentName startForegroundService(Intent service) { 231 return getShadowInstrumentation().startService(service); 232 } 233 234 @Implementation 235 protected boolean stopService(Intent name) { 236 return getShadowInstrumentation().stopService(name); 237 } 238 239 @Implementation 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) 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 252 protected void unbindService(final ServiceConnection serviceConnection) { 253 getShadowInstrumentation().unbindService(serviceConnection); 254 } 255 256 @Implementation(minSdk = JELLY_BEAN_MR1) 257 protected int getUserId() { 258 return 0; 259 } 260 261 @Implementation 262 protected File getExternalCacheDir() { 263 return Environment.getExternalStorageDirectory(); 264 } 265 266 @Implementation(maxSdk = JELLY_BEAN_MR2) 267 protected File getExternalFilesDir(String type) { 268 return Environment.getExternalStoragePublicDirectory(type); 269 } 270 271 @Implementation(minSdk = KITKAT) 272 protected File[] getExternalFilesDirs(String type) { 273 return new File[] {Environment.getExternalStoragePublicDirectory(type)}; 274 } 275 276 @Resetter 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 312 private ShadowInstrumentation getShadowInstrumentation() { 313 ActivityThread activityThread = (ActivityThread) RuntimeEnvironment.getActivityThread(); 314 return Shadow.extract(activityThread.getInstrumentation()); 315 } 316 } 317