1 package org.robolectric.android.plugins;
2 
3 import com.google.auto.service.AutoService;
4 
5 import org.robolectric.internal.dependency.DependencyResolver;
6 import org.robolectric.pluginapi.Sdk;
7 import org.robolectric.pluginapi.SdkProvider;
8 import org.robolectric.plugins.DefaultSdkProvider;
9 import org.robolectric.versioning.AndroidVersionInitTools;
10 import org.robolectric.versioning.AndroidVersions;
11 
12 import java.io.IOException;
13 import java.nio.file.Path;
14 import java.util.List;
15 import java.util.Map.Entry;
16 import java.util.TreeMap;
17 import java.util.jar.JarFile;
18 import java.util.stream.Collectors;
19 
20 import javax.annotation.Priority;
21 
22 @AutoService(SdkProvider.class)
23 @Priority(2)
24 public class AndroidLocalSdkProvider extends DefaultSdkProvider {
25 
AndroidLocalSdkProvider(DependencyResolver dependencyResolver)26     public AndroidLocalSdkProvider(DependencyResolver dependencyResolver) {
27         super(dependencyResolver);
28     }
29 
30     /**
31      * In this method, a default known name for all android-all jars that are compiled in android's
32      * default build system, soong, and currently produced by /external/robolectric-shadows/android.b
33      */
findTargetJar()34     protected Path findTargetJar() throws IOException {
35         DefaultSdk localBuiltSdk = new DefaultSdk(10000, "current", "r0", "UpsideDownCake", getJdkVersion());
36         return localBuiltSdk.getJarPath();
37     }
38 
getJdkVersion()39     protected int getJdkVersion(){
40         String fullVersionStr = System.getProperty("java.version");
41         if (fullVersionStr != null) {
42             String[] parts = fullVersionStr.split("\\.");
43             String versionStr = parts[0];
44             if ("1".equals(parts[0]) && parts.length > 1) {
45                 versionStr = parts[1];
46             }
47             try {
48                 return Integer.parseInt(versionStr);
49             } catch (NumberFormatException ex) {
50                 throw new RuntimeException("Could not determine jdk version ", ex);
51             }
52         }
53         throw new RuntimeException("Could not determine jdk version");
54     }
55 
populateSdks(TreeMap<Integer, Sdk> knownSdks)56     protected void populateSdks(TreeMap<Integer, Sdk> knownSdks) {
57         try {
58             Path location = findTargetJar();
59             try {
60                 JarFile jarFile = new JarFile(location.toFile());
61                 final AndroidVersions.AndroidRelease release = AndroidVersionInitTools.computeReleaseVersion(jarFile);
62                 if (release != null) {
63                     DefaultSdk currentSdk = new ProvidedJarSdk(release.getSdkInt(), "current",
64                             release.getShortCode(), location);
65                     knownSdks.keySet()
66                             .stream()
67                             .filter(entry -> entry >= release.getSdkInt())
68                             .forEach(entry -> knownSdks.remove(entry));
69                     knownSdks.put(release.getSdkInt(), currentSdk);
70                 } else {
71                     throw new RuntimeException(
72                             "Could not read the version of the current android-all sdk"
73                                     + "this prevents robolectric from determining which shadows to apply.");
74                 }
75             } catch (IOException ioe) {
76                 throw new RuntimeException(
77                         "Could not read the version of the current android-all sdk"
78                                 + "this prevents robolectric from determining which shadows to apply.",
79                         ioe);
80             }
81         } catch (IOException ioe) {
82             throw new RuntimeException("Could not populate robolectric sdks as dynamic config failed", ioe);
83         }
84         super.populateSdks(knownSdks);
85     }
86 
87     /**
88      * Provides an sdk that is detected from a Jar path.
89      */
90     private class ProvidedJarSdk extends DefaultSdk {
91 
92         private Path mJarPath;
93 
ProvidedJarSdk(int apiLevel, String androidVersion, String codeName, Path jarPath)94         ProvidedJarSdk(int apiLevel, String androidVersion, String codeName, Path jarPath) {
95             super(apiLevel, androidVersion, "", codeName, getJdkVersion());
96             this.mJarPath = jarPath;
97         }
98 
99         @Override
getJarPath()100         public synchronized Path getJarPath() {
101             return mJarPath;
102         }
103     }
104 
105 }
106