Merge Android Pie into master

Bug: 112104996
Change-Id: Ife26a62a004cc7153634b9e2f6ca9a1c805a91a3
diff --git a/Android.mk b/Android.mk
index c708fc7..a00938b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -17,5 +17,5 @@
 
 # include jacoco-cli in the dist directory to enable running it to generate a code-coverage report
 ifeq ($(EMMA_INSTRUMENT),true)
-$(call dist-for-goals, dist_files, $(HOST_OUT_JAVA_LIBRARIES)/jacoco-cli.jar)
+$(call dist-for-goals, dist_files apps_only, $(HOST_OUT_JAVA_LIBRARIES)/jacoco-cli.jar)
 endif
diff --git a/README.android b/README.android
index cad30c6..8fa2b90 100644
--- a/README.android
+++ b/README.android
@@ -14,3 +14,10 @@
 
 1) Remove the creation of JmxRegistration in org.jacoco.agent.rt.internal.Agent.
 2) Change default OutputMode to none in org.jacoco.core.runtime.AgentOptions
+3) Change the runtime to reduce dependencies on core libraries.
+   Previously, Offline's static initializer would eagerly create an
+   Agent, a process which has lots of dependencies. With this change,
+   Offline only eagerly creates a Map<Long, ExecutionData>, which is much
+   more lightweight. The Agent is only created when it's actually
+   needed. This makes it possible to instrument a lot of more core
+   libraries without creating a circular dependency at runtime.
diff --git a/org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/internal/AgentTest.java b/org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/internal/AgentTest.java
index 45bc8bd..9f9fc78 100644
--- a/org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/internal/AgentTest.java
+++ b/org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/internal/AgentTest.java
@@ -37,6 +37,7 @@
 import org.jacoco.core.runtime.RuntimeData;
 import org.jacoco.core.tools.ExecFileLoader;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 
 /**
@@ -132,6 +133,9 @@
 	}
 
 	@Test
+	// BEGIN android-change
+	@Ignore
+	// END android-change
 	public void startup_should_register_mbean_when_enabled() throws Exception {
 		options.setJmx(true);
 		Agent agent = createAgent();
diff --git a/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Agent.java b/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Agent.java
index 667a7d0..b4dd0c1 100644
--- a/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Agent.java
+++ b/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Agent.java
@@ -45,8 +45,26 @@
 	 * @return global instance
 	 */
 	public static synchronized Agent getInstance(final AgentOptions options) {
+		// BEGIN android-change
+		return getInstance(options, new RuntimeData());
+		// END android-change
+	}
+
+	// BEGIN android-change
+	/**
+	 * Returns a global instance which is already started, reusing an existing set of runtime
+	 * data. If the method is called the first time the instance is created with the given
+	 * options.
+	 * 
+	 * @param options
+	 *            options to configure the instance
+	 * @param data
+	 *            the runtime data to reuse
+	 * @return global instance
+	 */
+	public static synchronized Agent getInstance(final AgentOptions options, RuntimeData data) {
 		if (singleton == null) {
-			final Agent agent = new Agent(options, IExceptionLogger.SYSTEM_ERR);
+			final Agent agent = new Agent(options, IExceptionLogger.SYSTEM_ERR, data);
 			agent.startup();
 			Runtime.getRuntime().addShutdownHook(new Thread() {
 				@Override
@@ -58,18 +76,26 @@
 		}
 		return singleton;
 	}
+	// END android-change
 
+	// BEGIN android-change
 	/**
-	 * Returns a global instance which is already started. If a agent has not
-	 * been initialized before this method will fail.
+	 * Returns a global instance which is already started. If an agent has not
+	 * been initialized then one will be created via {@link Offline#createAgent()}.
+	 * This will capture any data written via {@link Offline#getProbes} prior to
+	 * this call, but not subsequently.
 	 * 
 	 * @return global instance
 	 * @throws IllegalStateException
 	 *             if no Agent has been started yet
 	 */
+	// END android-change
 	public static synchronized Agent getInstance() throws IllegalStateException {
 		if (singleton == null) {
-			throw new IllegalStateException("JaCoCo agent not started.");
+			// BEGIN android-change
+			// throw new IllegalStateException("JaCoCo agent not started.");
+			singleton = Offline.createAgent();
+			// END android-change
 		}
 		return singleton;
 	}
@@ -93,10 +119,28 @@
 	 *            logger used by this agent
 	 */
 	Agent(final AgentOptions options, final IExceptionLogger logger) {
+		// BEGIN android-change
+		this(options, logger, new RuntimeData());
+		// END android-change
+	}
+
+	// BEGIN android-change
+	/**
+	 * Creates a new agent with the given agent options, reusing the given runtime data.
+	 *
+	 * @param options
+	 *            agent options
+	 * @param logger
+	 *            logger used by this agent
+	 * @param data
+	 *            the runtime data to reuse
+	 */
+	private Agent(final AgentOptions options, final IExceptionLogger logger, RuntimeData data) {
 		this.options = options;
 		this.logger = logger;
-		this.data = new RuntimeData();
+		this.data = data;
 	}
+	// END android-change
 
 	/**
 	 * Returns the runtime data object created by this agent
diff --git a/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Offline.java b/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Offline.java
index 8b8b40c..7eac19f 100644
--- a/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Offline.java
+++ b/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Offline.java
@@ -11,8 +11,12 @@
  *******************************************************************************/
 package org.jacoco.agent.rt.internal;
 
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Properties;
 
+import org.jacoco.core.data.ExecutionData;
+import org.jacoco.core.data.ExecutionDataStore;
 import org.jacoco.core.runtime.AgentOptions;
 import org.jacoco.core.runtime.RuntimeData;
 
@@ -22,14 +26,19 @@
  */
 public final class Offline {
 
-	private static final RuntimeData DATA;
+	// BEGIN android-change
+	// private static final RuntimeData DATA;
+	private static final Map<Long, ExecutionData> DATA = new HashMap<Long, ExecutionData>();
+	// END android-change
 	private static final String CONFIG_RESOURCE = "/jacoco-agent.properties";
 
-	static {
-		final Properties config = ConfigLoader.load(CONFIG_RESOURCE,
-				System.getProperties());
-		DATA = Agent.getInstance(new AgentOptions(config)).getData();
-	}
+	// BEGIN android-change
+	// static {
+	//	 final Properties config = ConfigLoader.load(CONFIG_RESOURCE,
+	//			System.getProperties());
+	//	 DATA = Agent.getInstance(new AgentOptions(config)).getData();
+	// }
+	// END android-change
 
 	private Offline() {
 		// no instances
@@ -48,8 +57,41 @@
 	 */
 	public static boolean[] getProbes(final long classid,
 			final String classname, final int probecount) {
-		return DATA.getExecutionData(Long.valueOf(classid), classname,
-				probecount).getProbes();
+		// BEGIN android-change
+		// return DATA.getExecutionData(Long.valueOf(classid), classname,
+		//		probecount).getProbes();
+		synchronized (DATA) {
+			ExecutionData entry = DATA.get(classid);
+			if (entry == null) {
+				entry = new ExecutionData(classid, classname, probecount);
+				DATA.put(classid, entry);
+			} else {
+				entry.assertCompatibility(classid, classname, probecount);
+			}
+			return entry.getProbes();
+		}
+		// END android-change
 	}
 
+	// BEGIN android-change
+	/**
+	 * Creates a default agent, using config loaded from the classpath resource and the system
+	 * properties, and a runtime data instance populated with the execution data accumulated by
+	 * the probes up until this call is made (subsequent probe updates will not be reflected in
+	 * this agent).
+	 *
+	 * @return the new agent
+	 */
+	static Agent createAgent() {
+		final Properties config = ConfigLoader.load(CONFIG_RESOURCE,
+				System.getProperties());
+		synchronized (DATA) {
+			ExecutionDataStore store = new ExecutionDataStore();
+			for (ExecutionData data : DATA.values()) {
+				store.put(data);
+			}
+			return Agent.getInstance(new AgentOptions(config), new RuntimeData(store));
+		}
+	}
+	// END android-change
 }
diff --git a/org.jacoco.core.test/src/org/jacoco/core/runtime/AgentOptionsTest.java b/org.jacoco.core.test/src/org/jacoco/core/runtime/AgentOptionsTest.java
index f65142e..155e3e8 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/runtime/AgentOptionsTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/runtime/AgentOptionsTest.java
@@ -47,7 +47,10 @@
 		assertFalse(options.getInclNoLocationClasses());
 		assertNull(options.getSessionId());
 		assertTrue(options.getDumpOnExit());
-		assertEquals(AgentOptions.OutputMode.file, options.getOutput());
+		// BEGIN android-change
+		// assertEquals(AgentOptions.OutputMode.file, options.getOutput());
+		assertEquals(AgentOptions.OutputMode.none, options.getOutput());
+		// END android-change
 		assertEquals(AgentOptions.DEFAULT_ADDRESS, options.getAddress());
 		assertEquals(AgentOptions.DEFAULT_PORT, options.getPort());
 		assertNull(options.getClassDumpDir());
diff --git a/org.jacoco.core/src/org/jacoco/core/runtime/RuntimeData.java b/org.jacoco.core/src/org/jacoco/core/runtime/RuntimeData.java
index ff249e7..74a51dc 100644
--- a/org.jacoco.core/src/org/jacoco/core/runtime/RuntimeData.java
+++ b/org.jacoco.core/src/org/jacoco/core/runtime/RuntimeData.java
@@ -37,10 +37,24 @@
 	 * Creates a new runtime.
 	 */
 	public RuntimeData() {
-		store = new ExecutionDataStore();
+		// BEGIN android-change
+		this(new ExecutionDataStore());
+		// END android-change
+	}
+
+	// BEGIN android-change
+	/**
+	 * Creates a new runtime, reusing an existing {@link ExecutionDataStore}.
+	 * 
+	 * @param store
+	 *            the store to reuse
+	 */
+	public RuntimeData(ExecutionDataStore store) {
+		this.store = store;
 		sessionId = "<none>";
 		startTimeStamp = System.currentTimeMillis();
 	}
+	// END android-change
 
 	/**
 	 * Sets a session identifier for this runtime. The identifier is used when