hiddenapi domain: Use oat dex file location if available

To determine the caller and callee domains of a hidden API access check,
the logic will test the dex location against several known system
locations. However, DexFile instances backed by an OatFile have relative
dex locations to avoid need to rewrite the .oat file when moving files
between folders. The canonical dex location is stored in the OatDexFile
instead.

Because the OatDexFile data structure is not available in libdexfile,
move the domain resolution logic to hidden_api.cc and resolve when dex
files is first registered with the class linker.

Test: m test-art-gtest
Test: camera does not crash
Bug: 126901248
Bug: 127852529
Change-Id: Id494b1c47a2199c227dec046174e08320b9cbc3b
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index a2a4312..e602fa6 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -190,7 +190,7 @@
 ART_GTEST_dex2oat_image_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics VerifierDeps
 ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
 ART_GTEST_hiddenapi_test_DEX_DEPS := HiddenApi HiddenApiStubs
-ART_GTEST_hidden_api_test_DEX_DEPS := HiddenApiSignatures
+ART_GTEST_hidden_api_test_DEX_DEPS := HiddenApiSignatures Main MultiDex
 ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB DefaultMethods VerifySoftFailDuringClinit
 ART_GTEST_imtable_test_DEX_DEPS := IMTA IMTB
 ART_GTEST_instrumentation_test_DEX_DEPS := Instrumentation
diff --git a/libdexfile/dex/art_dex_file_loader.cc b/libdexfile/dex/art_dex_file_loader.cc
index bfae667..e1471f1 100644
--- a/libdexfile/dex/art_dex_file_loader.cc
+++ b/libdexfile/dex/art_dex_file_loader.cc
@@ -527,37 +527,18 @@
                                                       std::string* error_msg,
                                                       std::unique_ptr<DexFileContainer> container,
                                                       VerifyResult* verify_result) {
-  std::unique_ptr<DexFile> dex_file = DexFileLoader::OpenCommon(base,
-                                                                size,
-                                                                data_base,
-                                                                data_size,
-                                                                location,
-                                                                location_checksum,
-                                                                oat_dex_file,
-                                                                verify,
-                                                                verify_checksum,
-                                                                error_msg,
-                                                                std::move(container),
-                                                                verify_result);
-  if (dex_file != nullptr) {
-    // Set hidden API domain based based on location.
-    // Location can contain multidex suffix, so fetch its canonical version. Note
-    // that this will call `realpath`.
-    std::string path = DexFileLoader::GetDexCanonicalLocation(location.c_str());
-    // We check /system/framework before the runtime module location, because the
-    // runtime module location in a testing environment could be /system.
-    if (LocationIsOnSystemFramework(path.c_str())) {
-      dex_file->SetHiddenapiDomain(hiddenapi::Domain::kPlatform);
-    } else if (LocationIsOnRuntimeModule(path.c_str()) ||
-               LocationIsOnConscryptModule(path.c_str())) {
-      dex_file->SetHiddenapiDomain(hiddenapi::Domain::kCorePlatform);
-    } else if (LocationIsOnApex(path.c_str())) {
-      dex_file->SetHiddenapiDomain(hiddenapi::Domain::kPlatform);
-    } else {
-      dex_file->SetHiddenapiDomain(hiddenapi::Domain::kApplication);
-    }
-  }
-  return dex_file;
+  return DexFileLoader::OpenCommon(base,
+                                   size,
+                                   data_base,
+                                   data_size,
+                                   location,
+                                   location_checksum,
+                                   oat_dex_file,
+                                   verify,
+                                   verify_checksum,
+                                   error_msg,
+                                   std::move(container),
+                                   verify_result);
 }
 
 }  // namespace art
diff --git a/libdexfile/dex/art_dex_file_loader_test.cc b/libdexfile/dex/art_dex_file_loader_test.cc
index 8c9258b..be1221c 100644
--- a/libdexfile/dex/art_dex_file_loader_test.cc
+++ b/libdexfile/dex/art_dex_file_loader_test.cc
@@ -18,11 +18,9 @@
 
 #include <sys/mman.h>
 
-#include <fstream>
 #include <memory>
 
 #include "base/common_art_test.h"
-#include "base/file_utils.h"
 #include "base/mem_map.h"
 #include "base/os.h"
 #include "base/stl_util.h"
@@ -37,12 +35,6 @@
 
 namespace art {
 
-static void Copy(const std::string& src, const std::string& dst) {
-  std::ifstream  src_stream(src, std::ios::binary);
-  std::ofstream  dst_stream(dst, std::ios::binary);
-  dst_stream << src_stream.rdbuf();
-}
-
 class ArtDexFileLoaderTest : public CommonArtTest {
   void SetUp() override {
     CommonArtTest::SetUp();
@@ -320,174 +312,4 @@
   ASSERT_EQ(0, unlink(dex_location_sym.c_str()));
 }
 
-TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_DataDir) {
-  // Load file from a non-system directory and check that it is not flagged as framework.
-  std::string data_location_path = android_data_ + "/foo.jar";
-  ASSERT_FALSE(LocationIsOnSystemFramework(data_location_path.c_str()));
-
-  Copy(GetTestDexFileName("Main"), data_location_path);
-
-  ArtDexFileLoader loader;
-  std::vector<std::unique_ptr<const DexFile>> dex_files;
-  std::string error_msg;
-  bool success = loader.Open(data_location_path.c_str(),
-                             data_location_path,
-                             /* verify= */ false,
-                             /* verify_checksum= */ false,
-                             &error_msg,
-                             &dex_files);
-  ASSERT_TRUE(success) << error_msg;
-
-  ASSERT_GE(dex_files.size(), 1u);
-  for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
-    ASSERT_NE(dex_file->GetHiddenapiDomain(), hiddenapi::Domain::kPlatform);
-  }
-
-  dex_files.clear();
-
-  ASSERT_EQ(0, remove(data_location_path.c_str()));
-}
-
-TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_SystemDir) {
-  // Load file from a system, non-framework directory and check that it is not flagged as framework.
-  std::string system_location_path = GetAndroidRoot() + "/foo.jar";
-  ASSERT_FALSE(LocationIsOnSystemFramework(system_location_path.c_str()));
-
-  Copy(GetTestDexFileName("Main"), system_location_path);
-
-  ArtDexFileLoader loader;
-  std::vector<std::unique_ptr<const DexFile>> dex_files;
-  std::string error_msg;
-  bool success = loader.Open(system_location_path.c_str(),
-                             system_location_path,
-                             /* verify= */ false,
-                             /* verify_checksum= */ false,
-                             &error_msg,
-                             &dex_files);
-  ASSERT_TRUE(success) << error_msg;
-
-  ASSERT_GE(dex_files.size(), 1u);
-  for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
-    ASSERT_NE(dex_file->GetHiddenapiDomain(), hiddenapi::Domain::kPlatform);
-  }
-
-  dex_files.clear();
-
-  ASSERT_EQ(0, remove(system_location_path.c_str()));
-}
-
-TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_SystemFrameworkDir) {
-  // Load file from a system/framework directory and check that it is flagged as a framework dex.
-  std::string system_framework_location_path = GetAndroidRoot() + "/framework/foo.jar";
-  ASSERT_TRUE(LocationIsOnSystemFramework(system_framework_location_path.c_str()));
-
-  Copy(GetTestDexFileName("Main"), system_framework_location_path);
-
-  ArtDexFileLoader loader;
-  std::vector<std::unique_ptr<const DexFile>> dex_files;
-  std::string error_msg;
-  bool success = loader.Open(system_framework_location_path.c_str(),
-                             system_framework_location_path,
-                             /* verify= */ false,
-                             /* verify_checksum= */ false,
-                             &error_msg,
-                             &dex_files);
-  ASSERT_TRUE(success) << error_msg;
-
-  ASSERT_GE(dex_files.size(), 1u);
-  for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
-    ASSERT_EQ(dex_file->GetHiddenapiDomain(), hiddenapi::Domain::kPlatform);
-  }
-
-  dex_files.clear();
-
-  ASSERT_EQ(0, remove(system_framework_location_path.c_str()));
-}
-
-TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_DataDir_MultiDex) {
-  // Load multidex file from a non-system directory and check that it is not flagged as framework.
-  std::string data_multi_location_path = android_data_ + "/multifoo.jar";
-  ASSERT_FALSE(LocationIsOnSystemFramework(data_multi_location_path.c_str()));
-
-  Copy(GetTestDexFileName("MultiDex"), data_multi_location_path);
-
-  ArtDexFileLoader loader;
-  std::vector<std::unique_ptr<const DexFile>> dex_files;
-  std::string error_msg;
-  bool success = loader.Open(data_multi_location_path.c_str(),
-                             data_multi_location_path,
-                             /* verify= */ false,
-                             /* verify_checksum= */ false,
-                             &error_msg,
-                             &dex_files);
-  ASSERT_TRUE(success) << error_msg;
-
-  ASSERT_GT(dex_files.size(), 1u);
-  for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
-    ASSERT_NE(dex_file->GetHiddenapiDomain(), hiddenapi::Domain::kPlatform);
-  }
-
-  dex_files.clear();
-
-  ASSERT_EQ(0, remove(data_multi_location_path.c_str()));
-}
-
-TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_SystemDir_MultiDex) {
-  // Load multidex file from a system, non-framework directory and check that it is not flagged
-  // as framework.
-  std::string system_multi_location_path = GetAndroidRoot() + "/multifoo.jar";
-  ASSERT_FALSE(LocationIsOnSystemFramework(system_multi_location_path.c_str()));
-
-  Copy(GetTestDexFileName("MultiDex"), system_multi_location_path);
-
-  ArtDexFileLoader loader;
-  std::vector<std::unique_ptr<const DexFile>> dex_files;
-  std::string error_msg;
-  bool success = loader.Open(system_multi_location_path.c_str(),
-                             system_multi_location_path,
-                             /* verify= */ false,
-                             /* verify_checksum= */ false,
-                             &error_msg,
-                             &dex_files);
-  ASSERT_TRUE(success) << error_msg;
-
-  ASSERT_GT(dex_files.size(), 1u);
-  for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
-    ASSERT_NE(dex_file->GetHiddenapiDomain(), hiddenapi::Domain::kPlatform);
-  }
-
-  dex_files.clear();
-
-  ASSERT_EQ(0, remove(system_multi_location_path.c_str()));
-}
-
-TEST_F(ArtDexFileLoaderTest, IsPlatformDexFile_SystemFrameworkDir_MultiDex) {
-  // Load multidex file from a system/framework directory and check that it is flagged as a
-  // framework dex.
-  std::string system_framework_multi_location_path = GetAndroidRoot() + "/framework/multifoo.jar";
-  ASSERT_TRUE(LocationIsOnSystemFramework(system_framework_multi_location_path.c_str()));
-
-  Copy(GetTestDexFileName("MultiDex"), system_framework_multi_location_path);
-
-  ArtDexFileLoader loader;
-  std::vector<std::unique_ptr<const DexFile>> dex_files;
-  std::string error_msg;
-  bool success = loader.Open(system_framework_multi_location_path.c_str(),
-                             system_framework_multi_location_path,
-                             /* verify= */ false,
-                             /* verify_checksum= */ false,
-                             &error_msg,
-                             &dex_files);
-  ASSERT_TRUE(success) << error_msg;
-
-  ASSERT_GT(dex_files.size(), 1u);
-  for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
-    ASSERT_EQ(dex_file->GetHiddenapiDomain(), hiddenapi::Domain::kPlatform);
-  }
-
-  dex_files.clear();
-
-  ASSERT_EQ(0, remove(system_framework_multi_location_path.c_str()));
-}
-
 }  // namespace art
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index de9d9ca..e5dcc0d 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3765,6 +3765,9 @@
   if (initialize_oat_file_data) {
     oat_file->InitializeRelocations();
   }
+  // Let hiddenapi assign a domain to the newly registered dex file.
+  hiddenapi::InitializeDexFileDomain(dex_file, class_loader);
+
   jweak dex_cache_jweak = vm->AddWeakGlobalRef(self, dex_cache);
   dex_cache->SetDexFile(&dex_file);
   DexCacheData data;
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
index b458582..23e2e1f 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -21,10 +21,12 @@
 #include "art_field-inl.h"
 #include "art_method-inl.h"
 #include "base/dumpable.h"
+#include "base/file_utils.h"
 #include "class_root.h"
 #include "dex/class_accessor-inl.h"
 #include "dex/dex_file_loader.h"
 #include "mirror/class_ext.h"
+#include "oat_file.h"
 #include "scoped_thread_state_change.h"
 #include "thread-inl.h"
 #include "well_known_classes.h"
@@ -71,6 +73,73 @@
   return os;
 }
 
+static Domain DetermineDomainFromPath_Impl(const std::string& path,
+                                           const std::string& dex_location,
+                                           ObjPtr<mirror::ClassLoader> class_loader) {
+  // We check /system/framework before the runtime module location, because the
+  // runtime module location in a testing environment could be /system.
+  if (LocationIsOnSystemFramework(path.c_str())) {
+    return Domain::kPlatform;
+  }
+
+  if (LocationIsOnRuntimeModule(path.c_str()) || LocationIsOnConscryptModule(path.c_str())) {
+    return Domain::kCorePlatform;
+  }
+
+  if (LocationIsOnApex(path.c_str())) {
+    return Domain::kPlatform;
+  }
+
+  if (class_loader.IsNull()) {
+    LOG(WARNING) << "DexFile " << dex_location
+        << " is in boot class path but its path " << path << " is not in a known location";
+    return Domain::kPlatform;
+  }
+
+  return Domain::kApplication;
+}
+
+static bool IsAbsoluteLocation(const std::string& str) {
+  return !str.empty() && str[0] == '/';
+}
+
+void InitializeDexFileDomain(const DexFile& dex_file, ObjPtr<mirror::ClassLoader> class_loader) {
+  // TODO(dbrazdil): Fix when preopting on host for target. In that case, the dex location
+  // is the target path and functions in file_utils.h check against host system paths.
+
+  const std::string& dex_location = dex_file.GetLocation();
+  const OatDexFile* const oat_dex_file = dex_file.GetOatDexFile();
+
+  Domain dex_domain = Domain::kApplication;
+  if (IsAbsoluteLocation(dex_location)) {
+    // Resolve realpath() of the dex location.
+    dex_domain = DetermineDomainFromPath_Impl(
+        DexFileLoader::GetDexCanonicalLocation(dex_location.c_str()),
+        dex_location,
+        class_loader);
+  } else if (oat_dex_file != nullptr && !oat_dex_file->GetCanonicalDexFileLocation().empty()) {
+    // If the path is relative, we might find the canonical path in the OatDexFile.
+    dex_domain = DetermineDomainFromPath_Impl(
+          oat_dex_file->GetCanonicalDexFileLocation(),
+          dex_location,
+          class_loader);
+  } else if (class_loader.IsNull()) {
+    LOG(WARNING) << "DexFile " << dex_location
+        << " is in boot class path but has no canonical location";
+    dex_domain = Domain::kPlatform;
+  } else {
+    // No canonical path available, not in boot class path. Conservatively
+    // assign application domain.
+    dex_domain = Domain::kApplication;
+  }
+
+  // Assign the domain unless a more permissive domain has already been assigned.
+  // This may happen when DexFile is initialized as trusted.
+  if (IsDomainMoreTrustedThan(dex_domain, dex_file.GetHiddenapiDomain())) {
+    dex_file.SetHiddenapiDomain(dex_domain);
+  }
+}
+
 namespace detail {
 
 // Do not change the values of items in this enum, as they are written to the
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index 08a9fff..7abdb5d 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -70,6 +70,12 @@
         dex_file_(GetDexFileFromDexCache(dex_cache)),
         domain_(ComputeDomain(class_loader, dex_file_)) {}
 
+  // Initialize from class loader and dex file (only used by tests).
+  AccessContext(ObjPtr<mirror::ClassLoader> class_loader, const DexFile* dex_file)
+      : klass_(nullptr),
+        dex_file_(dex_file),
+        domain_(ComputeDomain(class_loader, dex_file_)) {}
+
   // Initialize from Class.
   explicit AccessContext(ObjPtr<mirror::Class> klass)
       REQUIRES_SHARED(Locks::mutator_lock_)
@@ -97,20 +103,12 @@
     return is_trusted ? Domain::kCorePlatform : Domain::kApplication;
   }
 
-  static Domain ComputeDomain(ObjPtr<mirror::ClassLoader> class_loader, const DexFile* dex_file)
-      REQUIRES_SHARED(Locks::mutator_lock_) {
+  static Domain ComputeDomain(ObjPtr<mirror::ClassLoader> class_loader, const DexFile* dex_file) {
     if (dex_file == nullptr) {
       return ComputeDomain(/* is_trusted= */ class_loader.IsNull());
     }
 
-    Domain dex_domain = dex_file->GetHiddenapiDomain();
-    if (class_loader.IsNull() && dex_domain == Domain::kApplication) {
-      LOG(WARNING) << "DexFile " << dex_file->GetLocation()
-          << " is in boot classpath but is assigned the application domain";
-      dex_file->SetHiddenapiDomain(Domain::kPlatform);
-      dex_domain = Domain::kPlatform;
-    }
-    return dex_domain;
+    return dex_file->GetHiddenapiDomain();
   }
 
   static Domain ComputeDomain(ObjPtr<mirror::Class> klass, const DexFile* dex_file)
@@ -356,6 +354,11 @@
   }
 }
 
+// Called by class linker when a new dex file has been registered. Assigns
+// the AccessContext domain to the newly-registered dex file based on its
+// location and class loader.
+void InitializeDexFileDomain(const DexFile& dex_file, ObjPtr<mirror::ClassLoader> class_loader);
+
 // Returns true if access to `member` should be denied in the given context.
 // The decision is based on whether the caller is in a trusted context or not.
 // Because determining the access context can be expensive, a lambda function
diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc
index cfc3b07..be64079 100644
--- a/runtime/hidden_api_test.cc
+++ b/runtime/hidden_api_test.cc
@@ -16,10 +16,15 @@
 
 #include "hidden_api.h"
 
+#include <fstream>
+
+#include "base/file_utils.h"
 #include "base/sdk_version.h"
+#include "base/stl_util.h"
 #include "common_runtime_test.h"
 #include "jni/jni_internal.h"
 #include "proxy_test.h"
+#include "well_known_classes.h"
 
 namespace art {
 
@@ -375,4 +380,209 @@
   ASSERT_EQ("L$Proxy1234;->interfaces:[Ljava/lang/Class;", ss_field.str());
 }
 
+static bool Copy(const std::string& src, const std::string& dst, /*out*/ std::string* error_msg) {
+  std::ifstream  src_stream(src, std::ios::binary);
+  std::ofstream  dst_stream(dst, std::ios::binary);
+  dst_stream << src_stream.rdbuf();
+  src_stream.close();
+  dst_stream.close();
+  if (src_stream.good() && dst_stream.good()) {
+    return true;
+  } else {
+    *error_msg = "Copy " + src + " => " + dst + " (src_good="
+        + (src_stream.good() ? "true" : "false") + ", dst_good="
+        + (dst_stream.good() ? "true" : "false") + ")";
+    return false;
+  }
+}
+
+static bool LoadDexFiles(const std::string& path,
+                         ScopedObjectAccess& soa,
+                         /* out */ std::vector<std::unique_ptr<const DexFile>>* dex_files,
+                         /* out */ ObjPtr<mirror::ClassLoader>* class_loader,
+                         /* out */ std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
+  if (!ArtDexFileLoader().Open(path.c_str(),
+                               path,
+                               /* verify= */ true,
+                               /* verify_checksum= */ true,
+                               error_msg,
+                               dex_files)) {
+    return false;
+  }
+
+  ClassLinker* const linker = Runtime::Current()->GetClassLinker();
+
+  StackHandleScope<2> hs(soa.Self());
+  Handle<mirror::Class> h_class = hs.NewHandle(soa.Decode<mirror::Class>(
+      WellKnownClasses::dalvik_system_PathClassLoader));
+  Handle<mirror::ClassLoader> h_loader = hs.NewHandle(linker->CreateWellKnownClassLoader(
+      soa.Self(),
+      MakeNonOwningPointerVector(*dex_files),
+      h_class,
+      /* parent_loader= */ ScopedNullHandle<mirror::ClassLoader>(),
+      /* shared_libraries= */ ScopedNullHandle<mirror::ObjectArray<mirror::ClassLoader>>()));
+  for (const auto& dex_file : *dex_files) {
+    linker->RegisterDexFile(*dex_file.get(), h_loader.Get());
+  }
+
+  *class_loader = h_loader.Get();
+  return true;
+}
+
+static bool CheckAllDexFilesInDomain(ObjPtr<mirror::ClassLoader> loader,
+                                     const std::vector<std::unique_ptr<const DexFile>>& dex_files,
+                                     hiddenapi::Domain expected_domain)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  for (const auto& dex_file : dex_files) {
+    hiddenapi::AccessContext context(loader, dex_file.get());
+    if (context.GetDomain() != expected_domain) {
+      LOG(ERROR) << dex_file->GetLocation() << ": access context domain does not match "
+          << "(expected=" << static_cast<uint32_t>(expected_domain)
+          << ", actual=" << static_cast<uint32_t>(context.GetDomain()) << ")";
+      return false;
+    }
+    if (dex_file->GetHiddenapiDomain() != expected_domain) {
+      LOG(ERROR) << dex_file->GetLocation() << ": dex file domain does not match "
+          << "(expected=" << static_cast<uint32_t>(expected_domain)
+          << ", actual=" << static_cast<uint32_t>(dex_file->GetHiddenapiDomain()) << ")";
+      return false;
+    }
+  }
+
+  return true;
+}
+
+TEST_F(HiddenApiTest, DexDomain_DataDir) {
+  // Load file from a non-system directory and check that it is not flagged as framework.
+  std::string data_location_path = android_data_ + "/foo.jar";
+  ASSERT_FALSE(LocationIsOnSystemFramework(data_location_path.c_str()));
+
+  ScopedObjectAccess soa(Thread::Current());
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  ObjPtr<mirror::ClassLoader> class_loader;
+
+  ASSERT_TRUE(Copy(GetTestDexFileName("Main"), data_location_path, &error_msg)) << error_msg;
+  ASSERT_TRUE(LoadDexFiles(data_location_path, soa, &dex_files, &class_loader, &error_msg))
+      << error_msg;
+  ASSERT_GE(dex_files.size(), 1u);
+  ASSERT_TRUE(CheckAllDexFilesInDomain(class_loader, dex_files, hiddenapi::Domain::kApplication));
+
+  dex_files.clear();
+  ASSERT_EQ(0, remove(data_location_path.c_str()));
+}
+
+TEST_F(HiddenApiTest, DexDomain_SystemDir) {
+  // Load file from a system, non-framework directory and check that it is not flagged as framework.
+  std::string system_location_path = GetAndroidRoot() + "/foo.jar";
+  ASSERT_FALSE(LocationIsOnSystemFramework(system_location_path.c_str()));
+
+  ScopedObjectAccess soa(Thread::Current());
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  ObjPtr<mirror::ClassLoader> class_loader;
+
+  ASSERT_TRUE(Copy(GetTestDexFileName("Main"), system_location_path, &error_msg)) << error_msg;
+  ASSERT_TRUE(LoadDexFiles(system_location_path, soa, &dex_files, &class_loader, &error_msg))
+      << error_msg;
+  ASSERT_GE(dex_files.size(), 1u);
+  ASSERT_TRUE(CheckAllDexFilesInDomain(class_loader, dex_files, hiddenapi::Domain::kApplication));
+
+  dex_files.clear();
+  ASSERT_EQ(0, remove(system_location_path.c_str()));
+}
+
+TEST_F(HiddenApiTest, DexDomain_SystemFrameworkDir) {
+  // Load file from a system/framework directory and check that it is flagged as a framework dex.
+  std::string system_framework_location_path = GetAndroidRoot() + "/framework/foo.jar";
+  ASSERT_TRUE(LocationIsOnSystemFramework(system_framework_location_path.c_str()));
+
+  ScopedObjectAccess soa(Thread::Current());
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  ObjPtr<mirror::ClassLoader> class_loader;
+
+  ASSERT_TRUE(Copy(GetTestDexFileName("Main"), system_framework_location_path, &error_msg))
+      << error_msg;
+  ASSERT_TRUE(LoadDexFiles(system_framework_location_path,
+                           soa,
+                           &dex_files,
+                           &class_loader,
+                           &error_msg)) << error_msg;
+  ASSERT_GE(dex_files.size(), 1u);
+  ASSERT_TRUE(CheckAllDexFilesInDomain(class_loader, dex_files, hiddenapi::Domain::kPlatform));
+
+  dex_files.clear();
+  ASSERT_EQ(0, remove(system_framework_location_path.c_str()));
+}
+
+TEST_F(HiddenApiTest, DexDomain_DataDir_MultiDex) {
+  // Load multidex file from a non-system directory and check that it is not flagged as framework.
+  std::string data_multi_location_path = android_data_ + "/multifoo.jar";
+  ASSERT_FALSE(LocationIsOnSystemFramework(data_multi_location_path.c_str()));
+
+  ScopedObjectAccess soa(Thread::Current());
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  ObjPtr<mirror::ClassLoader> class_loader;
+
+  ASSERT_TRUE(Copy(GetTestDexFileName("MultiDex"), data_multi_location_path, &error_msg))
+      << error_msg;
+  ASSERT_TRUE(LoadDexFiles(data_multi_location_path, soa, &dex_files, &class_loader, &error_msg))
+      << error_msg;
+  ASSERT_GE(dex_files.size(), 1u);
+  ASSERT_TRUE(CheckAllDexFilesInDomain(class_loader, dex_files, hiddenapi::Domain::kApplication));
+
+  dex_files.clear();
+  ASSERT_EQ(0, remove(data_multi_location_path.c_str()));
+}
+
+TEST_F(HiddenApiTest, DexDomain_SystemDir_MultiDex) {
+  // Load multidex file from a system, non-framework directory and check that it is not flagged
+  // as framework.
+  std::string system_multi_location_path = GetAndroidRoot() + "/multifoo.jar";
+  ASSERT_FALSE(LocationIsOnSystemFramework(system_multi_location_path.c_str()));
+
+  ScopedObjectAccess soa(Thread::Current());
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  ObjPtr<mirror::ClassLoader> class_loader;
+
+  ASSERT_TRUE(Copy(GetTestDexFileName("MultiDex"), system_multi_location_path, &error_msg))
+      << error_msg;
+  ASSERT_TRUE(LoadDexFiles(system_multi_location_path, soa, &dex_files, &class_loader, &error_msg))
+      << error_msg;
+  ASSERT_GT(dex_files.size(), 1u);
+  ASSERT_TRUE(CheckAllDexFilesInDomain(class_loader, dex_files, hiddenapi::Domain::kApplication));
+
+  dex_files.clear();
+  ASSERT_EQ(0, remove(system_multi_location_path.c_str()));
+}
+
+TEST_F(HiddenApiTest, DexDomain_SystemFrameworkDir_MultiDex) {
+  // Load multidex file from a system/framework directory and check that it is flagged as a
+  // framework dex.
+  std::string system_framework_multi_location_path = GetAndroidRoot() + "/framework/multifoo.jar";
+  ASSERT_TRUE(LocationIsOnSystemFramework(system_framework_multi_location_path.c_str()));
+
+  ScopedObjectAccess soa(Thread::Current());
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  std::string error_msg;
+  ObjPtr<mirror::ClassLoader> class_loader;
+
+  ASSERT_TRUE(Copy(GetTestDexFileName("MultiDex"),
+                   system_framework_multi_location_path,
+                   &error_msg)) << error_msg;
+  ASSERT_TRUE(LoadDexFiles(system_framework_multi_location_path,
+                           soa,
+                           &dex_files,
+                           &class_loader,
+                           &error_msg)) << error_msg;
+  ASSERT_GT(dex_files.size(), 1u);
+  ASSERT_TRUE(CheckAllDexFilesInDomain(class_loader, dex_files, hiddenapi::Domain::kPlatform));
+
+  dex_files.clear();
+  ASSERT_EQ(0, remove(system_framework_multi_location_path.c_str()));
+}
+
 }  // namespace art