Add native support for BaseDexClassLoader shared libraries.

bug: 112405321
Test: 688-shared-library
Change-Id: Ia993b3ded71d4491a59fb78b0282eacdb66634bd
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index b747225..ba520f9 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2404,6 +2404,35 @@
   return ClassPathEntry(nullptr, nullptr);
 }
 
+bool ClassLinker::FindClassInSharedLibraries(ScopedObjectAccessAlreadyRunnable& soa,
+                                             Thread* self,
+                                             const char* descriptor,
+                                             size_t hash,
+                                             Handle<mirror::ClassLoader> class_loader,
+                                             /*out*/ ObjPtr<mirror::Class>* result) {
+  ArtField* field =
+      jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
+  ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader.Get());
+  if (raw_shared_libraries == nullptr) {
+    return true;
+  }
+
+  StackHandleScope<2> hs(self);
+  Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries(
+      hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>()));
+  MutableHandle<mirror::ClassLoader> temp_loader = hs.NewHandle<mirror::ClassLoader>(nullptr);
+  for (int32_t i = 0; i < shared_libraries->GetLength(); ++i) {
+    temp_loader.Assign(shared_libraries->Get(i));
+    if (!FindClassInBaseDexClassLoader(soa, self, descriptor, hash, temp_loader, result)) {
+      return false;  // One of the shared libraries is not supported.
+    }
+    if (*result != nullptr) {
+      return true;  // Found the class up the chain.
+    }
+  }
+  return true;
+}
+
 bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
                                                 Thread* self,
                                                 const char* descriptor,
@@ -2419,6 +2448,7 @@
   if (IsPathOrDexClassLoader(soa, class_loader)) {
     // For regular path or dex class loader the search order is:
     //    - parent
+    //    - shared libraries
     //    - class loader dex files
 
     // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
@@ -2431,6 +2461,13 @@
       return true;  // Found the class up the chain.
     }
 
+    if (!FindClassInSharedLibraries(soa, self, descriptor, hash, class_loader, result)) {
+      return false;  // One of the shared library loader is not supported.
+    }
+    if (*result != nullptr) {
+      return true;  // Found the class in a shared library.
+    }
+
     // Search the current class loader classpath.
     *result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader);
     return true;
@@ -2439,6 +2476,7 @@
   if (IsDelegateLastClassLoader(soa, class_loader)) {
     // For delegate last, the search order is:
     //    - boot class path
+    //    - shared libraries
     //    - class loader dex files
     //    - parent
     *result = FindClassInBootClassLoaderClassPath(self, descriptor, hash);
@@ -2446,6 +2484,13 @@
       return true;  // The class is part of the boot class path.
     }
 
+    if (!FindClassInSharedLibraries(soa, self, descriptor, hash, class_loader, result)) {
+      return false;  // One of the shared library loader is not supported.
+    }
+    if (*result != nullptr) {
+      return true;  // Found the class in a shared library.
+    }
+
     *result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader);
     if (*result != nullptr) {
       return true;  // Found the class in the current class loader
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 0e276cd..4aa9fcd 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -875,6 +875,15 @@
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::dex_lock_);
 
+  bool FindClassInSharedLibraries(ScopedObjectAccessAlreadyRunnable& soa,
+                                  Thread* self,
+                                  const char* descriptor,
+                                  size_t hash,
+                                  Handle<mirror::ClassLoader> class_loader,
+                                  /*out*/ ObjPtr<mirror::Class>* result)
+      REQUIRES_SHARED(Locks::mutator_lock_)
+      REQUIRES(!Locks::dex_lock_);
+
   // Finds the class in the classpath of the given class loader. It only searches the class loader
   // dex files and does not recurse into its parent.
   // The method checks that the provided class loader is either a PathClassLoader or a
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 206418f..ef95cca 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -119,6 +119,7 @@
 jfieldID WellKnownClasses::dalvik_system_DexFile_cookie;
 jfieldID WellKnownClasses::dalvik_system_DexFile_fileName;
 jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList;
+jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
 jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements;
 jfieldID WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
 jfieldID WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
@@ -365,6 +366,7 @@
   org_apache_harmony_dalvik_ddmc_DdmServer_dispatch = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "dispatch", "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
 
   dalvik_system_BaseDexClassLoader_pathList = CacheField(env, dalvik_system_BaseDexClassLoader, false, "pathList", "Ldalvik/system/DexPathList;");
+  dalvik_system_BaseDexClassLoader_sharedLibraryLoaders = CacheField(env, dalvik_system_BaseDexClassLoader, false, "sharedLibraryLoaders", "[Ljava/lang/ClassLoader;");
   dalvik_system_DexFile_cookie = CacheField(env, dalvik_system_DexFile, false, "mCookie", "Ljava/lang/Object;");
   dalvik_system_DexFile_fileName = CacheField(env, dalvik_system_DexFile, false, "mFileName", "Ljava/lang/String;");
   dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index ce5ab1d..01e7d99 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -126,6 +126,7 @@
   static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_dispatch;
 
   static jfieldID dalvik_system_BaseDexClassLoader_pathList;
+  static jfieldID dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
   static jfieldID dalvik_system_DexFile_cookie;
   static jfieldID dalvik_system_DexFile_fileName;
   static jfieldID dalvik_system_DexPathList_dexElements;
diff --git a/test/688-shared-library/expected.txt b/test/688-shared-library/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/688-shared-library/expected.txt
diff --git a/test/688-shared-library/info.txt b/test/688-shared-library/info.txt
new file mode 100644
index 0000000..2eda65d
--- /dev/null
+++ b/test/688-shared-library/info.txt
@@ -0,0 +1,2 @@
+Tests on BaseDexClassLoader shared libraries and their class
+loading behavior.
diff --git a/test/688-shared-library/src-art/Main.java b/test/688-shared-library/src-art/Main.java
new file mode 100644
index 0000000..d59e7dc
--- /dev/null
+++ b/test/688-shared-library/src-art/Main.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import dalvik.system.DelegateLastClassLoader;
+import dalvik.system.PathClassLoader;
+
+public class Main {
+    static final String TEST_NAME = "688-shared-library";
+    static final String MAIN_JAR_FILE = System.getenv("DEX_LOCATION") + "/" + TEST_NAME + ".jar";
+    static final String EX_JAR_FILE = System.getenv("DEX_LOCATION") + "/" + TEST_NAME + "-ex.jar";
+    static ClassLoader bootLoader = Object.class.getClassLoader();
+
+    public static void main(String[] args) throws Exception {
+      testNoLibrary();
+      testOneLibrary();
+      testTwoLibraries1();
+      testTwoLibraries2();
+      testTransitive1();
+      testTransitive2();
+      testTransitive3();
+      testTransitive4();
+    }
+
+    public static void assertIdentical(Object expected, Object actual) {
+      if (expected != actual) {
+        throw new Error("Expected " + expected + ", got " + actual);
+      }
+    }
+
+    public static void testNoLibrary() throws Exception {
+      ClassLoader loader = new PathClassLoader(MAIN_JAR_FILE, null, bootLoader);
+      Class<?> cls = loader.loadClass("Main");
+      assertIdentical(loader, cls.getClassLoader());
+    }
+
+    public static void testOneLibrary() throws Exception {
+      ClassLoader[] sharedLibraries = {
+          new PathClassLoader(EX_JAR_FILE, null, bootLoader),
+      };
+      ClassLoader delegateFirst =
+          new PathClassLoader(MAIN_JAR_FILE, null, bootLoader, sharedLibraries);
+      Class<?> cls = delegateFirst.loadClass("Main");
+      assertIdentical(sharedLibraries[0], cls.getClassLoader());
+      cls = delegateFirst.loadClass("SharedLibraryOne");
+      assertIdentical(sharedLibraries[0], cls.getClassLoader());
+      
+      ClassLoader delegateLast =
+          new DelegateLastClassLoader(MAIN_JAR_FILE, null, bootLoader, sharedLibraries);
+      cls = delegateLast.loadClass("Main");
+      assertIdentical(sharedLibraries[0], cls.getClassLoader());
+      cls = delegateLast.loadClass("SharedLibraryOne");
+      assertIdentical(sharedLibraries[0], cls.getClassLoader());
+    }
+    
+    public static void testTwoLibraries1() throws Exception {
+      ClassLoader[] sharedLibraries = {
+          new PathClassLoader(MAIN_JAR_FILE, null, bootLoader),
+          new PathClassLoader(EX_JAR_FILE, null, bootLoader),
+      };
+      ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraries);
+      Class<?> cls = delegateFirst.loadClass("Main");
+      assertIdentical(sharedLibraries[0], cls.getClassLoader());
+      cls = delegateFirst.loadClass("SharedLibraryOne");
+      assertIdentical(sharedLibraries[1], cls.getClassLoader());
+      
+      ClassLoader delegateLast =
+          new DelegateLastClassLoader(MAIN_JAR_FILE, null, bootLoader, sharedLibraries);
+      cls = delegateLast.loadClass("Main");
+      assertIdentical(sharedLibraries[0], cls.getClassLoader());
+      cls = delegateLast.loadClass("SharedLibraryOne");
+      assertIdentical(sharedLibraries[1], cls.getClassLoader());
+    }
+    
+    public static void testTwoLibraries2() throws Exception {
+      ClassLoader[] sharedLibraries = {
+          new PathClassLoader(EX_JAR_FILE, null, bootLoader),
+          new PathClassLoader(MAIN_JAR_FILE, null, bootLoader),
+      };
+      ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraries);
+      Class<?> cls = delegateFirst.loadClass("Main");
+      assertIdentical(sharedLibraries[0], cls.getClassLoader());
+      cls = delegateFirst.loadClass("SharedLibraryOne");
+      assertIdentical(sharedLibraries[0], cls.getClassLoader());
+      
+      ClassLoader delegateLast = new DelegateLastClassLoader("", null, bootLoader, sharedLibraries);
+      cls = delegateLast.loadClass("Main");
+      assertIdentical(sharedLibraries[0], cls.getClassLoader());
+      cls = delegateLast.loadClass("SharedLibraryOne");
+      assertIdentical(sharedLibraries[0], cls.getClassLoader());
+    }
+
+    public static void testTransitive1() throws Exception {
+      ClassLoader[] sharedLibraryLevel2 = {
+          new PathClassLoader(EX_JAR_FILE, null, bootLoader),
+      };
+      ClassLoader[] sharedLibraryLevel1 = {
+          new PathClassLoader(MAIN_JAR_FILE, null, bootLoader, sharedLibraryLevel2),
+      };
+
+      ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraryLevel1);
+      Class<?> cls = delegateFirst.loadClass("Main");
+      assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+      cls = delegateFirst.loadClass("SharedLibraryOne");
+      assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+      
+      ClassLoader delegateLast =
+          new DelegateLastClassLoader("", null, bootLoader, sharedLibraryLevel1);
+      cls = delegateLast.loadClass("Main");
+      assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+      cls = delegateLast.loadClass("SharedLibraryOne");
+      assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+    }
+    
+    public static void testTransitive2() throws Exception {
+      ClassLoader[] sharedLibraryLevel2 = {
+          new PathClassLoader(MAIN_JAR_FILE, null, bootLoader),
+      };
+      ClassLoader[] sharedLibraryLevel1 = {
+          new PathClassLoader(EX_JAR_FILE, null, bootLoader, sharedLibraryLevel2),
+      };
+
+      ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraryLevel1);
+      Class<?> cls = delegateFirst.loadClass("Main");
+      assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+      cls = delegateFirst.loadClass("SharedLibraryOne");
+      assertIdentical(sharedLibraryLevel1[0], cls.getClassLoader());
+      
+      ClassLoader delegateLast =
+          new DelegateLastClassLoader("", null, bootLoader, sharedLibraryLevel1);
+      cls = delegateLast.loadClass("Main");
+      assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+      cls = delegateLast.loadClass("SharedLibraryOne");
+      assertIdentical(sharedLibraryLevel1[0], cls.getClassLoader());
+    }
+
+    public static void testTransitive3() throws Exception {
+      ClassLoader[] sharedLibraryLevel2 = {
+          new PathClassLoader(MAIN_JAR_FILE, null, bootLoader),
+      };
+      ClassLoader[] sharedLibraryLevel1 = {
+          new PathClassLoader(EX_JAR_FILE, null, bootLoader, sharedLibraryLevel2),
+          sharedLibraryLevel2[0],
+      };
+
+      ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraryLevel1);
+      Class<?> cls = delegateFirst.loadClass("Main");
+      assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+      cls = delegateFirst.loadClass("SharedLibraryOne");
+      assertIdentical(sharedLibraryLevel1[0], cls.getClassLoader());
+      
+      ClassLoader delegateLast =
+          new DelegateLastClassLoader("", null, bootLoader, sharedLibraryLevel1);
+      cls = delegateLast.loadClass("Main");
+      assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+      cls = delegateLast.loadClass("SharedLibraryOne");
+      assertIdentical(sharedLibraryLevel1[0], cls.getClassLoader());
+    }
+    
+    public static void testTransitive4() throws Exception {
+      ClassLoader[] sharedLibraryLevel2 = {
+          new PathClassLoader(EX_JAR_FILE, null, bootLoader),
+      };
+      ClassLoader[] sharedLibraryLevel1 = {
+          new PathClassLoader(MAIN_JAR_FILE, null, bootLoader, sharedLibraryLevel2),
+          sharedLibraryLevel2[0],
+      };
+
+      ClassLoader delegateFirst = new PathClassLoader("", null, bootLoader, sharedLibraryLevel1);
+      Class<?> cls = delegateFirst.loadClass("Main");
+      assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+      cls = delegateFirst.loadClass("SharedLibraryOne");
+      assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+      
+      ClassLoader delegateLast =
+          new DelegateLastClassLoader("", null, bootLoader, sharedLibraryLevel1);
+      cls = delegateLast.loadClass("Main");
+      assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+      cls = delegateLast.loadClass("SharedLibraryOne");
+      assertIdentical(sharedLibraryLevel2[0], cls.getClassLoader());
+    }
+}
diff --git a/test/688-shared-library/src-ex/Main.java b/test/688-shared-library/src-ex/Main.java
new file mode 100644
index 0000000..f6555b9
--- /dev/null
+++ b/test/688-shared-library/src-ex/Main.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+}
diff --git a/test/688-shared-library/src-ex/SharedLibraryOne.java b/test/688-shared-library/src-ex/SharedLibraryOne.java
new file mode 100644
index 0000000..d86755f
--- /dev/null
+++ b/test/688-shared-library/src-ex/SharedLibraryOne.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class SharedLibraryOne {
+}
diff --git a/test/knownfailures.json b/test/knownfailures.json
index e27a4d6..3cc58ac 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1024,6 +1024,7 @@
                   "677-fsi2",
                   "678-quickening",
                   "679-locks",
+                  "688-shared-library",
                   "999-redefine-hiddenapi",
                   "1000-non-moving-space-stress",
                   "1951-monitor-enter-no-suspend"],