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 import org.robolectric.util.Logger; 23 24 @AutoService(SdkProvider.class) 25 @Priority(2) 26 public class AndroidLocalSdkProvider extends DefaultSdkProvider { 27 28 static { 29 System.setProperty("robolectric.offline", "true"); 30 Logger.info("Offline mode set by AndroidLocalSdkProvider.class"); 31 System.setProperty("robolectric.usePreinstrumentedJars", "false"); 32 Logger.info("Disable preinstrumented jars in AndroidLocalSdkProvider.class"); 33 } 34 35 AndroidLocalSdkProvider(DependencyResolver dependencyResolver)36 public AndroidLocalSdkProvider(DependencyResolver dependencyResolver) { 37 super(dependencyResolver); 38 } 39 40 /** 41 * In this method, a default known name for all android-all jars that are compiled in android's 42 * default build system, soong, and currently produced by /external/robolectric-shadows/android.b 43 */ findTargetJar()44 protected Path findTargetJar() throws IOException { 45 DefaultSdk localBuiltSdk = new DefaultSdk(10000, "current", "r0", "UpsideDownCake", getJdkVersion()); 46 return localBuiltSdk.getJarPath(); 47 } 48 getJdkVersion()49 protected int getJdkVersion(){ 50 String fullVersionStr = System.getProperty("java.version"); 51 if (fullVersionStr != null) { 52 String[] parts = fullVersionStr.split("\\."); 53 String versionStr = parts[0]; 54 if ("1".equals(parts[0]) && parts.length > 1) { 55 versionStr = parts[1]; 56 } 57 try { 58 return Integer.parseInt(versionStr); 59 } catch (NumberFormatException ex) { 60 throw new RuntimeException("Could not determine jdk version ", ex); 61 } 62 } 63 throw new RuntimeException("Could not determine jdk version"); 64 } 65 populateSdks(TreeMap<Integer, Sdk> knownSdks)66 protected void populateSdks(TreeMap<Integer, Sdk> knownSdks) { 67 try { 68 Path location = findTargetJar(); 69 try { 70 JarFile jarFile = new JarFile(location.toFile()); 71 final AndroidVersions.AndroidRelease release = AndroidVersionInitTools.computeReleaseVersion(jarFile); 72 if (release != null) { 73 DefaultSdk currentSdk = new ProvidedJarSdk(release.getSdkInt(), "current", 74 release.getShortCode(), location); 75 knownSdks.keySet() 76 .stream() 77 .filter(entry -> entry >= release.getSdkInt()) 78 .forEach(entry -> knownSdks.remove(entry)); 79 knownSdks.put(release.getSdkInt(), currentSdk); 80 } else { 81 throw new RuntimeException( 82 "Could not read the version of the current android-all sdk" 83 + "this prevents robolectric from determining which shadows to apply."); 84 } 85 } catch (IOException ioe) { 86 throw new RuntimeException( 87 "Could not read the version of the current android-all sdk" 88 + "this prevents robolectric from determining which shadows to apply.", 89 ioe); 90 } 91 } catch (IOException ioe) { 92 throw new RuntimeException("Could not populate robolectric sdks as dynamic config failed", ioe); 93 } 94 super.populateSdks(knownSdks); 95 } 96 97 /** 98 * Provides an sdk that is detected from a Jar path. 99 */ 100 private class ProvidedJarSdk extends DefaultSdk { 101 102 private Path mJarPath; 103 ProvidedJarSdk(int apiLevel, String androidVersion, String codeName, Path jarPath)104 ProvidedJarSdk(int apiLevel, String androidVersion, String codeName, Path jarPath) { 105 super(apiLevel, androidVersion, "", codeName, getJdkVersion()); 106 this.mJarPath = jarPath; 107 } 108 109 @Override getJarPath()110 public synchronized Path getJarPath() { 111 return mJarPath; 112 } 113 } 114 115 } 116