Snap for 7483611 from e806b919fa39dea80bb0aa330d568c5464407e9e to mainline-neuralnetworks-release

Change-Id: I9df3c2333ac69099837fe9d6ed412b2480b5a237
diff --git a/Android.bp b/Android.bp
index 0641bde..726af1d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -18,6 +18,44 @@
 //
 // Note: this is only intended to be used for the platform development. This is *not* intended
 // to be used in the SDK where apps can use the official jacoco release.
+package {
+    default_applicable_licenses: ["external_jacoco_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+//
+// large-scale-change included anything that looked like it might be a license
+// text as a license_text. e.g. LICENSE, NOTICE, COPYING etc.
+//
+// Please consider removing redundant or irrelevant files from 'license_text:'.
+// See: http://go/android-license-faq
+license {
+    name: "external_jacoco_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+        "SPDX-license-identifier-BSD",
+        "SPDX-license-identifier-EPL",
+        "SPDX-license-identifier-MIT",
+    ],
+    license_text: [
+        "LICENSE.md",
+        "NOTICE",
+    ],
+}
+
 java_library {
     name: "jacocoagent",
     installable: true,
@@ -66,7 +104,10 @@
 // Generates stubs containing the classes that will be referenced by instrumented bytecode.
 droidstubs {
     name: "jacoco-stubs-gen",
-    srcs: ["org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Offline.java"],
+    srcs: [
+        "org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Offline.java",
+        "org.jacoco.core/src/org/jacoco/core/data/IExecutionData.java",
+    ],
 }
 
 // A stubs target containing the parts of JaCoCo that we need to add to the hidden API whitelist.
diff --git a/METADATA b/METADATA
index 43947c9..16d6e52 100644
--- a/METADATA
+++ b/METADATA
@@ -10,6 +10,7 @@
     value: "https://github.com/jacoco/jacoco.git"
   }
   version: "v0.8.4"
+  license_type: RECIPROCAL
   last_upgrade_date {
     year: 2019
     month: 5
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 69a9909..99a054e 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,12 +11,21 @@
  *******************************************************************************/
 package org.jacoco.agent.rt.internal;
 
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.channels.FileChannel;
+import java.nio.ByteBuffer;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Properties;
 
 import org.jacoco.core.data.ExecutionData;
+import org.jacoco.core.data.ExecutionDataWriter;
+import org.jacoco.core.data.ExecutionDataDelegate;
+import org.jacoco.core.data.IExecutionData;
 import org.jacoco.core.data.ExecutionDataStore;
+import org.jacoco.core.data.MappedExecutionData;
 import org.jacoco.core.runtime.AgentOptions;
 import org.jacoco.core.runtime.RuntimeData;
 
@@ -28,7 +37,8 @@
 
 	// BEGIN android-change
 	// private static final RuntimeData DATA;
-	private static final Map<Long, ExecutionData> DATA = new HashMap<Long, ExecutionData>();
+	private static final Map<Long, ExecutionDataDelegate> DATA = new HashMap<Long, ExecutionDataDelegate>();
+	private static FileChannel CHANNEL;
 	// END android-change
 	private static final String CONFIG_RESOURCE = "/jacoco-agent.properties";
 
@@ -44,6 +54,7 @@
 		// no instances
 	}
 
+	// BEGIN android-change
 	/**
 	 * API for offline instrumented classes.
 	 * 
@@ -53,27 +64,69 @@
 	 *            VM class name
 	 * @param probecount
 	 *            probe count for this class
-	 * @return probe array instance for this class
+	 * @return IExecutionData instance for this class
 	 */
-	public static boolean[] getProbes(final long classid,
+	public static IExecutionData getExecutionData(final long classid,
 			final String classname, final int probecount) {
-		// BEGIN android-change
 		// return DATA.getExecutionData(Long.valueOf(classid), classname,
 		//		probecount).getProbes();
 		synchronized (DATA) {
-			ExecutionData entry = DATA.get(classid);
+			ExecutionDataDelegate entry = DATA.get(classid);
 			if (entry == null) {
-				entry = new ExecutionData(classid, classname, probecount);
+				entry = new ExecutionDataDelegate(
+						classid, classname, probecount, CHANNEL);
 				DATA.put(classid, entry);
 			} else {
 				entry.assertCompatibility(classid, classname, probecount);
 			}
-			return entry.getProbes();
+			return entry;
 		}
-		// END android-change
 	}
 
-	// BEGIN android-change
+	/**
+	 * Enables memory-mapped execution data and converts existing
+	 * {@link ExecutionDataDelegate}s.
+	 */
+	public static void enableMemoryMappedData() {
+		try {
+			prepareFile(getPid());
+			for (ExecutionDataDelegate data : DATA.values()) {
+				data.convert(CHANNEL);
+			}
+		}  catch (IOException e) {
+			// TODO(olivernguyen): Add logging to debug issues more easily.
+		}
+	}
+
+	/**
+	 * Creates the output file that will be mapped for probe data.
+	 */
+	private static void prepareFile(int pid) throws IOException {
+		// Write header information to the file.
+		ByteBuffer headerBuffer = ByteBuffer.allocate(5);
+		headerBuffer.put(ExecutionDataWriter.BLOCK_HEADER);
+		headerBuffer.putChar(ExecutionDataWriter.MAGIC_NUMBER);
+		headerBuffer.putChar(ExecutionDataWriter.FORMAT_VERSION);
+		headerBuffer.flip();
+
+		// If this file already exists (due to pid re-usage), the previous coverage data
+		// will be lost when the file is overwritten.
+		File outputFile = new File("/data/misc/trace/jacoco-" + pid + ".mm.ec");
+		CHANNEL = new RandomAccessFile(outputFile, "rw").getChannel();
+		synchronized (CHANNEL) {
+			CHANNEL.write(headerBuffer);
+		}
+	}
+
+	/**
+	 * Helper function to determine the pid of this process.
+	 */
+	private static int getPid() throws IOException {
+		// Read /proc/self and resolve it to obtain its pid.
+		return Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());
+	}
+	// END 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
@@ -87,7 +140,7 @@
 				System.getProperties());
 		synchronized (DATA) {
 			ExecutionDataStore store = new ExecutionDataStore();
-			for (ExecutionData data : DATA.values()) {
+			for (IExecutionData data : DATA.values()) {
 				store.put(data);
 			}
 			return Agent.getInstance(new AgentOptions(config), new RuntimeData(store));
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/commands/ExecInfo.java b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/ExecInfo.java
index cacf4cb..4519d28 100644
--- a/org.jacoco.cli/src/org/jacoco/cli/internal/commands/ExecInfo.java
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/ExecInfo.java
@@ -20,7 +20,7 @@
 import java.util.List;
 
 import org.jacoco.cli.internal.Command;
-import org.jacoco.core.data.ExecutionData;
+import org.jacoco.core.data.IExecutionData;
 import org.jacoco.core.data.ExecutionDataReader;
 import org.jacoco.core.data.IExecutionDataVisitor;
 import org.jacoco.core.data.ISessionInfoVisitor;
@@ -68,13 +68,15 @@
 			}
 		});
 		reader.setExecutionDataVisitor(new IExecutionDataVisitor() {
-			public void visitClassExecution(final ExecutionData data) {
+			// BEGIN android-change
+			public void visitClassExecution(final IExecutionData data) {
 				out.printf("%016x  %3d of %3d   %s%n",
 						Long.valueOf(data.getId()),
-						Integer.valueOf(getHitCount(data.getProbes())),
-						Integer.valueOf(data.getProbes().length),
+						Integer.valueOf(getHitCount(data.getProbesCopy())),
+						Integer.valueOf(data.getProbeCount()),
 						data.getName());
 			}
+			// END android-change
 		});
 		reader.read();
 		in.close();
diff --git a/org.jacoco.core/src/org/jacoco/core/analysis/Analyzer.java b/org.jacoco.core/src/org/jacoco/core/analysis/Analyzer.java
index 76b7be3..7759b89 100644
--- a/org.jacoco.core/src/org/jacoco/core/analysis/Analyzer.java
+++ b/org.jacoco.core/src/org/jacoco/core/analysis/Analyzer.java
@@ -20,7 +20,7 @@
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 
-import org.jacoco.core.data.ExecutionData;
+import org.jacoco.core.data.IExecutionData;
 import org.jacoco.core.data.ExecutionDataStore;
 import org.jacoco.core.internal.ContentTypeDetector;
 import org.jacoco.core.internal.InputStreams;
@@ -78,16 +78,18 @@
 	 */
 	private ClassVisitor createAnalyzingVisitor(final long classid,
 			final String className) {
-		final ExecutionData data = executionData.get(classid);
+		// BEGIN android-change
+		final IExecutionData data = executionData.get(classid);
 		final boolean[] probes;
 		final boolean noMatch;
 		if (data == null) {
 			probes = null;
 			noMatch = executionData.contains(className);
 		} else {
-			probes = data.getProbes();
+			probes = data.getProbesCopy();
 			noMatch = false;
 		}
+		// END android-change
 		final ClassCoverageImpl coverage = new ClassCoverageImpl(className,
 				classid, noMatch);
 		final ClassAnalyzer analyzer = new ClassAnalyzer(coverage, probes,
diff --git a/org.jacoco.core/src/org/jacoco/core/data/ExecutionData.java b/org.jacoco.core/src/org/jacoco/core/data/ExecutionData.java
index d98775e..a728e03 100644
--- a/org.jacoco.core/src/org/jacoco/core/data/ExecutionData.java
+++ b/org.jacoco.core/src/org/jacoco/core/data/ExecutionData.java
@@ -20,7 +20,9 @@
  * has to be taken about the probe data array of type <code>boolean[]</code>
  * which can be modified.
  */
-public final class ExecutionData {
+// BEGIN android-change
+public final class ExecutionData implements IExecutionData {
+// END android-change
 
 	private final long id;
 
@@ -81,17 +83,45 @@
 		return name;
 	}
 
+	// BEGIN android-change
 	/**
-	 * Returns the execution data probes. A value of <code>true</code> indicates
-	 * that the corresponding probe was executed.
+	 * Returns a copy of the current probe values.
+	 *
+	 * @return copy of the probe array
+	 */
+	public boolean[] getProbesCopy() {
+		return Arrays.copyOf(probes, probes.length);
+	}
+
+	/**
+	 * The number of probes in this ExecutionData.
+	 *
+	 * @return the number of probes
+	 */
+	public int getProbeCount() {
+		return probes.length;
+	}
+
+	/**
+	 * Returns the execution data probe for a given index. A value of
+	 * <code>true</code> indicates that the corresponding probe was
+	 * executed.
 	 * 
 	 * @return probe data
 	 */
-	public boolean[] getProbes() {
-		return probes;
+	public boolean getProbe(final int index) {
+		return probes[index];
 	}
 
 	/**
+	 * Sets the execution data probe for a given index to <code>true</code>.
+	 */
+	public void setProbe(final int index) {
+		probes[index] = true;
+	}
+	// END android-change
+
+	/**
 	 * Sets all probes to <code>false</code>.
 	 */
 	public void reset() {
@@ -127,7 +157,9 @@
 	 * @param other
 	 *            execution data to merge
 	 */
-	public void merge(final ExecutionData other) {
+	// BEGIN android-change
+	public void merge(final IExecutionData other) {
+	// END android-change
 		merge(other, true);
 	}
 
@@ -154,10 +186,12 @@
 	 * @param flag
 	 *            merge mode
 	 */
-	public void merge(final ExecutionData other, final boolean flag) {
+	public void merge(final IExecutionData other, final boolean flag) {
+		// BEGIN android-change
 		assertCompatibility(other.getId(), other.getName(),
-				other.getProbes().length);
-		final boolean[] otherData = other.getProbes();
+				other.getProbeCount());
+		final boolean[] otherData = other.getProbesCopy();
+		// END android-change
 		for (int i = 0; i < probes.length; i++) {
 			if (otherData[i]) {
 				probes[i] = flag;
diff --git a/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataDelegate.java b/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataDelegate.java
new file mode 100644
index 0000000..7cbe989
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataDelegate.java
@@ -0,0 +1,197 @@
+// BEGIN android-change
+package org.jacoco.core.data;
+
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+
+/**
+ * Class that delegates calls to {@link IExecutionData} interface methods to another instance.
+ * Also has the ability to convert the underlying implementation to a {@link MappedExecutionData}
+ * instance.
+ */
+public class ExecutionDataDelegate implements IExecutionData {
+	private IExecutionData delegate;
+
+	public ExecutionDataDelegate(
+		final long id, final String name, final int probeCount, final FileChannel channel) {
+		if ((channel != null) && canMapData(name)) {
+			try {
+				delegate = new MappedExecutionData(id, name, probeCount, channel);
+			} catch (IOException e) {
+				delegate = new ExecutionData(id, name, probeCount);
+			}
+		} else {
+			delegate = new ExecutionData(id, name, probeCount);
+		}
+	}
+
+	/**
+	 * Converts the existing delegate into a {@link MappedExecutionData} instance if it is not
+	 * already one, and copies any probe data into the new instance.
+	 */
+	public synchronized void convert(final FileChannel channel) throws IOException {
+		if (!(delegate instanceof MappedExecutionData) && canMapData(getName())) {
+			IExecutionData newDelegate = new MappedExecutionData(
+				getId(), getName(), getProbeCount(), channel);
+			newDelegate.merge(delegate);
+			delegate = newDelegate;
+		}
+	}
+
+	/**
+	 * Determines if a class can be converted to memory-mapped.
+	 */
+	private static boolean canMapData(final String name) {
+		if (name.startsWith("android/app/")
+			|| name.startsWith("android/os/")
+			|| name.startsWith("com/android/internal/util/")
+			|| name.startsWith("java/")) {
+			return false;
+		}
+		return true;
+	}
+
+	/**
+	 * Return the unique identifier for this class. The identifier is the CRC64
+	 * checksum of the raw class file definition.
+	 *
+	 * @return class identifier
+	 */
+	public long getId() {
+		return delegate.getId();
+	}
+
+	/**
+	 * The VM name of the class.
+	 *
+	 * @return VM name
+	 */
+	public String getName() {
+		return delegate.getName();
+	}
+
+	/**
+	 * The number of instrumentation probes for this class.
+	 *
+	 * @return number of probes
+	 */
+	public int getProbeCount() {
+		return delegate.getProbeCount();
+	}
+
+	/**
+	 * Returns a copy of the probe data as a boolean array.
+	 *
+	 * Changes to the returned array will not be reflected in the execution data.
+	 *
+	 * @return copy of the probe data
+	 */
+	public boolean[] getProbesCopy() {
+		return delegate.getProbesCopy();
+	}
+
+	/**
+	 * Sets all probes to <code>false</code>.
+	 */
+	public void reset() {
+		delegate.reset();
+	}
+
+	/**
+	 * Checks whether any probe has been hit.
+	 *
+	 * @return <code>true</code>, if at least one probe has been hit
+	 */
+	public boolean hasHits() {
+		return delegate.hasHits();
+	}
+
+	/**
+	 * Merges the given execution data into the probe data of this object. I.e.
+	 * a probe entry in this object is marked as executed (<code>true</code>) if
+	 * this probe or the corresponding other probe was executed. So the result
+	 * is
+	 *
+	 * <pre>
+	 * A or B
+	 * </pre>
+	 *
+	 * The probe array of the other object is not modified.
+	 *
+	 * @param other
+	 *            execution data to merge
+	 */
+	public void merge(final IExecutionData other) {
+		delegate.merge(other);
+	}
+
+	/**
+	 * Merges the given execution data into the probe data of this object. A
+	 * probe in this object is set to the value of <code>flag</code> if the
+	 * corresponding other probe was executed. For <code>flag==true</code> this
+	 * corresponds to
+	 *
+	 * <pre>
+	 * A or B
+	 * </pre>
+	 *
+	 * For <code>flag==false</code> this can be considered as a subtraction
+	 *
+	 * <pre>
+	 * A and not B
+	 * </pre>
+	 *
+	 * The probe array of the other object is not modified.
+	 *
+	 * @param other
+	 *            execution data to merge
+	 * @param flag
+	 *            merge mode
+	 */
+	public void merge(final IExecutionData other, boolean flag) {
+		delegate.merge(other, flag);
+	}
+
+	/**
+	 * Asserts that this execution data object is compatible with the given
+	 * parameters. The purpose of this check is to detect a very unlikely class
+	 * id collision.
+	 *
+	 * @param id
+	 *            other class id, must be the same
+	 * @param name
+	 *            other name, must be equal to this name
+	 * @param probecount
+	 *            probe data length, must be the same as for this data
+	 * @throws IllegalStateException
+	 *             if the given parameters do not match this instance
+	 */
+	public void assertCompatibility(final long id, final String name, final int probeCount)
+			throws IllegalStateException {
+		delegate.assertCompatibility(id, name, probeCount);
+	}
+
+	/**
+	 * Returns the execution data probe for a given index. A value of
+	 * <code>true</code> indicates that the corresponding probe was
+	 * executed.
+	 *
+	 * @param index  the probe's index to look up
+	 *
+	 * @return probe data
+	 */
+	public boolean getProbe(final int index) {
+		return delegate.getProbe(index);
+	}
+
+	/**
+	 * Sets the execution data probe at the given index to <code>true</code>.
+	 *
+	 * @param index  the probe's index to set
+	 * @param value  the value to set the probe to
+	 */
+	public void setProbe(final int index) {
+		delegate.setProbe(index);
+	}
+ }
+ // END android-change
diff --git a/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataStore.java b/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataStore.java
index 3e567a3..ce0bec4 100644
--- a/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataStore.java
+++ b/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataStore.java
@@ -28,7 +28,9 @@
  */
 public final class ExecutionDataStore implements IExecutionDataVisitor {
 
-	private final Map<Long, ExecutionData> entries = new HashMap<Long, ExecutionData>();
+	// BEGIN android-change
+	private final Map<Long, IExecutionData> entries = new HashMap<Long, IExecutionData>();
+	// END android-change
 
 	private final Set<String> names = new HashSet<String>();
 
@@ -44,9 +46,11 @@
 	 *             to a corresponding one, that is already contained
 	 * @see ExecutionData#assertCompatibility(long, String, int)
 	 */
-	public void put(final ExecutionData data) throws IllegalStateException {
+	// BEGIN android-change
+	public void put(final IExecutionData data) throws IllegalStateException {
 		final Long id = Long.valueOf(data.getId());
-		final ExecutionData entry = entries.get(id);
+		final IExecutionData entry = entries.get(id);
+		// END android-change
 		if (entry == null) {
 			entries.put(id, data);
 			names.add(data.getName());
@@ -68,9 +72,11 @@
 	 *             to a corresponding one, that is already contained
 	 * @see ExecutionData#assertCompatibility(long, String, int)
 	 */
-	public void subtract(final ExecutionData data) throws IllegalStateException {
+	// BEGIN android-change
+	public void subtract(final IExecutionData data) throws IllegalStateException {
 		final Long id = Long.valueOf(data.getId());
-		final ExecutionData entry = entries.get(id);
+		final IExecutionData entry = entries.get(id);
+		// END android-change
 		if (entry != null) {
 			entry.merge(data, false);
 		}
@@ -84,7 +90,9 @@
 	 * @see #subtract(ExecutionData)
 	 */
 	public void subtract(final ExecutionDataStore store) {
-		for (final ExecutionData data : store.getContents()) {
+		// BEGIN android-change
+		for (final IExecutionData data : store.getContents()) {
+		// END android-change
 			subtract(data);
 		}
 	}
@@ -97,7 +105,9 @@
 	 *            class id
 	 * @return execution data or <code>null</code>
 	 */
-	public ExecutionData get(final long id) {
+	// BEGIN android-change
+	public IExecutionData get(final long id) {
+	// END android-change
 		return entries.get(Long.valueOf(id));
 	}
 
@@ -126,9 +136,11 @@
 	 *            probe data length
 	 * @return execution data
 	 */
-	public ExecutionData get(final Long id, final String name,
+	// BEGIN android-change
+	public IExecutionData get(final Long id, final String name,
 			final int probecount) {
-		ExecutionData entry = entries.get(id);
+		IExecutionData entry = entries.get(id);
+		// END android-change
 		if (entry == null) {
 			entry = new ExecutionData(id.longValue(), name, probecount);
 			entries.put(id, entry);
@@ -144,7 +156,9 @@
 	 * execution data objects itself are not removed.
 	 */
 	public void reset() {
-		for (final ExecutionData executionData : this.entries.values()) {
+		// BEGIN android-change
+		for (final IExecutionData executionData : this.entries.values()) {
+		// END android-change
 			executionData.reset();
 		}
 	}
@@ -154,9 +168,11 @@
 	 * 
 	 * @return current contents
 	 */
-	public Collection<ExecutionData> getContents() {
-		return new ArrayList<ExecutionData>(entries.values());
+	// BEGIN android-change
+	public Collection<IExecutionData> getContents() {
+		return new ArrayList<IExecutionData>(entries.values());
 	}
+	// END android-change
 
 	/**
 	 * Writes the content of the store to the given visitor interface.
@@ -165,14 +181,18 @@
 	 *            interface to write content to
 	 */
 	public void accept(final IExecutionDataVisitor visitor) {
-		for (final ExecutionData data : getContents()) {
+		// BEGIN android-change
+		for (final IExecutionData data : getContents()) {
+		// END android-change
 			visitor.visitClassExecution(data);
 		}
 	}
 
 	// === IExecutionDataVisitor ===
 
-	public void visitClassExecution(final ExecutionData data) {
+	// BEGIN android-change
+	public void visitClassExecution(final IExecutionData data) {
+	// END android-change
 		put(data);
 	}
 }
diff --git a/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataWriter.java b/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataWriter.java
index e697dda..bdf3445 100644
--- a/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataWriter.java
+++ b/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataWriter.java
@@ -94,13 +94,17 @@
 		}
 	}
 
-	public void visitClassExecution(final ExecutionData data) {
+	// BEGIN android-change
+	public void visitClassExecution(final IExecutionData data) {
+	// END android-change
 		if (data.hasHits()) {
 			try {
 				out.writeByte(BLOCK_EXECUTIONDATA);
 				out.writeLong(data.getId());
 				out.writeUTF(data.getName());
-				out.writeBooleanArray(data.getProbes());
+				// BEGIN android-change
+				out.writeBooleanArray(data.getProbesCopy());
+				// END android-change
 			} catch (final IOException e) {
 				throw new RuntimeException(e);
 			}
diff --git a/org.jacoco.core/src/org/jacoco/core/data/IExecutionData.java b/org.jacoco.core/src/org/jacoco/core/data/IExecutionData.java
new file mode 100644
index 0000000..289c372
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/data/IExecutionData.java
@@ -0,0 +1,129 @@
+// BEGIN android-change
+package org.jacoco.core.data;
+
+/**
+ * Interface for interacting with execution data for a single Java class.
+ */
+public interface IExecutionData {
+
+	/**
+	 * Return the unique identifier for this class. The identifier is the CRC64
+	 * checksum of the raw class file definition.
+	 * 
+	 * @return class identifier
+	 */
+	public abstract long getId();
+
+	/**
+	 * The VM name of the class.
+	 * 
+	 * @return VM name
+	 */
+	public abstract String getName();
+
+	/**
+	 * The number of instrumentation probes for this class.
+	 * 
+	 * @return number of probes
+	 */
+	public abstract int getProbeCount();
+
+	/**
+	 * Returns a copy of the probe data as a boolean array.
+	 *
+	 * Changes to the returned array will not be reflected in the execution data.
+	 * 
+	 * @return copy of the probe data
+	 */
+	public abstract boolean[] getProbesCopy();
+
+	/**
+	 * Sets all probes to <code>false</code>.
+	 */
+	public abstract void reset();
+
+	/**
+	 * Checks whether any probe has been hit.
+	 * 
+	 * @return <code>true</code>, if at least one probe has been hit
+	 */
+	public abstract boolean hasHits();
+
+	/**
+	 * Merges the given execution data into the probe data of this object. I.e.
+	 * a probe entry in this object is marked as executed (<code>true</code>) if
+	 * this probe or the corresponding other probe was executed. So the result
+	 * is
+	 * 
+	 * <pre>
+	 * A or B
+	 * </pre>
+	 * 
+	 * The probe array of the other object is not modified.
+	 * 
+	 * @param other
+	 *            execution data to merge
+	 */
+	public abstract void merge(final IExecutionData other);
+
+	/**
+	 * Merges the given execution data into the probe data of this object. A
+	 * probe in this object is set to the value of <code>flag</code> if the
+	 * corresponding other probe was executed. For <code>flag==true</code> this
+	 * corresponds to
+	 * 
+	 * <pre>
+	 * A or B
+	 * </pre>
+	 * 
+	 * For <code>flag==false</code> this can be considered as a subtraction
+	 * 
+	 * <pre>
+	 * A and not B
+	 * </pre>
+	 * 
+	 * The probe array of the other object is not modified.
+	 * 
+	 * @param other
+	 *            execution data to merge
+	 * @param flag
+	 *            merge mode
+	 */
+	public abstract void merge(final IExecutionData other, boolean flag);
+
+	/**
+	 * Asserts that this execution data object is compatible with the given
+	 * parameters. The purpose of this check is to detect a very unlikely class
+	 * id collision.
+	 * 
+	 * @param id
+	 *            other class id, must be the same
+	 * @param name
+	 *            other name, must be equal to this name
+	 * @param probecount
+	 *            probe data length, must be the same as for this data
+	 * @throws IllegalStateException
+	 *             if the given parameters do not match this instance
+	 */
+	public abstract void assertCompatibility(final long id, final String name, final int probeCount) throws IllegalStateException;
+
+	/**
+	 * Returns the execution data probe for a given index. A value of
+	 * <code>true</code> indicates that the corresponding probe was
+	 * executed.
+	 *
+	 * @param index  the probe's index to look up
+	 * 
+	 * @return probe data
+	 */
+	public abstract boolean getProbe(final int index);
+
+	/**
+	 * Sets the execution data probe at the given index to <code>true</code>.
+	 *
+	 * @param index  the probe's index to set
+	 * @param value  the value to set the probe to
+	 */
+	public abstract void setProbe(final int index);
+}
+// END android-change
diff --git a/org.jacoco.core/src/org/jacoco/core/data/IExecutionDataVisitor.java b/org.jacoco.core/src/org/jacoco/core/data/IExecutionDataVisitor.java
index 6cea7c5..14eabbe 100644
--- a/org.jacoco.core/src/org/jacoco/core/data/IExecutionDataVisitor.java
+++ b/org.jacoco.core/src/org/jacoco/core/data/IExecutionDataVisitor.java
@@ -24,6 +24,8 @@
 	 * @param data
 	 *            execution data for a class
 	 */
-	void visitClassExecution(ExecutionData data);
+	// BEGIN android-change
+	void visitClassExecution(IExecutionData data);
+	// END android-change
 
 }
diff --git a/org.jacoco.core/src/org/jacoco/core/data/MappedExecutionData.java b/org.jacoco.core/src/org/jacoco/core/data/MappedExecutionData.java
new file mode 100644
index 0000000..7e74d17
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/data/MappedExecutionData.java
@@ -0,0 +1,304 @@
+// BEGIN android-change
+package org.jacoco.core.data;
+
+import static java.lang.String.format;
+
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+import java.nio.charset.Charset;
+import java.nio.ByteBuffer;
+import java.nio.MappedByteBuffer;
+
+import org.jacoco.core.data.ExecutionDataWriter;
+
+/**
+ * Memory-mapped execution data implementation.
+ *
+ * Stores the probe data in a section of a memory-mapped file, so that it will be available without
+ * requiring a flush of the coverage data.
+ */
+public final class MappedExecutionData implements IExecutionData {
+	private final long id;
+	private final String name;
+	private final int probeCount;
+
+	// Stores the memory-mapped byte buffer containing the probe data, packed as bits.
+	private MappedByteBuffer probeBuffer;
+
+	/**
+	 * Creates the mapped execution data.
+	 */
+	public MappedExecutionData(
+		final long id, final String name, final int probeCount, final FileChannel channel)
+			throws IOException {
+		this.id = id;
+		this.name = name;
+		this.probeCount = probeCount;
+
+		createMemoryMappedProbeArray(id, name, probeCount, channel);
+	}
+
+	public MappedExecutionData(
+			final long id,
+			final String name,
+			final boolean[] probes,
+			final FileChannel channel) throws IOException {
+		this.id = id;
+		this.name = name;
+		this.probeCount = probes.length;
+
+		createMemoryMappedProbeArray(id, name, probes.length, channel);
+		for (int i = 0; i < probes.length; i++) {
+			if (probes[i]) {
+				setProbe(i);
+			}
+		}
+	}
+
+	/**
+	 * Maps a section of the already-opened file for the probe data.
+	 *
+	 * @param id  the class id
+	 * @param name  the VM class name
+	 * @param probeCount  the number of probes for this class
+	 */
+	private void createMemoryMappedProbeArray(
+		final long id, final String name, final int probeCount, final FileChannel channel)
+			throws IOException {
+		synchronized (channel) {
+			int byteCount = (probeCount + 7) / 8;
+
+			// Write the ExecutionData block info.
+			ByteBuffer execDataBuffer = ByteBuffer.allocate(11);
+			execDataBuffer.put(ExecutionDataWriter.BLOCK_EXECUTIONDATA);
+			execDataBuffer.putLong(id);
+			execDataBuffer.putShort((short) name.length());
+			execDataBuffer.flip();
+			channel.write(execDataBuffer);
+			channel.write(ByteBuffer.wrap(name.getBytes(Charset.forName("UTF-8"))));
+
+			// Write the probe info and map part of this file for the probe data.
+			channel.write(toVarIntByteBuffer(probeCount));
+			probeBuffer = channel.map(FileChannel.MapMode.READ_WRITE, channel.position(), byteCount);
+			channel.position(channel.position() + byteCount);
+		}
+	}
+
+	/**
+	 * Writes a variable-length int to a {@link ByteBuffer}.
+	 *
+	 * @param value  the value to write
+	 * @return a ByteBuffer that can be used to write to a FileChannel containing the value
+	 */
+	private ByteBuffer toVarIntByteBuffer(int value) {
+		ByteBuffer buffer = ByteBuffer.allocate(5);
+		if (value == 0) {
+			buffer.put((byte) 0);
+		} else {
+			while (value > 0) {
+				if ((value & 0xFFFFFF80) == 0) {
+					buffer.put((byte) value);
+				} else {
+					buffer.put((byte) (0x80 | (value & 0x7F)));
+				}
+				value >>>= 7;
+			}
+		}
+		buffer.flip();
+		return buffer;
+	}
+
+	/**
+	 * Return the unique identifier for this class. The identifier is the CRC64
+	 * checksum of the raw class file definition.
+	 *
+	 * @return class identifier
+	 */
+	public long getId() {
+		return id;
+	}
+
+	/**
+	 * The VM name of the class.
+	 *
+	 * @return VM name
+	 */
+	public String getName() {
+		return name;
+	}
+
+	/**
+	 * The number of instrumentation probes for this class.
+	 *
+	 * @return number of probes
+	 */
+	public int getProbeCount() {
+		return probeCount;
+	}
+
+	/**
+	 * Returns a copy of the probe data as a boolean array.
+	 *
+	 * Changes to the returned array will not be reflected in the execution data.
+	 *
+	 * @return copy of the probe data
+	 */
+	public boolean[] getProbesCopy() {
+		final int bytes = (probeCount + 7) / 8;
+		boolean[] probes = new boolean[probeCount];
+		for (int index = 0; index < probeCount; index += 8) {
+			byte byteProbe = probeBuffer.get(index / 8);
+			for (int bit = 0; (bit < 8) && ((index + bit) < probeCount); bit++) {
+				probes[index + bit] = ((byteProbe & 0x1) > 0);
+				byteProbe >>>= 1;
+			}
+		}
+		return probes;
+	}
+
+	/**
+	 * Sets all probes to <code>false</code>.
+	 */
+	public void reset() {
+		final int bytes = (probeCount + 7) / 8;
+		synchronized (probeBuffer) {
+			for (int i = 0; i < bytes; i++) {
+				probeBuffer.put(i, (byte) 0);
+			}
+		}
+	}
+
+	/**
+	 * Checks whether any probe has been hit.
+	 *
+	 * @return <code>true</code>, if at least one probe has been hit
+	 */
+	public boolean hasHits() {
+		final int bytes = (probeCount + 7) / 8;
+		synchronized (probeBuffer) {
+			for (int i = 0; i < bytes; i++) {
+				if (probeBuffer.get(i) > 0) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Merges the given execution data into the probe data of this object. I.e.
+	 * a probe entry in this object is marked as executed (<code>true</code>) if
+	 * this probe or the corresponding other probe was executed. So the result
+	 * is
+	 *
+	 * <pre>
+	 * A or B
+	 * </pre>
+	 *
+	 * The probe array of the other object is not modified.
+	 *
+	 * @param other
+	 *            execution data to merge
+	 */
+	public void merge(final IExecutionData other) {
+		merge(other, true);
+	}
+
+	/**
+	 * Merges the given execution data into the probe data of this object. A
+	 * probe in this object is set to the value of <code>flag</code> if the
+	 * corresponding other probe was executed. For <code>flag==true</code> this
+	 * corresponds to
+	 *
+	 * <pre>
+	 * A or B
+	 * </pre>
+	 *
+	 * For <code>flag==false</code> this can be considered as a subtraction
+	 *
+	 * <pre>
+	 * A and not B
+	 * </pre>
+	 *
+	 * The probe array of the other object is not modified.
+	 *
+	 * @param other
+	 *            execution data to merge
+	 * @param flag
+	 *            merge mode
+	 */
+	public void merge(final IExecutionData other, final boolean flag) {
+		synchronized (probeBuffer) {
+			for (int i = 0; i < probeCount; i++) {
+				if (other.getProbe(i)) {
+					setProbe(i);
+				}
+			}
+		}
+	}
+
+	/**
+	 * Asserts that this execution data object is compatible with the given
+	 * parameters. The purpose of this check is to detect a very unlikely class
+	 * id collision.
+	 *
+	 * @param id
+	 *            other class id, must be the same
+	 * @param name
+	 *            other name, must be equal to this name
+	 * @param probecount
+	 *            probe data length, must be the same as for this data
+	 * @throws IllegalStateException
+	 *             if the given parameters do not match this instance
+	 */
+	public void assertCompatibility(final long id, final String name, final int probeCount)
+		throws IllegalStateException {
+		if (this.id != id) {
+			throw new IllegalStateException(format(
+					"Different ids (%016x and %016x).", Long.valueOf(this.id),
+					Long.valueOf(id)));
+		}
+		if (!this.name.equals(name)) {
+			throw new IllegalStateException(format(
+					"Different class names %s and %s for id %016x.", this.name,
+					name, Long.valueOf(id)));
+		}
+		if (this.probeCount != probeCount) {
+			throw new IllegalStateException(format(
+					"Incompatible execution data for class %s with id %016x.",
+					name, Long.valueOf(id)));
+		}
+	}
+
+	/**
+	 * Returns the execution data probe for a given index. A value of
+	 * <code>true</code> indicates that the corresponding probe was
+	 * executed.
+	 *
+	 * @param index  the probe's index to look up
+	 *
+	 * @return probe data
+	 */
+	public boolean getProbe(final int index) {
+		int offset = index / 8;
+		int bit = 1 << (index % 8);
+		return (probeBuffer.get(offset) & bit) != 0;
+	}
+
+	/**
+	 * Sets the execution data probe at the given index to <code>true</code>.
+	 *
+	 * @param index  the probe's index to set
+	 */
+	public void setProbe(final int index) {
+		int offset = index / 8;
+		int bit = 1 << (index % 8);
+		byte currentValue = probeBuffer.get(offset);
+		if ((currentValue & bit) == 0) {
+			synchronized (probeBuffer) {
+				probeBuffer.put(offset, (byte) (probeBuffer.get(offset) | bit));
+			}
+		}
+	}
+}
+// END android-change
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/InstrSupport.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/InstrSupport.java
index 85e83a3..4d4e1ba 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/instr/InstrSupport.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/InstrSupport.java
@@ -77,7 +77,9 @@
 	 * Data type of the field that stores coverage information for a class (
 	 * <code>boolean[]</code>).
 	 */
-	public static final String DATAFIELD_DESC = "[Z";
+	// BEGIN android-change
+	public static final String DATAFIELD_DESC = "Lorg/jacoco/core/data/IExecutionData;";
+	// END android-change
 
 	// === Init Method ===
 
@@ -89,7 +91,9 @@
 	/**
 	 * Descriptor of the initialization method.
 	 */
-	public static final String INITMETHOD_DESC = "()[Z";
+	// BEGIN android-change
+	public static final String INITMETHOD_DESC = "()Lorg/jacoco/core/data/IExecutionData;";
+	// END android-change
 
 	/**
 	 * Access modifiers of the initialization method.
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeInserter.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeInserter.java
index 63fbf76..0cac8f8 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeInserter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeInserter.java
@@ -67,25 +67,20 @@
 
 	public void insertProbe(final int id) {
 
-		// For a probe we set the corresponding position in the boolean[] array
-		// to true.
+		// BEGIN android-change
+		// For a probe we call setProbe on the IExecutionData object.
 
 		mv.visitVarInsn(Opcodes.ALOAD, variable);
 
-		// Stack[0]: [Z
+		// Stack[0]: Lorg/jacoco/core/data/IExecutionData;
 
 		InstrSupport.push(mv, id);
 
 		// Stack[1]: I
-		// Stack[0]: [Z
+		// Stack[0]: Lorg/jacoco/core/data/IExecutionData;
 
-		mv.visitInsn(Opcodes.ICONST_1);
-
-		// Stack[2]: I
-		// Stack[1]: I
-		// Stack[0]: [Z
-
-		mv.visitInsn(Opcodes.BASTORE);
+		mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "org/jacoco/core/data/IExecutionData", "setProbe", "(I)V", true);
+		// END android-change
 	}
 
 	@Override
diff --git a/org.jacoco.core/src/org/jacoco/core/runtime/LoggerRuntime.java b/org.jacoco.core/src/org/jacoco/core/runtime/LoggerRuntime.java
index 7255395..bef4203 100644
--- a/org.jacoco.core/src/org/jacoco/core/runtime/LoggerRuntime.java
+++ b/org.jacoco.core/src/org/jacoco/core/runtime/LoggerRuntime.java
@@ -168,7 +168,9 @@
 		@Override
 		public void publish(final LogRecord record) {
 			if (key.equals(record.getMessage())) {
-				data.getProbes(record.getParameters());
+				// BEGIN android-change
+				data.getExecutionData(record.getParameters());
+				// END android-change
 			}
 		}
 
diff --git a/org.jacoco.core/src/org/jacoco/core/runtime/OfflineInstrumentationAccessGenerator.java b/org.jacoco.core/src/org/jacoco/core/runtime/OfflineInstrumentationAccessGenerator.java
index af6671e..a5e88b6 100644
--- a/org.jacoco.core/src/org/jacoco/core/runtime/OfflineInstrumentationAccessGenerator.java
+++ b/org.jacoco.core/src/org/jacoco/core/runtime/OfflineInstrumentationAccessGenerator.java
@@ -50,8 +50,10 @@
 		mv.visitLdcInsn(Long.valueOf(classid));
 		mv.visitLdcInsn(classname);
 		InstrSupport.push(mv, probecount);
-		mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeClassName, "getProbes",
-				"(JLjava/lang/String;I)[Z", false);
+		// BEGIN android-change
+		mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeClassName, "getExecutionData",
+				"(JLjava/lang/String;I)Lorg/jacoco/core/data/IExecutionData;", false);
+		// END android-change
 		return 4;
 	}
 
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 afb5b7f..c0fbb65 100644
--- a/org.jacoco.core/src/org/jacoco/core/runtime/RuntimeData.java
+++ b/org.jacoco.core/src/org/jacoco/core/runtime/RuntimeData.java
@@ -11,7 +11,7 @@
  *******************************************************************************/
 package org.jacoco.core.runtime;
 
-import org.jacoco.core.data.ExecutionData;
+import org.jacoco.core.data.IExecutionData;
 import org.jacoco.core.data.ExecutionDataStore;
 import org.jacoco.core.data.IExecutionDataVisitor;
 import org.jacoco.core.data.ISessionInfoVisitor;
@@ -128,15 +128,18 @@
 	 *            probe data length
 	 * @return execution data
 	 */
-	public ExecutionData getExecutionData(final Long id, final String name,
+	// BEGIN android-change
+	public IExecutionData getExecutionData(final Long id, final String name,
 			final int probecount) {
+	// END android-change
 		synchronized (store) {
 			return store.get(id, name, probecount);
 		}
 	}
 
+	// BEGIN android-change
 	/**
-	 * Retrieves the execution probe array for a given class. The passed
+	 * Retrieves the execution data for a given class. The passed
 	 * {@link Object} array instance is used for parameters and the return value
 	 * as follows. Call parameters:
 	 * 
@@ -149,18 +152,19 @@
 	 * Return value:
 	 * 
 	 * <ul>
-	 * <li>args[0]: probe array (<code>boolean[]</code>)
+	 * <li>args[0]: execution data ({@link IExecutionData})
 	 * </ul>
 	 * 
 	 * @param args
 	 *            parameter array of length 3
 	 */
-	public void getProbes(final Object[] args) {
+	public void getExecutionData(final Object[] args) {
 		final Long classid = (Long) args[0];
 		final String name = (String) args[1];
 		final int probecount = ((Integer) args[2]).intValue();
-		args[0] = getExecutionData(classid, name, probecount).getProbes();
+		args[0] = getExecutionData(classid, name, probecount);
 	}
+	// END android-change
 
 	/**
 	 * In violation of the regular semantic of {@link Object#equals(Object)}
@@ -173,7 +177,9 @@
 	@Override
 	public boolean equals(final Object args) {
 		if (args instanceof Object[]) {
-			getProbes((Object[]) args);
+			// BEGIN android-change
+			getExecutionData((Object[]) args);
+			// END android-change
 		}
 		return super.equals(args);
 	}
diff --git a/org.jacoco.report/src/org/jacoco/report/IReportVisitor.java b/org.jacoco.report/src/org/jacoco/report/IReportVisitor.java
index db36796..1cf4aed 100644
--- a/org.jacoco.report/src/org/jacoco/report/IReportVisitor.java
+++ b/org.jacoco.report/src/org/jacoco/report/IReportVisitor.java
@@ -15,7 +15,7 @@
 import java.util.Collection;
 import java.util.List;
 
-import org.jacoco.core.data.ExecutionData;
+import org.jacoco.core.data.IExecutionData;
 import org.jacoco.core.data.SessionInfo;
 
 /**
@@ -39,7 +39,9 @@
 	 *             in case of IO problems with the report writer
 	 */
 	void visitInfo(List<SessionInfo> sessionInfos,
-			Collection<ExecutionData> executionData) throws IOException;
+			// BEGIN android-change
+			Collection<IExecutionData> executionData) throws IOException;
+			// END android-change
 
 	/**
 	 * Has to be called after all report data has been emitted.
diff --git a/org.jacoco.report/src/org/jacoco/report/MultiReportVisitor.java b/org.jacoco.report/src/org/jacoco/report/MultiReportVisitor.java
index 3cd5e55..f4fefa8 100644
--- a/org.jacoco.report/src/org/jacoco/report/MultiReportVisitor.java
+++ b/org.jacoco.report/src/org/jacoco/report/MultiReportVisitor.java
@@ -17,7 +17,7 @@
 import java.util.List;
 
 import org.jacoco.core.analysis.IBundleCoverage;
-import org.jacoco.core.data.ExecutionData;
+import org.jacoco.core.data.IExecutionData;
 import org.jacoco.core.data.SessionInfo;
 
 /**
@@ -41,7 +41,9 @@
 	}
 
 	public void visitInfo(final List<SessionInfo> sessionInfos,
-			final Collection<ExecutionData> executionData) throws IOException {
+			// BEGIN android-change
+			final Collection<IExecutionData> executionData) throws IOException {
+			// END android-chnage
 		for (final IReportVisitor v : visitors) {
 			v.visitInfo(sessionInfos, executionData);
 		}
diff --git a/org.jacoco.report/src/org/jacoco/report/check/RulesChecker.java b/org.jacoco.report/src/org/jacoco/report/check/RulesChecker.java
index cc6acf0..f743560 100644
--- a/org.jacoco.report/src/org/jacoco/report/check/RulesChecker.java
+++ b/org.jacoco.report/src/org/jacoco/report/check/RulesChecker.java
@@ -17,7 +17,7 @@
 import java.util.List;
 
 import org.jacoco.core.analysis.IBundleCoverage;
-import org.jacoco.core.data.ExecutionData;
+import org.jacoco.core.data.IExecutionData;
 import org.jacoco.core.data.SessionInfo;
 import org.jacoco.report.ILanguageNames;
 import org.jacoco.report.IReportGroupVisitor;
@@ -86,7 +86,9 @@
 			}
 
 			public void visitInfo(final List<SessionInfo> sessionInfos,
-					final Collection<ExecutionData> executionData)
+					// BEGIN android-change
+					final Collection<IExecutionData> executionData)
+					// END android-change
 					throws IOException {
 			}
 
diff --git a/org.jacoco.report/src/org/jacoco/report/csv/CSVFormatter.java b/org.jacoco.report/src/org/jacoco/report/csv/CSVFormatter.java
index 793e215..42939da 100644
--- a/org.jacoco.report/src/org/jacoco/report/csv/CSVFormatter.java
+++ b/org.jacoco.report/src/org/jacoco/report/csv/CSVFormatter.java
@@ -17,7 +17,7 @@
 import java.util.Collection;
 import java.util.List;
 
-import org.jacoco.core.data.ExecutionData;
+import org.jacoco.core.data.IExecutionData;
 import org.jacoco.core.data.SessionInfo;
 import org.jacoco.report.ILanguageNames;
 import org.jacoco.report.IReportVisitor;
@@ -84,7 +84,9 @@
 			}
 
 			public void visitInfo(final List<SessionInfo> sessionInfos,
-					final Collection<ExecutionData> executionData)
+					// BEGIN android-change
+					final Collection<IExecutionData> executionData)
+					// END android-change
 					throws IOException {
 				// Info not used for CSV report
 			}
diff --git a/org.jacoco.report/src/org/jacoco/report/html/HTMLFormatter.java b/org.jacoco.report/src/org/jacoco/report/html/HTMLFormatter.java
index 9994ced..ce8cd2d 100644
--- a/org.jacoco.report/src/org/jacoco/report/html/HTMLFormatter.java
+++ b/org.jacoco.report/src/org/jacoco/report/html/HTMLFormatter.java
@@ -18,7 +18,7 @@
 
 import org.jacoco.core.analysis.IBundleCoverage;
 import org.jacoco.core.analysis.ICoverageNode.CounterEntity;
-import org.jacoco.core.data.ExecutionData;
+import org.jacoco.core.data.IExecutionData;
 import org.jacoco.core.data.SessionInfo;
 import org.jacoco.report.ILanguageNames;
 import org.jacoco.report.IMultiReportOutput;
@@ -193,12 +193,16 @@
 		return new IReportVisitor() {
 
 			private List<SessionInfo> sessionInfos;
-			private Collection<ExecutionData> executionData;
+			// BEGIN android-change
+			private Collection<IExecutionData> executionData;
+			// END android-change
 
 			private HTMLGroupVisitor groupHandler;
 
 			public void visitInfo(final List<SessionInfo> sessionInfos,
-					final Collection<ExecutionData> executionData)
+					// BEGIN android-change
+					final Collection<IExecutionData> executionData)
+					// END android-change
 					throws IOException {
 				this.sessionInfos = sessionInfos;
 				this.executionData = executionData;
diff --git a/org.jacoco.report/src/org/jacoco/report/internal/html/page/SessionsPage.java b/org.jacoco.report/src/org/jacoco/report/internal/html/page/SessionsPage.java
index 67de494..3a48902 100644
--- a/org.jacoco.report/src/org/jacoco/report/internal/html/page/SessionsPage.java
+++ b/org.jacoco.report/src/org/jacoco/report/internal/html/page/SessionsPage.java
@@ -20,7 +20,7 @@
 import java.util.Date;
 import java.util.List;
 
-import org.jacoco.core.data.ExecutionData;
+import org.jacoco.core.data.IExecutionData;
 import org.jacoco.core.data.SessionInfo;
 import org.jacoco.report.ILanguageNames;
 import org.jacoco.report.internal.ReportOutputFolder;
@@ -48,7 +48,7 @@
 
 	private final DateFormat dateFormat;
 
-	private final List<ExecutionData> executionData;
+	private final List<IExecutionData> executionData;
 
 	private final ElementIndex index;
 
@@ -69,18 +69,24 @@
 	 *            settings context
 	 */
 	public SessionsPage(final List<SessionInfo> sessionInfos,
-			final Collection<ExecutionData> executionData,
+			// BEGIN android-change
+			final Collection<IExecutionData> executionData,
+			// END android-change
 			final ElementIndex index, final ReportPage parent,
 			final ReportOutputFolder folder, final IHTMLReportContext context) {
 		super(parent, folder, context);
 		this.sessionInfos = sessionInfos;
-		this.executionData = new ArrayList<ExecutionData>(executionData);
+		// BEGIN android-change
+		this.executionData = new ArrayList<IExecutionData>(executionData);
+		// END android-change
 		this.index = index;
 		this.dateFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT,
 				DateFormat.DEFAULT, context.getLocale());
 		final ILanguageNames names = context.getLanguageNames();
-		Collections.sort(this.executionData, new Comparator<ExecutionData>() {
-			public int compare(final ExecutionData e1, final ExecutionData e2) {
+		// BEGIN android-change
+		Collections.sort(this.executionData, new Comparator<IExecutionData>() {
+			public int compare(final IExecutionData e1, final IExecutionData e2) {
+			// END android-change
 				return names.getQualifiedClassName(e1.getName()).compareTo(
 						names.getQualifiedClassName(e2.getName()));
 			}
@@ -129,7 +135,9 @@
 		}
 		final HTMLElement tbody = table.tbody();
 		final ILanguageNames names = context.getLanguageNames();
-		for (final ExecutionData e : executionData) {
+		// BEGIN android-change
+		for (final IExecutionData e : executionData) {
+		// END android-change
 			final HTMLElement tr = tbody.tr();
 			final String link = index.getLinkToClass(e.getId());
 			final String qualifiedName = names.getQualifiedClassName(e
diff --git a/org.jacoco.report/src/org/jacoco/report/xml/XMLFormatter.java b/org.jacoco.report/src/org/jacoco/report/xml/XMLFormatter.java
index a1fee86..3bda5ed 100644
--- a/org.jacoco.report/src/org/jacoco/report/xml/XMLFormatter.java
+++ b/org.jacoco.report/src/org/jacoco/report/xml/XMLFormatter.java
@@ -17,7 +17,7 @@
 import java.util.List;
 
 import org.jacoco.core.analysis.IBundleCoverage;
-import org.jacoco.core.data.ExecutionData;
+import org.jacoco.core.data.IExecutionData;
 import org.jacoco.core.data.SessionInfo;
 import org.jacoco.report.IReportGroupVisitor;
 import org.jacoco.report.IReportVisitor;
@@ -61,7 +61,9 @@
 			private XMLGroupVisitor groupVisitor;
 
 			public void visitInfo(final List<SessionInfo> sessionInfos,
-					final Collection<ExecutionData> executionData)
+					// BEGIN android-change
+					final Collection<IExecutionData> executionData)
+					// END android-change
 					throws IOException {
 				this.sessionInfos = sessionInfos;
 			}