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