Revert^4 "[art] Enable compilation of secondary dexes by default i..."

This reverts commit f5d83b3a550496f6ef120606de9920b3fb85baf1.

Reason for revert: Blacklist more tests expected to fail. Fix up
                   run script.

Bug: 149098478
Test: art/test/testrunner/testrunner.py -b --host
Test: art/test/testrunner/testrunner.py -b --host -t 596-app-images -t 660-clinit
Test: art/test/testrunner/testrunner.py -b --host -t 597-app-images-same-classloader
Test: art/test/testrunner/testrunner.py -b --host --debuggable
Test: art/test/testrunner/testrunner.py -b --host --all-jvmti -t 596 -t 597-app-images-same-classloader
Test: art/test/testrunner/testrunner.py -b --target -t 596 -t 597-app-images-same-classloader
Change-Id: If84e1cb941e64be9907cd7e9ce1ec30ce32f46c7
diff --git a/test/130-hprof/run b/test/130-hprof/run
new file mode 100644
index 0000000..73a984e
--- /dev/null
+++ b/test/130-hprof/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 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.
+
+# Currently app images aren't unloaded when dex files are unloaded.
+exec ${RUN} $@ --no-secondary-app-image
diff --git a/test/138-duplicate-classes-check/src-art/Main.java b/test/138-duplicate-classes-check/src-art/Main.java
index b32f0bc..35d0209 100644
--- a/test/138-duplicate-classes-check/src-art/Main.java
+++ b/test/138-duplicate-classes-check/src-art/Main.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-import dalvik.system.DexClassLoader;
+import dalvik.system.PathClassLoader;
 import java.io.File;
 import java.lang.reflect.Method;
 
@@ -32,9 +32,8 @@
         // Now run the class from the -ex file.
 
         String dexPath = System.getenv("DEX_LOCATION") + "/138-duplicate-classes-check-ex.jar";
-        String optimizedDirectory = System.getenv("DEX_LOCATION");
         String librarySearchPath = null;
-        DexClassLoader loader = new DexClassLoader(dexPath, optimizedDirectory, librarySearchPath,
+        PathClassLoader loader = new PathClassLoader(dexPath, librarySearchPath,
                 getClass().getClassLoader());
 
         try {
diff --git a/test/141-class-unload/run b/test/141-class-unload/run
new file mode 100644
index 0000000..73a984e
--- /dev/null
+++ b/test/141-class-unload/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 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.
+
+# Currently app images aren't unloaded when dex files are unloaded.
+exec ${RUN} $@ --no-secondary-app-image
diff --git a/test/596-app-images/app_images.cc b/test/596-app-images/app_images.cc
index 498ea1d..1bc2c1e 100644
--- a/test/596-app-images/app_images.cc
+++ b/test/596-app-images/app_images.cc
@@ -27,6 +27,8 @@
 #include "gc/space/space-inl.h"
 #include "image.h"
 #include "mirror/class.h"
+#include "nativehelper/scoped_utf_chars.h"
+#include "oat_file.h"
 #include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
 
@@ -34,13 +36,36 @@
 
 namespace {
 
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_checkAppImageLoaded(JNIEnv*, jclass) {
+// Returns whether the extensionless basename of `location` is equal to name.
+// E.g. check_name("/foo/bar/baz.odex", "baz") == true,
+//      check_name("/foo/bar/baz.odex", "bar") == false
+static bool check_name(const std::string& location, const std::string& name) {
+  std::string loc_name = location;
+  size_t idx = loc_name.rfind('/');
+  if (idx != std::string::npos) {
+    loc_name = loc_name.substr(idx + 1);
+  }
+  idx = loc_name.rfind('.');
+  if (idx != std::string::npos) {
+    loc_name = loc_name.substr(0, idx);
+  }
+  return loc_name == name;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_checkAppImageLoaded(JNIEnv* env,
+                                                                    jclass,
+                                                                    jstring jimage_name) {
+  ScopedUtfChars image_name(env, jimage_name);
   ScopedObjectAccess soa(Thread::Current());
   for (auto* space : Runtime::Current()->GetHeap()->GetContinuousSpaces()) {
     if (space->IsImageSpace()) {
       auto* image_space = space->AsImageSpace();
       const auto& image_header = image_space->GetImageHeader();
-      if (image_header.IsAppImage()) {
+      // Check that this is an app image associated with the dex file named
+      // `jname` by verifying the extensionless basename of the odex file
+      // location is equal to `jname`.
+      if (image_header.IsAppImage() &&
+          check_name(image_space->GetOatFile()->GetLocation(), image_name.c_str())) {
         return JNI_TRUE;
       }
     }
diff --git a/test/596-app-images/expected.txt b/test/596-app-images/expected.txt
index 6a5618e..ff3be57 100644
--- a/test/596-app-images/expected.txt
+++ b/test/596-app-images/expected.txt
@@ -1 +1,2 @@
 JNI_OnLoad called
+Secondary went
diff --git a/test/596-app-images/profile b/test/596-app-images/profile
new file mode 100644
index 0000000..2a3172c
--- /dev/null
+++ b/test/596-app-images/profile
@@ -0,0 +1,7 @@
+LMain$Inner;
+LMain$Nested;
+LStaticFields;
+LStaticFieldsInitSub;
+LStaticFieldsInit;
+LStaticInternString;
+LSecondary;
diff --git a/test/596-app-images/run b/test/596-app-images/run
new file mode 100644
index 0000000..dbdcd1c
--- /dev/null
+++ b/test/596-app-images/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 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.
+
+# We need a profile to tell dex2oat to include classes in the final app image
+exec ${RUN} --profile $@
diff --git a/test/596-app-images/src-art/Main.java b/test/596-app-images/src-art/Main.java
new file mode 100644
index 0000000..8bc2f3f
--- /dev/null
+++ b/test/596-app-images/src-art/Main.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2020 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.PathClassLoader;
+import java.lang.reflect.Field;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+class Main {
+  static final String DEX_FILE = System.getenv("DEX_LOCATION") + "/596-app-images.jar";
+  static final String SECONDARY_DEX_FILE =
+    System.getenv("DEX_LOCATION") + "/596-app-images-ex.jar";
+  static final String LIBRARY_SEARCH_PATH = System.getProperty("java.library.path");
+
+  static class Inner {
+    final public static int abc = 10;
+  }
+
+  static class Nested {
+
+  }
+
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[0]);
+
+    testAppImageLoaded();
+    testInitializedClasses();
+    testInternedStrings();
+    testReloadInternedString();
+    testClassesOutsideAppImage();
+    testLoadingSecondaryAppImage();
+  }
+
+  public static native boolean checkAppImageLoaded(String name);
+  public static native boolean checkAppImageContains(Class<?> klass);
+  public static native boolean checkInitialized(Class<?> klass);
+
+  public static void testAppImageLoaded() throws Exception {
+    assertTrue("App image is loaded", checkAppImageLoaded("596-app-images"));
+    assertTrue("App image contains Inner", checkAppImageContains(Inner.class));
+  }
+
+  public static void testInitializedClasses() throws Exception {
+    assertInitialized(Inner.class);
+    assertInitialized(Nested.class);
+    assertInitialized(StaticFields.class);
+    assertInitialized(StaticFieldsInitSub.class);
+    assertInitialized(StaticFieldsInit.class);
+    assertInitialized(StaticInternString.class);
+  }
+
+  private static void assertInitialized(Class<?> klass) {
+    assertTrue(klass.toString() + " is preinitialized", checkInitialized(klass));
+  }
+
+  public static void testInternedStrings() throws Exception {
+    StringBuffer sb = new StringBuffer();
+    sb.append("java.");
+    sb.append("abc.");
+    sb.append("Action");
+
+    String tmp = sb.toString();
+    String intern = tmp.intern();
+
+    assertNotSame("Dynamically constructed string is not interned", tmp, intern);
+    assertEquals("Static string on initialized class is matches runtime interned string", intern,
+        StaticInternString.intent);
+    assertEquals("Static string on initialized class is pre-interned", BootInternedString.boot,
+        BootInternedString.boot.intern());
+
+    // TODO: Does this next check really provide us anything?
+    Field f = StaticInternString.class.getDeclaredField("intent");
+    assertEquals("String literals are interned properly", intern, f.get(null));
+
+    assertEquals("String literals are interned properly across classes",
+        StaticInternString.getIntent(), StaticInternString2.getIntent());
+  }
+
+  public static void testReloadInternedString() throws Exception {
+    // reload the class StaticInternString, check whether static strings interned properly
+    PathClassLoader loader = new PathClassLoader(DEX_FILE, LIBRARY_SEARCH_PATH, null);
+    Class<?> staticInternString = loader.loadClass("StaticInternString");
+    assertTrue("Class in app image isn't loaded a second time after loading dex file again",
+        checkAppImageContains(staticInternString));
+
+    Method getIntent = staticInternString.getDeclaredMethod("getIntent");
+    assertEquals("Interned strings are still interned after multiple dex loads",
+        StaticInternString.getIntent(), getIntent.invoke(staticInternString));
+  }
+
+  public static void testClassesOutsideAppImage() {
+    assertFalse("App image doesn't contain non-optimized class",
+        checkAppImageContains(NonOptimizedClass.class));
+    assertFalse("App image didn't pre-initialize non-optimized class",
+        checkInitialized(NonOptimizedClass.class));
+  }
+
+  public static void testLoadingSecondaryAppImage() throws Exception {
+    final ClassLoader parent = Main.class.getClassLoader();
+
+    // Sanity check that the image isn't already loaded so we don't get bogus results below
+    assertFalse("Secondary app image isn't already loaded",
+        checkAppImageLoaded("596-app-images-ex"));
+
+    PathClassLoader pcl = new PathClassLoader(SECONDARY_DEX_FILE, parent);
+
+    assertTrue("Ensure app image is loaded if it should be",
+        checkAppImageLoaded("596-app-images-ex"));
+
+    Class<?> secondaryCls = pcl.loadClass("Secondary");
+    assertTrue("Ensure Secondary class is in the app image if the CLC is correct",
+        checkAppImageContains(secondaryCls));
+    assertTrue("Ensure Secondary class is preinitialized if the CLC is correct",
+        checkInitialized(secondaryCls));
+
+    secondaryCls.getDeclaredMethod("go").invoke(null);
+  }
+
+  private static void assertTrue(String message, boolean flag) {
+    if (flag) {
+      return;
+    }
+    throw new AssertionError(message);
+  }
+
+  private static void assertEquals(String message, Object a, Object b) {
+    StringBuilder sb = new StringBuilder(message != null ? message  : "");
+    if (sb.length() > 0) {
+      sb.append(" ");
+    }
+    sb.append("expected:<").append(a).append("> but was:<").append(b).append(">");
+    assertTrue(sb.toString(), (a == null && b == null) || (a != null && a.equals(b)));
+  }
+
+  private static void assertFalse(String message, boolean flag) {
+    assertTrue(message, !flag);
+  }
+
+  private static void assertNotSame(String message, Object a, Object b) {
+    StringBuilder sb = new StringBuilder(message != null ? message  : "");
+    if (sb.length() > 0) {
+      sb.append(" ");
+    }
+    sb.append("unexpected sameness, found:<").append(a).append("> and:<").append(b).append(">");
+    assertTrue(sb.toString(), a != b);
+  }
+}
+
+class StaticFields {
+  public static int abc;
+}
+
+class StaticFieldsInitSub extends StaticFieldsInit {
+  final public static int def = 10;
+}
+
+class StaticFieldsInit {
+  final public static int abc = 10;
+}
+
+class StaticInternString {
+  final public static String intent = "java.abc.Action";
+  static public String getIntent() {
+    return intent;
+  }
+}
+
+class BootInternedString {
+  final public static String boot = "double";
+}
+
+class StaticInternString2 {
+  final public static String intent = "java.abc.Action";
+
+  static String getIntent() {
+    return intent;
+  }
+}
+
+class NonOptimizedClass {}
diff --git a/test/596-app-images/src-ex/Secondary.java b/test/596-app-images/src-ex/Secondary.java
new file mode 100644
index 0000000..36eee88
--- /dev/null
+++ b/test/596-app-images/src-ex/Secondary.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 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 Secondary {
+  public static void go() {
+    System.out.println("Secondary went");
+  }
+}
diff --git a/test/596-app-images/src/Main.java b/test/596-app-images/src/Main.java
deleted file mode 100644
index 88d95f4..0000000
--- a/test/596-app-images/src/Main.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright 2016 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 java.lang.reflect.Field;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-
-class Main {
-  static class Inner {
-    final public static int abc = 10;
-  }
-
-  static class Nested {
-
-  }
-
-  public static void main(String[] args) {
-    System.loadLibrary(args[0]);
-    if (!checkAppImageLoaded()) {
-      System.out.println("App image is not loaded!");
-    } else if (!checkAppImageContains(Inner.class)) {
-      System.out.println("App image does not contain Inner!");
-    }
-
-    if (!checkInitialized(Inner.class))
-      System.out.println("Inner class is not initialized!");
-
-    if (!checkInitialized(Nested.class))
-      System.out.println("Nested class is not initialized!");
-
-    if (!checkInitialized(StaticFields.class))
-      System.out.println("StaticFields class is not initialized!");
-
-    if (!checkInitialized(StaticFieldsInitSub.class))
-      System.out.println("StaticFieldsInitSub class is not initialized!");
-
-    if (!checkInitialized(StaticFieldsInit.class))
-      System.out.println("StaticFieldsInit class is not initialized!");
-
-    if (!checkInitialized(StaticInternString.class))
-      System.out.println("StaticInternString class is not initialized!");
-
-    StringBuffer sb = new StringBuffer();
-    sb.append("java.");
-    sb.append("abc.");
-    sb.append("Action");
-
-    String tmp = sb.toString();
-    String intern = tmp.intern();
-
-    assertNotEqual(tmp, intern, "Dynamically constructed String, not interned.");
-    assertEqual(intern, StaticInternString.intent, "Static encoded literal String not interned.");
-    assertEqual(BootInternedString.boot, BootInternedString.boot.intern(),
-        "Static encoded literal String not moved back to runtime intern table.");
-
-    try {
-      Field f = StaticInternString.class.getDeclaredField("intent");
-      assertEqual(intern, f.get(null), "String Literals are not interned properly.");
-
-    } catch (Exception e) {
-      System.out.println("Exception");
-    }
-
-    assertEqual(StaticInternString.getIntent(), StaticInternString2.getIntent(),
-        "String Literals are not intenred properly, App image static strings duplicated.");
-
-    // reload the class StaticInternString, check whether static strings interned properly
-    final String DEX_FILE = System.getenv("DEX_LOCATION") + "/596-app-images.jar";
-    final String LIBRARY_SEARCH_PATH = System.getProperty("java.library.path");
-
-    try {
-      Class<?> pathClassLoader = Class.forName("dalvik.system.PathClassLoader");
-      if (pathClassLoader == null) {
-        throw new AssertionError("Counldn't find path class loader class");
-      }
-      Constructor<?> ctor =
-          pathClassLoader.getDeclaredConstructor(String.class, String.class, ClassLoader.class);
-      ClassLoader loader = (ClassLoader) ctor.newInstance(
-          DEX_FILE, LIBRARY_SEARCH_PATH, null);
-
-      Class<?> staticInternString = loader.loadClass("StaticInternString");
-
-      if (!checkAppImageContains(staticInternString)) {
-        System.out.println("Not loaded again.");
-      }
-      Method getIntent = staticInternString.getDeclaredMethod("getIntent");
-
-      assertEqual(StaticInternString.getIntent(), getIntent.invoke(staticInternString),
-          "Dynamically loaded app image's literal strings not interned properly.");
-    } catch (Exception e) {
-      e.printStackTrace(System.out);
-    }
-
-  }
-
-  public static native boolean checkAppImageLoaded();
-  public static native boolean checkAppImageContains(Class<?> klass);
-  public static native boolean checkInitialized(Class<?> klass);
-
-  public static void assertEqual(Object a, Object b, String msg) {
-    if (a != b)
-      System.out.println(msg);
-  }
-
-  public static void assertNotEqual(Object a, Object b, String msg) {
-    if (a == b)
-      System.out.println(msg);
-  }
-
-}
-
-class StaticFields{
-  public static int abc;
-}
-
-class StaticFieldsInitSub extends StaticFieldsInit {
-  final public static int def = 10;
-}
-
-class StaticFieldsInit{
-  final public static int abc = 10;
-}
-
-class StaticInternString {
-  final public static String intent = "java.abc.Action";
-  static public String getIntent() {
-    return intent;
-  }
-}
-
-class BootInternedString {
-  final public static String boot = "double";
-}
-
-class StaticInternString2 {
-  final public static String intent = "java.abc.Action";
-
-  static String getIntent() {
-    return intent;
-  }
-}
-
diff --git a/test/597-app-images-same-classloader/expected.txt b/test/597-app-images-same-classloader/expected.txt
new file mode 100644
index 0000000..ff3be57
--- /dev/null
+++ b/test/597-app-images-same-classloader/expected.txt
@@ -0,0 +1,2 @@
+JNI_OnLoad called
+Secondary went
diff --git a/test/597-app-images-same-classloader/info.txt b/test/597-app-images-same-classloader/info.txt
new file mode 100644
index 0000000..38f6e70
--- /dev/null
+++ b/test/597-app-images-same-classloader/info.txt
@@ -0,0 +1,2 @@
+Tests that loading an app image into an already existent classloader works if
+the classloader context is correct.
diff --git a/test/597-app-images-same-classloader/profile b/test/597-app-images-same-classloader/profile
new file mode 100644
index 0000000..c7406e2
--- /dev/null
+++ b/test/597-app-images-same-classloader/profile
@@ -0,0 +1,2 @@
+LMain;
+LSecondary;
diff --git a/test/597-app-images-same-classloader/run b/test/597-app-images-same-classloader/run
new file mode 100644
index 0000000..496273f
--- /dev/null
+++ b/test/597-app-images-same-classloader/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 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.
+
+# We need a profile to tell dex2oat to include classes in the final app image
+exec ${RUN} --profile --secondary-class-loader-context "PCL[$DEX_LOCATION/$TEST_NAME.jar]" $@
diff --git a/test/597-app-images-same-classloader/src-art/Main.java b/test/597-app-images-same-classloader/src-art/Main.java
new file mode 100644
index 0000000..96a902c
--- /dev/null
+++ b/test/597-app-images-same-classloader/src-art/Main.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 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.PathClassLoader;
+
+class Main {
+  static final String TEST_NAME = "597-app-images-same-classloader";
+
+  static final String DEX_FILE = System.getenv("DEX_LOCATION") + "/" + TEST_NAME + ".jar";
+  static final String LIBRARY_SEARCH_PATH = System.getProperty("java.library.path");
+
+  static final String SECONDARY_NAME = TEST_NAME + "-ex";
+  static final String SECONDARY_DEX_FILE =
+    System.getenv("DEX_LOCATION") + "/" + SECONDARY_NAME + ".jar";
+
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[0]);
+
+    testLoadingSecondaryAppImageInLoadedClassLoader();
+  }
+
+  public static native boolean checkAppImageLoaded(String name);
+  public static native boolean checkAppImageContains(Class<?> klass);
+  public static native boolean checkInitialized(Class<?> klass);
+
+  public static void testLoadingSecondaryAppImageInLoadedClassLoader() throws Exception {
+    // Sanity check that the image isn't already loaded so we don't get bogus results below
+    assertFalse("Secondary app image isn't already loaded",
+        checkAppImageLoaded(SECONDARY_NAME));
+
+    PathClassLoader pcl = new PathClassLoader(DEX_FILE, LIBRARY_SEARCH_PATH, null);
+    pcl.addDexPath(SECONDARY_DEX_FILE);
+
+    assertTrue("Ensure app image is loaded if it should be",
+        checkAppImageLoaded(SECONDARY_NAME));
+
+    Class<?> secondaryCls = pcl.loadClass("Secondary");
+    assertTrue("Ensure Secondary class is in the app image",
+        checkAppImageContains(secondaryCls));
+    assertTrue("Ensure Secondary class is preinitialized", checkInitialized(secondaryCls));
+
+    secondaryCls.getDeclaredMethod("go").invoke(null);
+  }
+
+  private static void assertTrue(String message, boolean flag) {
+    if (flag) {
+      return;
+    }
+    throw new AssertionError(message);
+  }
+
+  private static void assertFalse(String message, boolean flag) {
+    assertTrue(message, !flag);
+  }
+}
diff --git a/test/597-app-images-same-classloader/src-ex/Secondary.java b/test/597-app-images-same-classloader/src-ex/Secondary.java
new file mode 100644
index 0000000..36eee88
--- /dev/null
+++ b/test/597-app-images-same-classloader/src-ex/Secondary.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 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 Secondary {
+  public static void go() {
+    System.out.println("Secondary went");
+  }
+}
diff --git a/test/660-clinit/src/Main.java b/test/660-clinit/src/Main.java
index 5fb5fe5..0d6837e 100644
--- a/test/660-clinit/src/Main.java
+++ b/test/660-clinit/src/Main.java
@@ -21,7 +21,7 @@
   public static void main(String[] args) throws Exception {
     System.loadLibrary(args[0]);
 
-    if (!checkAppImageLoaded()) {
+    if (!checkAppImageLoaded("660-clinit")) {
       System.out.println("AppImage not loaded.");
     }
     if (!checkAppImageContains(ClInit.class)) {
@@ -90,7 +90,7 @@
     }
   }
 
-  public static native boolean checkAppImageLoaded();
+  public static native boolean checkAppImageLoaded(String name);
   public static native boolean checkAppImageContains(Class<?> klass);
   public static native boolean checkInitialized(Class<?> klass);
 }
diff --git a/test/674-hiddenapi/run b/test/674-hiddenapi/run
index 2babeef..b57d10a 100755
--- a/test/674-hiddenapi/run
+++ b/test/674-hiddenapi/run
@@ -16,4 +16,7 @@
 
 # Make verification soft fail so that we can re-verify boot classpath
 # methods at runtime.
-exec ${RUN} $@ --verify-soft-fail
\ No newline at end of file
+#
+# N.B. Compilation of secondary dexes can prevent hidden API checks, e.g. if
+# a blacklisted field get is inlined.
+exec ${RUN} $@ --verify-soft-fail --no-secondary-compilation
diff --git a/test/719-dm-verify-redefinition/run b/test/719-dm-verify-redefinition/run
index f4e9d71..26e10e1 100644
--- a/test/719-dm-verify-redefinition/run
+++ b/test/719-dm-verify-redefinition/run
@@ -23,4 +23,5 @@
   --dm \
   --vdex-arg \
   --class-loader-context=PCL[$DEX_LOCATION/719-dm-verify-redefinition-ex.jar] \
+  --no-secondary-compilation \
   "${@}"
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 3701220..2a8a7d4 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -85,6 +85,9 @@
 TEST_DM="n"
 TEST_IS_NDEBUG="n"
 APP_IMAGE="y"
+SECONDARY_APP_IMAGE="y"
+SECONDARY_CLASS_LOADER_CONTEXT=""
+SECONDARY_COMPILATION="y"
 JVMTI_STRESS="n"
 JVMTI_STEP_STRESS="n"
 JVMTI_FIELD_STRESS="n"
@@ -131,6 +134,8 @@
     elif [ "x$1" = "x--jvmti" ]; then
         USE_JVMTI="y"
         IS_JVMTI_TEST="y"
+        # Secondary images block some tested behavior.
+        SECONDARY_APP_IMAGE="n"
         shift
     elif [ "x$1" = "x--add-libdir-argument" ]; then
         ADD_LIBDIR_ARGUMENTS="y"
@@ -216,6 +221,7 @@
         # APP_IMAGE doesn't really work with jvmti redefine stress
         USE_JVMTI="y"
         APP_IMAGE="n"
+        SECONDARY_APP_IMAGE="n"
         JVMTI_STRESS="y"
         JVMTI_REDEFINE_STRESS="y"
         shift
@@ -237,6 +243,16 @@
     elif [ "x$1" = "x--no-app-image" ]; then
         APP_IMAGE="n"
         shift
+    elif [ "x$1" = "x--no-secondary-app-image" ]; then
+        SECONDARY_APP_IMAGE="n"
+        shift
+    elif [ "x$1" = "x--secondary-class-loader-context" ]; then
+        shift
+        SECONDARY_CLASS_LOADER_CONTEXT="$1"
+        shift
+    elif [ "x$1" = "x--no-secondary-compilation" ]; then
+        SECONDARY_COMPILATION="n"
+        shift
     elif [ "x$1" = "x--strip-dex" ]; then
         STRIP_DEX="y"
         shift
@@ -898,7 +914,7 @@
   profman_cmdline="$ANDROID_ART_BIN_DIR/profman  \
     --apk=$DEX_LOCATION/$TEST_NAME.jar \
     --dex-location=$DEX_LOCATION/$TEST_NAME.jar"
-  if [ -f $DEX_LOCATION/$TEST_NAME-ex.jar ]; then
+  if [ -f "$TEST_NAME-ex.jar" ] && [ "$SECONDARY_COMPILATION" = "y" ] ; then
     profman_cmdline="${profman_cmdline} \
       --apk=$DEX_LOCATION/$TEST_NAME-ex.jar \
       --dex-location=$DEX_LOCATION/$TEST_NAME-ex.jar"
@@ -914,16 +930,27 @@
   fi
 fi
 
-# Enable mini-debug-info for JIT (if JIT is used).
-FLAGS="$FLAGS -Xcompiler-option --generate-mini-debug-info"
+function write_dex2oat_cmdlines {
+  local name="$1"
 
-if [ "$PREBUILD" = "y" ]; then
-  mkdir_locations="${mkdir_locations} ${DEX_LOCATION}/oat/$ISA"
+  local class_loader_context=""
+  local enable_app_image=false
   if [ "$APP_IMAGE" = "y" ]; then
-    # Pick a base that will force the app image to get relocated.
-    app_image="--app-image-file=$DEX_LOCATION/oat/$ISA/$TEST_NAME.art --resolve-startup-const-strings=true"
+    enable_app_image=true
   fi
 
+  # If the name ends in -ex then this is a secondary dex file
+  if [ "${name:${#name}-3}" = "-ex" ]; then
+    # Lazily realize the default value in case DEX_LOCATION/TEST_NAME change
+    [ -z "$SECONDARY_CLASS_LOADER_CONTEXT" ] && SECONDARY_CLASS_LOADER_CONTEXT="PCL[];PCL[$DEX_LOCATION/$TEST_NAME.jar]"
+    class_loader_context="'--class-loader-context=$SECONDARY_CLASS_LOADER_CONTEXT'"
+    $enable_app_image && [ "$SECONDARY_APP_IMAGE" = "y" ] || enable_app_image=false
+  fi
+
+  local app_image=""
+  $enable_app_image && app_image="--app-image-file=$DEX_LOCATION/oat/$ISA/$name.art --resolve-startup-const-strings=true"
+
+  local dex2oat_binary
   dex2oat_binary=${DEX2OAT_DEBUG_BINARY}
   if  [[ "$TEST_IS_NDEBUG" = "y" ]]; then
     dex2oat_binary=${DEX2OAT_NDEBUG_BINARY}
@@ -931,11 +958,12 @@
   dex2oat_cmdline="$INVOKE_WITH $ANDROID_ART_BIN_DIR/$dex2oat_binary \
                       $COMPILE_FLAGS \
                       --boot-image=${BOOT_IMAGE} \
-                      --dex-file=$DEX_LOCATION/$TEST_NAME.jar \
-                      --oat-file=$DEX_LOCATION/oat/$ISA/$TEST_NAME.odex \
-                      ${app_image} \
+                      --dex-file=$DEX_LOCATION/$name.jar \
+                      --oat-file=$DEX_LOCATION/oat/$ISA/$name.odex \
+                      "$app_image" \
                       --generate-mini-debug-info \
-                      --instruction-set=$ISA"
+                      --instruction-set=$ISA \
+                      $class_loader_context"
   if [ "x$INSTRUCTION_SET_FEATURES" != "x" ] ; then
     dex2oat_cmdline="${dex2oat_cmdline} --instruction-set-features=${INSTRUCTION_SET_FEATURES}"
   fi
@@ -952,15 +980,48 @@
     dex2oat_cmdline="timeout -k ${DEX2OAT_TIMEOUT}s -s SIGRTMIN+2 ${DEX2OAT_RT_TIMEOUT}s ${dex2oat_cmdline} --watchdog-timeout=${DEX2OAT_TIMEOUT}000"
   fi
   if [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then
-    vdex_cmdline="${dex2oat_cmdline} ${VDEX_ARGS} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex --output-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex"
+    vdex_cmdline="${dex2oat_cmdline} ${VDEX_ARGS} --input-vdex=$DEX_LOCATION/oat/$ISA/$name.vdex --output-vdex=$DEX_LOCATION/oat/$ISA/$name.vdex"
   elif [ "$TEST_VDEX" = "y" ]; then
-    vdex_cmdline="${dex2oat_cmdline} ${VDEX_ARGS} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex"
+    vdex_cmdline="${dex2oat_cmdline} ${VDEX_ARGS} --input-vdex=$DEX_LOCATION/oat/$ISA/$name.vdex"
   elif [ "$TEST_DM" = "y" ]; then
     dex2oat_cmdline="${dex2oat_cmdline} --output-vdex=$DEX_LOCATION/oat/$ISA/primary.vdex"
-    dm_cmdline="zip -qj $DEX_LOCATION/oat/$ISA/$TEST_NAME.dm $DEX_LOCATION/oat/$ISA/primary.vdex"
-    vdex_cmdline="${dex2oat_cmdline} ${VDEX_ARGS} --dump-timings --dm-file=$DEX_LOCATION/oat/$ISA/$TEST_NAME.dm"
-  elif [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then
-    vdex_cmdline="${dex2oat_cmdline} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex --output-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex"
+    dm_cmdline="zip -qj $DEX_LOCATION/oat/$ISA/$name.dm $DEX_LOCATION/oat/$ISA/primary.vdex"
+    vdex_cmdline="${dex2oat_cmdline} ${VDEX_ARGS} --dump-timings --dm-file=$DEX_LOCATION/oat/$ISA/$name.dm"
+  fi
+}
+
+# Enable mini-debug-info for JIT (if JIT is used).
+FLAGS="$FLAGS -Xcompiler-option --generate-mini-debug-info"
+
+if [ "$PREBUILD" = "y" ]; then
+  mkdir_locations="${mkdir_locations} ${DEX_LOCATION}/oat/$ISA"
+
+  # "Primary".
+  write_dex2oat_cmdlines "$TEST_NAME"
+  dex2oat_cmdline=$(echo $dex2oat_cmdline)
+  dm_cmdline=$(echo $dm_cmdline)
+  vdex_cmdline=$(echo $vdex_cmdline)
+
+  # Enable mini-debug-info for JIT (if JIT is used).
+  FLAGS="$FLAGS -Xcompiler-option --generate-mini-debug-info"
+
+  if [ -f "$TEST_NAME-ex.jar" ] && [ "$SECONDARY_COMPILATION" = "y" ] ; then
+    # "Secondary" for test coverage.
+
+    # Store primary values.
+    base_dex2oat_cmdline="$dex2oat_cmdline"
+    base_dm_cmdline="$dm_cmdline"
+    base_vdex_cmdline="$vdex_cmdline"
+
+    write_dex2oat_cmdlines "$TEST_NAME-ex"
+    dex2oat_cmdline=$(echo $dex2oat_cmdline)
+    dm_cmdline=$(echo $dm_cmdline)
+    vdex_cmdline=$(echo $vdex_cmdline)
+
+    # Concatenate.
+    dex2oat_cmdline="$base_dex2oat_cmdline && $dex2oat_cmdline"
+    dm_cmdline="$base_dm_cmdline" # Only use primary dm.
+    vdex_cmdline="$base_vdex_cmdline && $vdex_cmdline"
   fi
 fi
 
@@ -1009,11 +1070,22 @@
                   -XX:DumpNativeStackOnSigQuit:false \
                   -cp $DEX_LOCATION/$TEST_NAME.jar$SECONDARY_DEX $MAIN $ARGS"
 
+sanitize_dex2oat_cmdline() {
+  local args=()
+  for arg in "$@"; do
+    if [ "$arg" = "--class-loader-context=&" ]; then
+      arg="--class-loader-context=\&"
+    fi
+    args+=("$arg")
+  done
+  echo -n "${args[@]}"
+}
+
 # Remove whitespace.
-dex2oat_cmdline=$(echo $dex2oat_cmdline)
+dex2oat_cmdline=$(sanitize_dex2oat_cmdline $(echo $dex2oat_cmdline))
 dalvikvm_cmdline=$(echo $dalvikvm_cmdline)
 dm_cmdline=$(echo $dm_cmdline)
-vdex_cmdline=$(echo $vdex_cmdline)
+vdex_cmdline=$(sanitize_dex2oat_cmdline $(echo $vdex_cmdline))
 profman_cmdline=$(echo $profman_cmdline)
 
 # Use an empty ASAN_OPTIONS to enable defaults.
@@ -1274,9 +1346,9 @@
     $linkroot_cmdline || { echo "create symlink android-root failed." >&2 ; exit 2; }
     $linkroot_overlay_cmdline || { echo "overlay android-root failed." >&2 ; exit 2; }
     $profman_cmdline || { echo "Profman failed." >&2 ; exit 2; }
-    $dex2oat_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
-    $dm_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
-    $vdex_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
+    eval "$dex2oat_cmdline" || { echo "Dex2oat failed." >&2 ; exit 2; }
+    eval "$dm_cmdline" || { echo "Dex2oat failed." >&2 ; exit 2; }
+    eval "$vdex_cmdline" || { echo "Dex2oat failed." >&2 ; exit 2; }
     $strip_cmdline || { echo "Strip failed." >&2 ; exit 3; }
     $sync_cmdline || { echo "Sync failed." >&2 ; exit 4; }
 
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 9dc5f26..4501779 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -304,8 +304,23 @@
     },
     {
         "tests": "596-app-images",
-        "description": "Code being tested has been disabled",
-        "bug": "b/70734839"
+        "description": "app images do not initialize classes when debuggable",
+        "variant": "debuggable"
+    },
+    {
+        "tests": "596-app-images",
+        "description": "not generated when using the access check configuration",
+        "variant": "interp-ac"
+    },
+    {
+        "tests": "597-app-images-same-classloader",
+        "description": "not generated when using the access check configuration",
+        "variant": "interp-ac"
+    },
+    {
+        "tests": ["596-app-images", "597-app-images-same-classloader"],
+        "description": "jvmti does not interact well with images",
+        "variant": "field-stress | jvmti-stress | redefine-stress | step-stress | trace-stress"
     },
     {
         "tests": "055-enum-performance",