Merge "In place split install native support"
diff --git a/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
index 9ce4d37..5ec115a 100644
--- a/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
+++ b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
@@ -20,6 +20,7 @@
 import java.net.URL;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Enumeration;
 import java.util.List;
 
@@ -139,6 +140,15 @@
         pathList.addDexPath(dexPath, null /*optimizedDirectory*/);
     }
 
+    /**
+     * Adds additional native paths for consideration in subsequent calls to
+     * {@link #findLibrary(String)}
+     * @hide
+     */
+    public void addNativePath(Collection<String> libPaths) {
+        pathList.addNativePath(libPaths);
+    }
+
     @Override
     protected URL findResource(String name) {
         return pathList.findResource(name);
diff --git a/dalvik/src/main/java/dalvik/system/DexPathList.java b/dalvik/src/main/java/dalvik/system/DexPathList.java
index 3693bb2..b7861b6 100644
--- a/dalvik/src/main/java/dalvik/system/DexPathList.java
+++ b/dalvik/src/main/java/dalvik/system/DexPathList.java
@@ -25,9 +25,12 @@
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.List;
+import java.util.Objects;
+import java.util.Set;
 import libcore.io.ClassPathURLStreamHandler;
 import libcore.io.IoUtils;
 import libcore.io.Libcore;
@@ -62,7 +65,8 @@
     private Element[] dexElements;
 
     /** List of native library path elements. */
-    private final NativeLibraryElement[] nativeLibraryPathElements;
+    // Some applications rely on this field being an array or we'd use a final list here
+    /* package visible for testing */ NativeLibraryElement[] nativeLibraryPathElements;
 
     /** List of application native library directories. */
     private final List<File> nativeLibraryDirectories;
@@ -553,6 +557,33 @@
     }
 
     /**
+     * Adds a collection of library paths from which to load native libraries. Paths can be absolute
+     * native library directories (i.e. /data/app/foo/lib/arm64) or apk references (i.e.
+     * /data/app/foo/base.apk!/lib/arm64).
+     *
+     * Note: This method will attempt to dedupe elements.
+     * Note: This method replaces the value of {@link #nativeLibraryPathElements}
+     */
+    public void addNativePath(Collection<String> libPaths) {
+        if (libPaths.isEmpty()) {
+            return;
+        }
+        List<File> libFiles = new ArrayList<>(libPaths.size());
+        for (String path : libPaths) {
+            libFiles.add(new File(path));
+        }
+        ArrayList<NativeLibraryElement> newPaths =
+                new ArrayList<>(nativeLibraryPathElements.length + libPaths.size());
+        newPaths.addAll(Arrays.asList(nativeLibraryPathElements));
+        for (NativeLibraryElement element : makePathElements(libFiles)) {
+            if (!newPaths.contains(element)) {
+                newPaths.add(element);
+            }
+        }
+        nativeLibraryPathElements = newPaths.toArray(new NativeLibraryElement[newPaths.size()]);
+    }
+
+    /**
      * Element of the dex/resource path. Note: should be called DexElement, but apps reflect on
      * this.
      */
@@ -798,5 +829,19 @@
 
             return null;
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof NativeLibraryElement)) return false;
+            NativeLibraryElement that = (NativeLibraryElement) o;
+            return Objects.equals(path, that.path) &&
+                    Objects.equals(zipDir, that.zipDir);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(path, zipDir);
+        }
     }
 }
diff --git a/luni/src/test/java/libcore/dalvik/system/DexClassLoaderTest.java b/luni/src/test/java/libcore/dalvik/system/DexClassLoaderTest.java
index 995cab1..efc9c29 100644
--- a/luni/src/test/java/libcore/dalvik/system/DexClassLoaderTest.java
+++ b/luni/src/test/java/libcore/dalvik/system/DexClassLoaderTest.java
@@ -20,6 +20,8 @@
 import java.io.File;
 import java.io.InputStream;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Map;
 
 import libcore.io.Streams;
@@ -36,12 +38,14 @@
     private File dex2;
     private File jar1;
     private File jar2;
+    private File nativeLib1;
 
     private Map<String, File> resourcesMap;
 
     protected void setUp() throws Exception {
         resourcesMap = ClassLoaderTestSupport.setupAndCopyResources(Arrays.asList(
-                "loading-test.dex", "loading-test2.dex", "loading-test.jar", "loading-test2.jar"));
+                "loading-test.dex", "loading-test2.dex", "loading-test.jar", "loading-test2.jar",
+                "libfake.so"));
 
         dex1 = resourcesMap.get("loading-test.dex");
         assertNotNull(dex1);
@@ -51,6 +55,8 @@
         assertNotNull(jar1);
         jar2 = resourcesMap.get("loading-test2.jar");
         assertNotNull(jar2);
+        nativeLib1 = resourcesMap.get("libfake.so");
+        assertNotNull(nativeLib1);
     }
 
     protected void tearDown() {
@@ -337,4 +343,26 @@
     public void test_twoJar_diff_getResourceAsStream() throws Exception {
         createLoaderAndCallMethod("test.TestMethods", "test_diff_getResourceAsStream", jar1, jar2);
     }
+
+    /*
+     * Tests native modification behaviors
+     */
+
+    /**
+     * Checks that a adding a native library to an existing class loader makes it visible for
+     * subsequent calls.
+     * @throws Exception
+     */
+    public void test_oneDex_addNative_findsLibrary() throws Exception {
+        String path = nativeLib1.getParentFile().getAbsolutePath();
+        DexClassLoader classLoader = (DexClassLoader) createLoader(dex1);
+
+        assertNull("findLibrary should not find un-added path",
+                classLoader.findLibrary("fake"));
+
+        classLoader.addNativePath(Collections.singletonList(path));
+
+        assertNotNull("findLibrary should find newly added path",
+                classLoader.findLibrary("fake"));
+    }
 }
diff --git a/luni/src/test/resources/dalvik/system/libfake.so b/luni/src/test/resources/dalvik/system/libfake.so
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/luni/src/test/resources/dalvik/system/libfake.so