1 package org.robolectric.internal;
2 
3 import android.annotation.SuppressLint;
4 import java.net.URL;
5 import java.util.LinkedHashMap;
6 import java.util.Map;
7 import java.util.Objects;
8 import javax.annotation.Nonnull;
9 import org.robolectric.internal.bytecode.InstrumentationConfiguration;
10 import org.robolectric.internal.bytecode.SandboxClassLoader;
11 import org.robolectric.internal.dependency.DependencyResolver;
12 
13 @SuppressLint("NewApi")
14 public class SandboxFactory {
15   public static final SandboxFactory INSTANCE = new SandboxFactory();
16 
17   /** The factor for cache size. See {@link #CACHE_SIZE} for details. */
18   private static final int CACHE_SIZE_FACTOR = 3;
19 
20   /** We need to set the cache size of class loaders more than the number of supported APIs as different tests may have different configurations. */
21   private static final int CACHE_SIZE = SdkConfig.getSupportedApis().size() * CACHE_SIZE_FACTOR;
22 
23   // Simple LRU Cache. SdkEnvironments are unique across InstrumentationConfiguration and SdkConfig
24   private final LinkedHashMap<SandboxKey, SdkEnvironment> sdkToEnvironment = new LinkedHashMap<SandboxKey, SdkEnvironment>() {
25     @Override
26     protected boolean removeEldestEntry(Map.Entry<SandboxKey, SdkEnvironment> eldest) {
27       return size() > CACHE_SIZE;
28     }
29   };
30 
getSdkEnvironment( InstrumentationConfiguration instrumentationConfig, SdkConfig sdkConfig, boolean useLegacyResources, DependencyResolver dependencyResolver)31   public synchronized SdkEnvironment getSdkEnvironment(
32       InstrumentationConfiguration instrumentationConfig, SdkConfig sdkConfig,
33       boolean useLegacyResources, DependencyResolver dependencyResolver) {
34     SandboxKey key = new SandboxKey(sdkConfig, instrumentationConfig, useLegacyResources);
35 
36     SdkEnvironment sdkEnvironment = sdkToEnvironment.get(key);
37     if (sdkEnvironment == null) {
38       URL[] urls = dependencyResolver.getLocalArtifactUrls(sdkConfig.getAndroidSdkDependency());
39 
40       ClassLoader robolectricClassLoader = createClassLoader(instrumentationConfig, urls);
41       sdkEnvironment = createSdkEnvironment(sdkConfig, robolectricClassLoader);
42 
43       sdkToEnvironment.put(key, sdkEnvironment);
44     }
45     return sdkEnvironment;
46   }
47 
createSdkEnvironment(SdkConfig sdkConfig, ClassLoader robolectricClassLoader)48   protected SdkEnvironment createSdkEnvironment(SdkConfig sdkConfig,
49       ClassLoader robolectricClassLoader) {
50     return new SdkEnvironment(sdkConfig, robolectricClassLoader);
51   }
52 
53   @Nonnull
createClassLoader(InstrumentationConfiguration instrumentationConfig, URL... urls)54   public ClassLoader createClassLoader(InstrumentationConfiguration instrumentationConfig, URL... urls) {
55     return new SandboxClassLoader(ClassLoader.getSystemClassLoader(), instrumentationConfig, urls);
56   }
57 
58   static class SandboxKey {
59     private final SdkConfig sdkConfig;
60     private final InstrumentationConfiguration instrumentationConfiguration;
61     private final boolean useLegacyResources;
62 
SandboxKey(SdkConfig sdkConfig, InstrumentationConfiguration instrumentationConfiguration, boolean useLegacyResources)63     public SandboxKey(SdkConfig sdkConfig,
64         InstrumentationConfiguration instrumentationConfiguration, boolean useLegacyResources) {
65       this.sdkConfig = sdkConfig;
66       this.instrumentationConfiguration = instrumentationConfiguration;
67       this.useLegacyResources = useLegacyResources;
68     }
69 
70     @Override
equals(Object o)71     public boolean equals(Object o) {
72       if (this == o) {
73         return true;
74       }
75       if (o == null || getClass() != o.getClass()) {
76         return false;
77       }
78       SandboxKey that = (SandboxKey) o;
79       return useLegacyResources == that.useLegacyResources
80           && Objects.equals(sdkConfig, that.sdkConfig)
81           && Objects.equals(instrumentationConfiguration, that.instrumentationConfiguration);
82     }
83 
84     @Override
hashCode()85     public int hashCode() {
86 
87       return Objects.hash(sdkConfig, instrumentationConfiguration, useLegacyResources);
88     }
89   }
90 }
91