Snap for 4751833 from a599c6c553aee86dd5a15db3e71d6d4cee7a5fd4 to pi-release

Change-Id: I4569e60985de773d586bed9b671af8f93931b07b
diff --git a/dalvik/src/main/java/dalvik/system/DexPathList.java b/dalvik/src/main/java/dalvik/system/DexPathList.java
index 3693bb2..5bd7f1c 100644
--- a/dalvik/src/main/java/dalvik/system/DexPathList.java
+++ b/dalvik/src/main/java/dalvik/system/DexPathList.java
@@ -27,7 +27,9 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Enumeration;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import libcore.io.ClassPathURLStreamHandler;
 import libcore.io.IoUtils;
 import libcore.io.Libcore;
@@ -50,6 +52,7 @@
 /*package*/ final class DexPathList {
     private static final String DEX_SUFFIX = ".dex";
     private static final String zipSeparator = "!/";
+    private static final Element[] EMPTY_ELEMENTS = new Element[0];
 
     /** class definition context */
     private final ClassLoader definingContext;
@@ -212,7 +215,8 @@
      */
     public void addDexPath(String dexPath, File optimizedDirectory) {
         final List<IOException> suppressedExceptionList = new ArrayList<IOException>();
-        final Element[] newElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
+        final Element[] newElements = makeDexElements(
+                dexElements, splitDexPath(dexPath), optimizedDirectory,
                 suppressedExceptionList, definingContext);
 
         if (newElements != null && newElements.length > 0) {
@@ -306,7 +310,19 @@
      * the given array.
      */
     private static Element[] makeDexElements(List<File> files, File optimizedDirectory,
+      List<IOException> suppressedExceptions, ClassLoader loader) {
+      return makeDexElements(
+              EMPTY_ELEMENTS, files, optimizedDirectory, suppressedExceptions, loader);
+    }
+
+    /**
+     * Makes an array of dex/resource path elements, one per element of
+     * the given array.
+     */
+    private static Element[] makeDexElements(
+            Element[] existingElements, List<File> files, File optimizedDirectory,
             List<IOException> suppressedExceptions, ClassLoader loader) {
+
       Element[] elements = new Element[files.size()];
       int elementsPos = 0;
       /*
@@ -359,9 +375,59 @@
       if (elementsPos != elements.length) {
           elements = Arrays.copyOf(elements, elementsPos);
       }
+      try {
+          // Check for http://b/77342775
+          checkForDuplicates(existingElements, elements);
+      } catch (Throwable e) {
+          System.logE("Unexpected throwable during checkForDuplicates() for http://b/77342775", e);
+          // ignore
+      }
       return elements;
     }
 
+    // BEGIN Logging for http://b/77342775
+    /**
+     * Logs a warning if newElements contains duplicate paths, or contains paths already
+     * present in oldElements. Duplicate paths in oldElements that do not occur in newElements
+     * are tolerated without logging.
+     */
+    private static void checkForDuplicates(Element[] oldElements, Element[] newElements) {
+        Set<String> elementPaths = new HashSet<>(oldElements.length + newElements.length);
+        List<String> oldPaths = getElementPaths(oldElements);  // may contain nulls
+        elementPaths.addAll(oldPaths);  // may contain null
+        for (Element element : newElements) {
+            String path = getElementPath(element);
+            boolean isDuplicate = !elementPaths.add(path);
+            if (isDuplicate && (path != null)) {
+                List<String> newPaths = getElementPaths(newElements);  // may contain nulls
+                String s = "DexPathList: " + oldPaths + " + " + newPaths + " contains duplicates.";
+                System.logE(s, new RuntimeException("bug 77342775"));
+                return;
+            }
+        }
+    }
+
+    private static List<String> getElementPaths(Element[] elements) {
+        List<String> result = new ArrayList<>(elements.length);
+        for (Element element : elements) {
+            result.add(getElementPath(element));
+        }
+        return result;
+    }
+
+    private static String getElementPath(Element element) {
+        File file = element.path;
+        if (file != null) {
+            return file.getPath();
+        }
+        DexFile dexFile = element.dexFile;
+        if (dexFile != null) {
+            return dexFile.getName();
+        }
+        return null;
+    }
+    // END Logging for http://b/77342775
+
     /**
      * Constructs a {@code DexFile} instance, as appropriate depending on whether
      * {@code optimizedDirectory} is {@code null}. An application image file may be associated with