Home | History | Annotate | Download | only in shadows
      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