Support background verification of secondary dex files.

Re-use the infrastructure for background verification of in-memory dex
files to also trigger for secondary dex files.

Test: 692-vdex-secondary-loader
Bug: 160294863
Change-Id: I754d519b6a903c51e439ccab100d2d8f22f45df3
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 3811ed9..1c4adab 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -326,27 +326,10 @@
   return CreateCookieFromOatFileManagerResult(env, dex_files, oat_file, error_msgs);
 }
 
-static jstring DexFile_getClassLoaderContext(JNIEnv* env,
-                                            jclass,
-                                            jobject class_loader,
-                                            jobjectArray dex_elements) {
-  CHECK(class_loader != nullptr);
-  constexpr const char* kBaseDir = "";
-  std::unique_ptr<ClassLoaderContext> context =
-  ClassLoaderContext::CreateContextForClassLoader(class_loader, dex_elements);
-  if (context == nullptr || !context->OpenDexFiles(kBaseDir)) {
-    LOG(WARNING) << "Could not establish class loader context";
-    return nullptr;
-  }
-  std::string str_context = context->EncodeContextForOatFile(kBaseDir);
-  return env->NewStringUTF(str_context.c_str());
-}
-
 static void DexFile_verifyInBackgroundNative(JNIEnv* env,
                                              jclass,
                                              jobject cookie,
-                                             jobject class_loader,
-                                             jstring class_loader_context) {
+                                             jobject class_loader) {
   CHECK(cookie != nullptr);
   CHECK(class_loader != nullptr);
 
@@ -359,17 +342,10 @@
   }
   CHECK(oat_file == nullptr) << "Called verifyInBackground on a dex file backed by oat";
 
-  ScopedUtfChars class_loader_context_utf(env, class_loader_context);
-  if (env->ExceptionCheck()) {
-    LOG(ERROR) << "Failed to unwrap class loader context string";
-    return;
-  }
-
   // Hand over to OatFileManager to spawn a verification thread.
   Runtime::Current()->GetOatFileManager().RunBackgroundVerification(
       dex_files,
-      class_loader,
-      class_loader_context_utf.c_str());
+      class_loader);
 }
 
 static jboolean DexFile_closeDexFile(JNIEnv* env, jclass, jobject cookie) {
@@ -954,14 +930,9 @@
                 "Ljava/lang/ClassLoader;"
                 "[Ldalvik/system/DexPathList$Element;"
                 ")Ljava/lang/Object;"),
-  NATIVE_METHOD(DexFile, getClassLoaderContext,
-                "(Ljava/lang/ClassLoader;"
-                "[Ldalvik/system/DexPathList$Element;"
-                ")Ljava/lang/String;"),
   NATIVE_METHOD(DexFile, verifyInBackgroundNative,
                 "(Ljava/lang/Object;"
                 "Ljava/lang/ClassLoader;"
-                "Ljava/lang/String;"
                 ")V"),
   NATIVE_METHOD(DexFile, isValidCompilerFilter, "(Ljava/lang/String;)Z"),
   NATIVE_METHOD(DexFile, isProfileGuidedCompilerFilter, "(Ljava/lang/String;)Z"),
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 8547d54..7a82899 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -21,6 +21,7 @@
 #include <vector>
 #include <sys/stat.h>
 
+#include "android-base/file.h"
 #include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 
@@ -469,16 +470,6 @@
   return headers;
 }
 
-static std::vector<const DexFile::Header*> GetDexFileHeaders(
-    const std::vector<const DexFile*>& dex_files) {
-  std::vector<const DexFile::Header*> headers;
-  headers.reserve(dex_files.size());
-  for (const DexFile* dex_file : dex_files) {
-    headers.push_back(&dex_file->GetHeader());
-  }
-  return headers;
-}
-
 std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
     std::vector<MemMap>&& dex_mem_maps,
     jobject class_loader,
@@ -602,6 +593,12 @@
 // recently used one(s) (according to stat-reported atime).
 static bool UnlinkLeastRecentlyUsedVdexIfNeeded(const std::string& vdex_path_to_add,
                                                 std::string* error_msg) {
+  std::string basename = android::base::Basename(vdex_path_to_add);
+  if (!OatFileAssistant::IsAnonymousVdexBasename(basename)) {
+    // File is not for in memory dex files.
+    return true;
+  }
+
   if (OS::FileExists(vdex_path_to_add.c_str())) {
     // File already exists and will be overwritten.
     // This will not change the number of entries in the cache.
@@ -628,7 +625,7 @@
     if (de->d_type != DT_REG) {
       continue;
     }
-    std::string basename = de->d_name;
+    basename = de->d_name;
     if (!OatFileAssistant::IsAnonymousVdexBasename(basename)) {
       continue;
     }
@@ -666,10 +663,8 @@
  public:
   BackgroundVerificationTask(const std::vector<const DexFile*>& dex_files,
                              jobject class_loader,
-                             const char* class_loader_context,
                              const std::string& vdex_path)
       : dex_files_(dex_files),
-        class_loader_context_(class_loader_context),
         vdex_path_(vdex_path) {
     Thread* const self = Thread::Current();
     ScopedObjectAccess soa(self);
@@ -757,15 +752,13 @@
  private:
   const std::vector<const DexFile*> dex_files_;
   jobject class_loader_;
-  const std::string class_loader_context_;
   const std::string vdex_path_;
 
   DISALLOW_COPY_AND_ASSIGN(BackgroundVerificationTask);
 };
 
 void OatFileManager::RunBackgroundVerification(const std::vector<const DexFile*>& dex_files,
-                                               jobject class_loader,
-                                               const char* class_loader_context) {
+                                               jobject class_loader) {
   Runtime* const runtime = Runtime::Current();
   Thread* const self = Thread::Current();
 
@@ -786,23 +779,40 @@
     return;
   }
 
-  std::string dex_location;
-  std::string vdex_path;
-  if (OatFileAssistant::AnonymousDexVdexLocation(GetDexFileHeaders(dex_files),
-                                                 kRuntimeISA,
-                                                 &dex_location,
-                                                 &vdex_path)) {
-    if (verification_thread_pool_ == nullptr) {
-      verification_thread_pool_.reset(
-          new ThreadPool("Verification thread pool", /* num_threads= */ 1));
-      verification_thread_pool_->StartWorkers(self);
-    }
-    verification_thread_pool_->AddTask(self, new BackgroundVerificationTask(
-        dex_files,
-        class_loader,
-        class_loader_context,
-        vdex_path));
+  if (dex_files.size() < 1) {
+    // Nothing to verify.
+    return;
   }
+
+  std::string dex_location = dex_files[0]->GetLocation();
+  const std::string& data_dir = Runtime::Current()->GetProcessDataDirectory();
+  if (!android::base::StartsWith(dex_location, data_dir)) {
+    // For now, we only run background verification for secondary dex files.
+    // Running it for primary or split APKs could have some undesirable
+    // side-effects, like overloading the device on app startup.
+    return;
+  }
+
+  std::string error_msg;
+  std::string odex_filename;
+  if (!OatFileAssistant::DexLocationToOdexFilename(dex_location,
+                                                   kRuntimeISA,
+                                                   &odex_filename,
+                                                   &error_msg)) {
+    LOG(WARNING) << "Could not get odex filename for " << dex_location << ": " << error_msg;
+    return;
+  }
+
+  std::string vdex_filename = GetVdexFilename(odex_filename);
+  if (verification_thread_pool_ == nullptr) {
+    verification_thread_pool_.reset(
+        new ThreadPool("Verification thread pool", /* num_threads= */ 1));
+    verification_thread_pool_->StartWorkers(self);
+  }
+  verification_thread_pool_->AddTask(self, new BackgroundVerificationTask(
+      dex_files,
+      class_loader,
+      vdex_filename));
 }
 
 void OatFileManager::WaitForWorkersToBeCreated() {
diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h
index 11eabe2..294a916 100644
--- a/runtime/oat_file_manager.h
+++ b/runtime/oat_file_manager.h
@@ -124,8 +124,7 @@
 
   // Spawn a background thread which verifies all classes in the given dex files.
   void RunBackgroundVerification(const std::vector<const DexFile*>& dex_files,
-                                 jobject class_loader,
-                                 const char* class_loader_context);
+                                 jobject class_loader);
 
   // Wait for thread pool workers to be created. This is used during shutdown as
   // threads are not allowed to attach while runtime is in shutdown lock.
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index 02a1563..4e54c0d 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -269,7 +269,8 @@
 
   // Write checksum section.
   for (const DexFile* dex_file : dex_files) {
-    const uint32_t* checksum_ptr = &dex_file->GetHeader().checksum_;
+    uint32_t checksum = dex_file->GetLocationChecksum();
+    const uint32_t* checksum_ptr = &checksum;
     static_assert(sizeof(*checksum_ptr) == sizeof(VdexFile::VdexChecksum));
     if (!out->WriteFully(reinterpret_cast<const char*>(checksum_ptr),
                          sizeof(VdexFile::VdexChecksum))) {
diff --git a/test/692-vdex-inmem-loader/vdex_inmem_loader.cc b/test/692-vdex-inmem-loader/vdex_inmem_loader.cc
index aeb478e..1c7b6e8 100644
--- a/test/692-vdex-inmem-loader/vdex_inmem_loader.cc
+++ b/test/692-vdex-inmem-loader/vdex_inmem_loader.cc
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "base/file_utils.h"
 #include "class_loader_utils.h"
 #include "jni.h"
 #include "nativehelper/scoped_utf_chars.h"
@@ -81,23 +82,27 @@
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::ClassLoader> h_loader = hs.NewHandle(soa.Decode<mirror::ClassLoader>(loader));
 
-  std::vector<const DexFile::Header*> dex_headers;
+  std::vector<const DexFile*> dex_files;
   VisitClassLoaderDexFiles(
       soa,
       h_loader,
       [&](const DexFile* dex_file) {
-        dex_headers.push_back(&dex_file->GetHeader());
+        dex_files.push_back(dex_file);
         return true;
       });
 
-  std::string dex_location;
-  std::string vdex_filename;
+  std::string dex_location = dex_files[0]->GetLocation();
+  std::string odex_filename;
   std::string error_msg;
-  return OatFileAssistant::AnonymousDexVdexLocation(dex_headers,
-                                                    kRuntimeISA,
-                                                    &dex_location,
-                                                    &vdex_filename) &&
-         OS::FileExists(vdex_filename.c_str());
+  if (!OatFileAssistant::DexLocationToOdexFilename(dex_location,
+                                                   kRuntimeISA,
+                                                   &odex_filename,
+                                                   &error_msg)) {
+    LOG(WARNING) << "Could not get odex filename for " << dex_location << ": " << error_msg;
+    return false;
+  }
+
+  return OS::FileExists(GetVdexFilename(odex_filename).c_str());
 }
 
 extern "C" JNIEXPORT jboolean JNICALL Java_Main_isBackedByOatFile(JNIEnv*,
diff --git a/test/692-vdex-secondary-loader/expected-stderr.txt b/test/692-vdex-secondary-loader/expected-stderr.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/692-vdex-secondary-loader/expected-stderr.txt
diff --git a/test/692-vdex-secondary-loader/expected-stdout.txt b/test/692-vdex-secondary-loader/expected-stdout.txt
new file mode 100644
index 0000000..a127604
--- /dev/null
+++ b/test/692-vdex-secondary-loader/expected-stdout.txt
@@ -0,0 +1,4 @@
+JNI_OnLoad called
+Hello
+Hello
+Hello
diff --git a/test/692-vdex-secondary-loader/info.txt b/test/692-vdex-secondary-loader/info.txt
new file mode 100644
index 0000000..db23562
--- /dev/null
+++ b/test/692-vdex-secondary-loader/info.txt
@@ -0,0 +1,3 @@
+Test that dex files loaded with PathClassClassLoader get verified and the verification results
+cached in a vdex file. Subsequent loads should initialize an instance of
+OatFile using the data in the vdex.
diff --git a/test/692-vdex-secondary-loader/run b/test/692-vdex-secondary-loader/run
new file mode 100644
index 0000000..0732095
--- /dev/null
+++ b/test/692-vdex-secondary-loader/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2021 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.
+
+# Disable dex2oat of secondary dex files.
+exec ${RUN} $@ --no-secondary-compilation
diff --git a/test/692-vdex-secondary-loader/src-ex/art/ClassA.java b/test/692-vdex-secondary-loader/src-ex/art/ClassA.java
new file mode 100644
index 0000000..7236216
--- /dev/null
+++ b/test/692-vdex-secondary-loader/src-ex/art/ClassA.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+package art;
+
+public class ClassA {
+  public static String getHello() {
+    return "Hello";
+  }
+}
diff --git a/test/692-vdex-secondary-loader/src-ex/art/ClassB.java b/test/692-vdex-secondary-loader/src-ex/art/ClassB.java
new file mode 100644
index 0000000..9d62c07
--- /dev/null
+++ b/test/692-vdex-secondary-loader/src-ex/art/ClassB.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+package art;
+
+public class ClassB {
+  public static void printHello() {
+    System.out.println(ClassA.getHello());
+  }
+}
diff --git a/test/692-vdex-secondary-loader/src/Main.java b/test/692-vdex-secondary-loader/src/Main.java
new file mode 100644
index 0000000..ed53faf
--- /dev/null
+++ b/test/692-vdex-secondary-loader/src/Main.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2021 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.Method;
+import java.io.File;
+import java.nio.ByteBuffer;
+import java.util.Base64;
+
+public class Main {
+  private static void check(boolean expected, boolean actual, String message) {
+    if (expected != actual) {
+      System.err.println(
+          "ERROR: " + message + " (expected=" + expected + ", actual=" + actual + ")");
+      throw new Error("");
+    }
+  }
+
+  private static ClassLoader singleLoader() {
+    return new PathClassLoader(DEX_EXTRA, /*parent*/null);
+  }
+
+  private static void test(ClassLoader loader,
+                           boolean expectedHasVdexFile,
+                           boolean expectedBackedByOat,
+                           boolean invokeMethod) throws Exception {
+    // If ART created a vdex file, it must have verified all the classes.
+    // That happens if and only if we expect a vdex at the end of the test but
+    // do not expect it to have been loaded.
+    boolean expectedClassesVerified = expectedHasVdexFile && !expectedBackedByOat;
+
+    waitForVerifier();
+    check(expectedClassesVerified, areClassesVerified(loader), "areClassesVerified");
+    check(expectedHasVdexFile, hasVdexFile(loader), "hasVdexFile");
+    check(expectedBackedByOat, isBackedByOatFile(loader), "isBackedByOatFile");
+    check(expectedBackedByOat, areClassesPreverified(loader), "areClassesPreverified");
+
+    if (invokeMethod) {
+      loader.loadClass("art.ClassB").getDeclaredMethod("printHello").invoke(null);
+    }
+
+    if (expectedBackedByOat) {
+      String filter = getCompilerFilter(loader.loadClass("art.ClassB"));
+      if (!("verify".equals(filter))) {
+        throw new Error("Expected verify, got " + filter);
+      }
+    }
+  }
+
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[0]);
+
+    // Feature is disabled in debuggable mode because runtime threads are not
+    // allowed to load classes.
+    boolean featureEnabled = !isDebuggable();
+
+    // SDK version not set. Background verification job should not have run
+    // and vdex should not have been created.
+    test(singleLoader(), /*hasVdex*/ false, /*backedByOat*/ false, /*invokeMethod*/ true);
+
+    // Feature only enabled for target SDK version Q and later.
+    setTargetSdkVersion(/* Q */ 29);
+
+    // SDK version directory is now set. Background verification job should have run,
+    // should have verified classes and written results to a vdex.
+    test(singleLoader(), /*hasVdex*/ featureEnabled, /*backedByOat*/ false, /*invokeMethod*/ true);
+    test(singleLoader(), /*hasVdex*/ featureEnabled, /*backedByOat*/ featureEnabled,
+        /*invokeMethod*/ true);
+  }
+
+  private static native boolean isDebuggable();
+  private static native int setTargetSdkVersion(int version);
+  private static native void waitForVerifier();
+  private static native boolean areClassesVerified(ClassLoader loader);
+  private static native boolean hasVdexFile(ClassLoader loader);
+  private static native boolean isBackedByOatFile(ClassLoader loader);
+  private static native boolean areClassesPreverified(ClassLoader loader);
+  private static native String getCompilerFilter(Class cls);
+
+  private static final String DEX_LOCATION = System.getenv("DEX_LOCATION");
+  private static final String DEX_EXTRA =
+      new File(DEX_LOCATION, "692-vdex-secondary-loader-ex.jar").getAbsolutePath();
+}
diff --git a/test/knownfailures.json b/test/knownfailures.json
index a5aea43..b1ad919 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1128,6 +1128,7 @@
                   "689-zygote-jit-deopt",
                   "690-hiddenapi-same-name-methods",
                   "691-hiddenapi-proxy",
+                  "692-vdex-secondary-loader",
                   "692-vdex-inmem-loader",
                   "693-vdex-inmem-loader-evict",
                   "723-string-init-range",