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