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