Merge "Fix host-only exception in art test 005-annotations."
diff --git a/Android.mk b/Android.mk
index 1a5daff..a98bedc 100644
--- a/Android.mk
+++ b/Android.mk
@@ -352,13 +352,18 @@
   # Module with both release and debug variants, as well as
   # additional tools.
   TARGET_RUNTIME_APEX := com.android.runtime.debug
+  APEX_TEST_MODULE := art-check-debug-apex-gen-fakebin
 else
   # Release module (without debug variants nor tools).
   TARGET_RUNTIME_APEX := com.android.runtime.release
+  APEX_TEST_MODULE := art-check-release-apex-gen-fakebin
 endif
 
 LOCAL_MODULE := com.android.runtime
 LOCAL_REQUIRED_MODULES := $(TARGET_RUNTIME_APEX)
+ifneq ($(HOST_OS),darwin)
+  LOCAL_REQUIRED_MODULES += $(APEX_TEST_MODULE)
+endif
 
 # Clear locally used variable.
 art_target_include_debug_build :=
@@ -458,19 +463,6 @@
 include $(BUILD_PHONY_PACKAGE)
 endif
 
-# Create dummy hidden API lists which are normally generated by the framework
-# but which we do not have in the master-art manifest.
-# We need to execute this now to ensure Makefile rules depending on these files can
-# be constructed.
-define build-art-hiddenapi
-$(shell if [ ! -d frameworks/base ]; then \
-  mkdir -p ${TARGET_OUT_COMMON_INTERMEDIATES}/PACKAGING; \
-	touch ${TARGET_OUT_COMMON_INTERMEDIATES}/PACKAGING/hiddenapi-flags.csv; \
-  fi;)
-endef
-
-$(eval $(call build-art-hiddenapi))
-
 ########################################################################
 # "m build-art" for quick minimal build
 .PHONY: build-art
@@ -483,6 +475,28 @@
 build-art-target: $(TARGET_OUT_EXECUTABLES)/art $(ART_TARGET_DEPENDENCIES) $(TARGET_CORE_IMG_OUTS)
 
 ########################################################################
+# Workaround for not using symbolic links for linker and bionic libraries
+# in a minimal setup (eg buildbot or golem).
+########################################################################
+
+PRIVATE_BIONIC_FILES := \
+  bin/bootstrap/linker \
+  bin/bootstrap/linker64 \
+  lib/bootstrap/libc.so \
+  lib/bootstrap/libm.so \
+  lib/bootstrap/libdl.so \
+  lib64/bootstrap/libc.so \
+  lib64/bootstrap/libm.so \
+  lib64/bootstrap/libdl.so 
+
+.PHONY: art-bionic-files
+art-bionic-files: libc.bootstrap libdl.bootstrap libm.bootstrap linker
+	for f in $(PRIVATE_BIONIC_FILES); do \
+	  tf=$(TARGET_OUT)/$$f; \
+	  if [ -f $$tf ]; then cp -f $$tf $$(echo $$tf | sed 's,bootstrap/,,'); fi; \
+	done
+
+########################################################################
 # Phony target for only building what go/lem requires for pushing ART on /data.
 
 .PHONY: build-art-target-golem
@@ -514,7 +528,8 @@
                         $(TARGET_CORE_IMG_OUT_BASE).art \
                         $(TARGET_CORE_IMG_OUT_BASE)-interpreter.art \
                         libc.bootstrap libdl.bootstrap libm.bootstrap \
-                        icu-data-art-test
+                        icu-data-art-test \
+                        art-bionic-files
 	# remove debug libraries from public.libraries.txt because golem builds
 	# won't have it.
 	sed -i '/libartd.so/d' $(TARGET_OUT)/etc/public.libraries.txt
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 67d85c1..21eee7a 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -38,6 +38,7 @@
   GetMethodSignature \
   HiddenApi \
   HiddenApiSignatures \
+  HiddenApiStubs \
   ImageLayoutA \
   ImageLayoutB \
   IMTA \
@@ -188,7 +189,7 @@
 ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ManyMethods Statics VerifierDeps MainUncompressed EmptyUncompressed StringLiterals
 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
+ART_GTEST_hiddenapi_test_DEX_DEPS := HiddenApi HiddenApiStubs
 ART_GTEST_hidden_api_test_DEX_DEPS := HiddenApiSignatures
 ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB DefaultMethods VerifySoftFailDuringClinit
 ART_GTEST_imtable_test_DEX_DEPS := IMTA IMTB
@@ -384,6 +385,7 @@
     art_hiddenapi_tests \
     art_imgdiag_tests \
     art_libartbase_tests \
+    art_libartpalette_tests \
     art_libdexfile_external_tests \
     art_libdexfile_support_tests \
     art_libdexfile_tests \
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index 6ede916..ad94b45 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -13,12 +13,11 @@
 ]
 // - Base requirements (libraries).
 art_runtime_base_native_shared_libs = [
-    "libadbconnection",
     "libart",
     "libart-compiler",
-    "libdexfile_external",
     "libopenjdkjvm",
     "libopenjdkjvmti",
+    "libadbconnection",
 ]
 bionic_native_shared_libs = [
     "libc",
@@ -42,11 +41,11 @@
 
 // - Debug variants (libraries).
 art_runtime_debug_native_shared_libs = [
-    "libadbconnectiond",
     "libartd",
     "libartd-compiler",
     "libopenjdkjvmd",
     "libopenjdkjvmtid",
+    "libadbconnectiond",
 ]
 libcore_debug_native_shared_libs = [
     "libopenjdkd",
@@ -104,6 +103,14 @@
     "apache-xml",
 ]
 
+// Temporary library includes for b/123591866 as all libraries are moved into the main art-apex.
+art_runtime_libraries_zipapex = [
+    "libnativebridge",
+    "libnativeloader",
+    "libnativehelper",
+    "libcutils",
+]
+
 apex_key {
     name: "com.android.runtime.key",
     public_key: "com.android.runtime.avbpubkey",
@@ -192,15 +199,18 @@
 art_apex {
     name: "com.android.runtime.host",
     compile_multilib: "both",
+    installable: false,
     payload_type: "zip",
     host_supported: true,
     device_supported: false,
     manifest: "manifest.json",
     java_libs: libcore_target_java_libs,
+    ignore_system_library_special_case: true,
     native_shared_libs: art_runtime_base_native_shared_libs
         + art_runtime_debug_native_shared_libs
         + libcore_native_shared_libs
-        + libcore_debug_native_shared_libs,
+        + libcore_debug_native_shared_libs
+        + art_runtime_libraries_zipapex,
     multilib: {
         both: {
             // TODO: Add logic to create a `dalvikvm` symlink to `dalvikvm32` or `dalvikvm64`
@@ -219,5 +229,83 @@
         darwin: {
             enabled: false,
         },
+        linux_bionic: {
+            enabled: true,
+            multilib: {
+                both: {
+                    native_shared_libs: bionic_native_shared_libs,
+                    binaries: bionic_binaries_both,
+                }
+            }
+        },
+    },
+}
+
+python_binary_host {
+    name: "art-apex-tester",
+    srcs: ["art_apex_test.py"],
+    main: "art_apex_test.py",
+    version: {
+        py2: {
+            enabled: false,
+        },
+        py3: {
+            enabled: true,
+        },
+    },
+}
+
+// Genrules so we can run the checker, and empty Java library so that it gets executed.
+
+genrule {
+    name: "art-check-release-apex-gen",
+    srcs: [":com.android.runtime.release"],
+    tools: [
+        "art-apex-tester",
+        "debugfs",
+    ],
+    cmd: "$(location art-apex-tester)"
+              + " --debugfs $(location debugfs)"
+              + " --tmpdir $(genDir)"
+              + " $(in)"
+         + " && touch $(out)",
+    out: ["art-check-release-apex-gen.dummy"],
+}
+cc_prebuilt_binary {
+    name: "art-check-release-apex-gen-fakebin",
+    srcs: [":art-check-release-apex-gen"],
+    host_supported: true,
+    device_supported: false,
+    target: {
+        darwin: {
+            enabled: false,  // No python3.
+        },
+    },
+}
+
+genrule {
+    name: "art-check-debug-apex-gen",
+    srcs: [":com.android.runtime.debug"],
+    tools: [
+        "art-apex-tester",
+        "debugfs",
+    ],
+    cmd: "$(location art-apex-tester)"
+              + " --debugfs $(location debugfs)"
+              + " --tmpdir $(genDir)"
+              + " --debug"
+              + " $(in)"
+         + " && touch $(out)",
+    out: ["art-check-debug-apex-gen.dummy"],
+}
+cc_prebuilt_binary {
+    name: "art-check-debug-apex-gen-fakebin",
+    srcs: [":art-check-debug-apex-gen"],
+    host_supported: true,
+    device_supported: false,
+    target: {
+        darwin: {
+            enabled: false,  // No python3.
+        },
     },
 }
diff --git a/build/apex/art_apex_test.py b/build/apex/art_apex_test.py
index e636a72..1abc466 100755
--- a/build/apex/art_apex_test.py
+++ b/build/apex/art_apex_test.py
@@ -51,7 +51,7 @@
   def get(self, path):
     dir, name = os.path.split(path)
     if len(dir) == 0:
-      dir = '/'
+      dir = '.'
     map = self.read_dir(dir)
     return map[name] if name in map else None
 
@@ -104,11 +104,78 @@
     self._folder_cache[dir] = map
     return map
 
+class HostApexProvider:
+  def __init__(self, apex, tmpdir):
+    self._tmpdir = tmpdir
+    self._folder_cache = {}
+    self._payload = os.path.join(self._tmpdir, 'apex_payload.zip')
+    # Extract payload to tmpdir.
+    zip = zipfile.ZipFile(apex)
+    zip.extract('apex_payload.zip', tmpdir)
+
+  def __del__(self):
+    # Delete temps.
+    if os.path.exists(self._payload):
+      os.remove(self._payload)
+
+  def get(self, path):
+    dir, name = os.path.split(path)
+    if len(dir) == 0:
+      dir = ''
+    map = self.read_dir(dir)
+    return map[name] if name in map else None
+
+  def read_dir(self, dir):
+    if dir in self._folder_cache:
+      return self._folder_cache[dir]
+    if not self._folder_cache:
+      self.parse_zip()
+    if dir in self._folder_cache:
+      return self._folder_cache[dir]
+    return {}
+
+  def parse_zip(self):
+    zip = zipfile.ZipFile(self._payload)
+    infos = zip.infolist()
+    for zipinfo in infos:
+      path = zipinfo.filename
+
+      # Assume no empty file is stored.
+      assert path
+
+      def get_octal(val, index):
+        return (val >> (index * 3)) & 0x7;
+      def bits_is_exec(val):
+        # TODO: Enforce group/other, too?
+        return get_octal(val, 2) & 1 == 1
+
+      is_zipinfo = True
+      while path:
+        dir, base = os.path.split(path)
+        # TODO: If directories are stored, base will be empty.
+
+        if not dir in self._folder_cache:
+          self._folder_cache[dir] = {}
+        dir_map = self._folder_cache[dir]
+        if not base in dir_map:
+          if is_zipinfo:
+            bits = (zipinfo.external_attr >> 16) & 0xFFFF
+            is_dir = get_octal(bits, 4) == 4
+            is_symlink = get_octal(bits, 4) == 2
+            is_exec = bits_is_exec(bits)
+          else:
+            is_exec = False  # Seems we can't get this easily?
+            is_symlink = False
+            is_dir = True
+          dir_map[base] = FSObject(base, is_dir, is_exec, is_symlink)
+        is_zipinfo = False
+        path = dir
+
+# DO NOT USE DIRECTLY! This is an "abstract" base class.
 class Checker:
   def __init__(self, provider):
     self._provider = provider
     self._errors = 0
-    self._is_multilib = provider.get('lib64') is not None;
 
   def fail(self, msg, *args):
     self._errors += 1
@@ -116,6 +183,8 @@
 
   def error_count(self):
     return self._errors
+  def reset_errors(self):
+    self._errors = 0
 
   def is_file(self, file):
     fs_object = self._provider.get(file)
@@ -130,6 +199,11 @@
     if not chk[0]:
       self.fail(chk[1], file)
     return chk[0]
+  def check_no_file(self, file):
+    chk = self.is_file(file)
+    if chk[0]:
+      self.fail('File %s does exist', file)
+    return not chk[0]
 
   def check_binary(self, file):
     path = 'bin/%s' % (file)
@@ -140,13 +214,6 @@
       return False
     return True
 
-  def check_multilib_binary(self, file):
-    res = self.check_binary('%s32' % (file))
-    if self._is_multilib:
-      res = self.check_binary('%s64' % (file)) and res
-    self.check_binary_symlink(file)
-    return res
-
   def check_binary_symlink(self, file):
     path = 'bin/%s' % (file)
     fs_object = self._provider.get(path)
@@ -161,14 +228,6 @@
       return False
     return True
 
-  def check_library(self, file):
-    # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
-    # the precision of this test?
-    res = self.check_file('lib/%s' % (file))
-    if self._is_multilib:
-      res = self.check_file('lib64/%s' % (file)) and res
-    return res
-
   def check_single_library(self, file):
     res1 = self.is_file('lib/%s' % (file))
     res2 = self.is_file('lib64/%s' % (file))
@@ -177,46 +236,133 @@
       return False
     return True
 
+  def check_no_library(self, file):
+    res1 = self.is_file('lib/%s' % (file))
+    res2 = self.is_file('lib64/%s' % (file))
+    if res1[0] or res2[0]:
+      self.fail('Library exists: %s', file)
+      return False
+    return True
+
   def check_java_library(self, file):
     return self.check_file('javalib/%s' % (file))
 
-class ReleaseChecker(Checker):
+  # Just here for docs purposes, even if it isn't good Python style.
+
+  def check_library(self, file):
+    raise NotImplementedError
+
+  def check_first_library(self, file):
+    raise NotImplementedError
+
+  def check_multilib_binary(self, file):
+    raise NotImplementedError
+
+  def check_prefer32_binary(self, file):
+    raise NotImplementedError
+
+
+class Arch32Checker(Checker):
   def __init__(self, provider):
     super().__init__(provider)
+
+  def check_multilib_binary(self, file):
+    return all([self.check_binary('%s32' % (file)),
+                self.check_no_file('bin/%s64' % (file)),
+                self.check_binary_symlink(file)])
+
+  def check_library(self, file):
+    # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
+    # the precision of this test?
+    return all([self.check_file('lib/%s' % (file)), self.check_no_file('lib64/%s' % (file))])
+
+  def check_first_library(self, file):
+    return self.check_library(file)
+
+  def check_prefer32_binary(self, file):
+    return self.check_binary('%s32' % (file))
+
+
+class Arch64Checker(Checker):
+  def __init__(self, provider):
+    super().__init__(provider)
+
+  def check_multilib_binary(self, file):
+    return all([self.check_no_file('bin/%s32' % (file)),
+                self.check_binary('%s64' % (file)),
+                self.check_binary_symlink(file)])
+
+  def check_library(self, file):
+    # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
+    # the precision of this test?
+    return all([self.check_no_file('lib/%s' % (file)), self.check_file('lib64/%s' % (file))])
+
+  def check_first_library(self, file):
+    return self.check_library(file)
+
+  def check_prefer32_binary(self, file):
+    return self.check_binary('%s64' % (file))
+
+
+class MultilibChecker(Checker):
+  def __init__(self, provider):
+    super().__init__(provider)
+
+  def check_multilib_binary(self, file):
+    return all([self.check_binary('%s32' % (file)),
+                self.check_binary('%s64' % (file)),
+                self.check_binary_symlink(file)])
+
+  def check_library(self, file):
+    # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
+    # the precision of this test?
+    return all([self.check_file('lib/%s' % (file)), self.check_file('lib64/%s' % (file))])
+
+  def check_first_library(self, file):
+    return all([self.check_no_file('lib/%s' % (file)), self.check_file('lib64/%s' % (file))])
+
+  def check_prefer32_binary(self, file):
+    return self.check_binary('%s32' % (file))
+
+
+class ReleaseChecker:
+  def __init__(self, checker):
+    self._checker = checker
   def __str__(self):
     return 'Release Checker'
 
   def run(self):
     # Check that the mounted image contains an APEX manifest.
-    self.check_file('apex_manifest.json')
+    self._checker.check_file('apex_manifest.json')
 
     # Check that the mounted image contains ART base binaries.
-    self.check_multilib_binary('dalvikvm')
-    self.check_binary('dex2oat')
-    self.check_binary('dexoptanalyzer')
-    self.check_binary('profman')
+    self._checker.check_multilib_binary('dalvikvm')
+    self._checker.check_binary('dex2oat')
+    self._checker.check_binary('dexoptanalyzer')
+    self._checker.check_binary('profman')
 
     # oatdump is only in device apex's due to build rules
     # TODO: Check for it when it is also built for host.
-    # self.check_binary('oatdump')
+    # self._checker.check_binary('oatdump')
 
     # Check that the mounted image contains Android Runtime libraries.
-    self.check_library('libart-compiler.so')
-    self.check_library('libart-dexlayout.so')
-    self.check_library('libart.so')
-    self.check_library('libartbase.so')
-    self.check_library('libdexfile.so')
-    self.check_library('libopenjdkjvm.so')
-    self.check_library('libopenjdkjvmti.so')
-    self.check_library('libprofile.so')
+    self._checker.check_library('libart-compiler.so')
+    self._checker.check_library('libart-dexlayout.so')
+    self._checker.check_library('libart.so')
+    self._checker.check_library('libartbase.so')
+    self._checker.check_library('libartpalette.so')
+    self._checker.check_no_library('libartpalette-system.so')
+    self._checker.check_library('libdexfile.so')
+    self._checker.check_library('libopenjdkjvm.so')
+    self._checker.check_library('libopenjdkjvmti.so')
+    self._checker.check_library('libprofile.so')
     # Check that the mounted image contains Android Core libraries.
-    self.check_library('libexpat.so')
-    self.check_library('libjavacore.so')
-    self.check_library('libopenjdk.so')
-    self.check_library('libz.so')
-    self.check_library('libziparchive.so')
+    # Note: host vs target libs are checked elsewhere.
+    self._checker.check_library('libjavacore.so')
+    self._checker.check_library('libopenjdk.so')
+    self._checker.check_library('libziparchive.so')
     # Check that the mounted image contains additional required libraries.
-    self.check_library('libadbconnection.so')
+    self._checker.check_library('libadbconnection.so')
 
     # TODO: Should we check for other libraries, such as:
     #
@@ -232,54 +378,75 @@
     #
     # ?
 
-    self.check_java_library('core-oj.jar')
-    self.check_java_library('core-libart.jar')
-    self.check_java_library('okhttp.jar')
-    self.check_java_library('bouncycastle.jar')
-    self.check_java_library('apache-xml.jar')
+    self._checker.check_java_library('core-oj.jar')
+    self._checker.check_java_library('core-libart.jar')
+    self._checker.check_java_library('okhttp.jar')
+    self._checker.check_java_library('bouncycastle.jar')
+    self._checker.check_java_library('apache-xml.jar')
 
-class DebugChecker(Checker):
-  def __init__(self, provider):
-    super().__init__(provider)
+class ReleaseTargetChecker:
+  def __init__(self, checker):
+    self._checker = checker
+  def __str__(self):
+    return 'Release (Target) Checker'
+
+  def run(self):
+    # Check that the mounted image contains Android Core libraries.
+    self._checker.check_library('libexpat.so')
+    self._checker.check_library('libz.so')
+
+class ReleaseHostChecker:
+  def __init__(self, checker):
+    self._checker = checker;
+  def __str__(self):
+    return 'Release (Host) Checker'
+
+  def run(self):
+    # Check that the mounted image contains Android Core libraries.
+    self._checker.check_library('libexpat-host.so')
+    self._checker.check_library('libz-host.so')
+
+class DebugChecker:
+  def __init__(self, checker):
+    self._checker = checker
   def __str__(self):
     return 'Debug Checker'
 
   def run(self):
     # Check that the mounted image contains ART tools binaries.
-    self.check_binary('dexdiag')
-    self.check_binary('dexdump')
-    self.check_binary('dexlist')
+    self._checker.check_binary('dexdiag')
+    self._checker.check_binary('dexdump')
+    self._checker.check_binary('dexlist')
 
     # Check that the mounted image contains ART debug binaries.
-    # TODO(b/123427238): This should probably be dex2oatd, fix!
-    self.check_binary('dex2oatd32')
-    self.check_binary('dexoptanalyzerd')
-    self.check_binary('profmand')
+    self._checker.check_binary('dex2oatd')
+    self._checker.check_binary('dexoptanalyzerd')
+    self._checker.check_binary('profmand')
 
     # Check that the mounted image contains Android Runtime debug libraries.
-    self.check_library('libartbased.so')
-    self.check_library('libartd-compiler.so')
-    self.check_library('libartd-dexlayout.so')
-    self.check_library('libartd.so')
-    self.check_library('libdexfiled.so')
-    self.check_library('libopenjdkjvmd.so')
-    self.check_library('libopenjdkjvmtid.so')
-    self.check_library('libprofiled.so')
+    self._checker.check_library('libartbased.so')
+    self._checker.check_library('libartd-compiler.so')
+    self._checker.check_library('libartd-dexlayout.so')
+    self._checker.check_library('libartd.so')
+    self._checker.check_library('libdexfiled.so')
+    self._checker.check_library('libopenjdkjvmd.so')
+    self._checker.check_library('libopenjdkjvmtid.so')
+    self._checker.check_library('libprofiled.so')
     # Check that the mounted image contains Android Core debug libraries.
-    self.check_library('libopenjdkd.so')
+    self._checker.check_library('libopenjdkd.so')
     # Check that the mounted image contains additional required debug libraries.
-    self.check_library('libadbconnectiond.so')
+    self._checker.check_library('libadbconnectiond.so')
 
-class DebugTargetChecker(Checker):
-  def __init__(self, provider):
-    super().__init__(provider)
+class DebugTargetChecker:
+  def __init__(self, checker):
+    self._checker = checker
   def __str__(self):
     return 'Debug (Target) Checker'
 
   def run(self):
     # Check for files pulled in from debug target-only oatdump.
-    self.check_binary('oatdump')
-    self.check_single_library('libart-disassembler.so')
+    self._checker.check_binary('oatdump')
+    self._checker.check_first_library('libart-disassembler.so')
 
 def print_list(provider):
     def print_list_impl(provider, path):
@@ -296,7 +463,7 @@
         print(new_path)
         if val.is_dir:
           print_list_impl(provider, new_path)
-    print_list_impl(provider, '.')
+    print_list_impl(provider, '')
 
 def print_tree(provider, title):
     def get_vertical(has_next_list):
@@ -326,36 +493,33 @@
           print_tree_impl(provider, os.path.join(path, val.name), has_next_list)
           has_next_list.pop()
     print('%s' % (title))
-    print_tree_impl(provider, '.', [])
+    print_tree_impl(provider, '', [])
 
 # Note: do not sys.exit early, for __del__ cleanup.
 def artApexTestMain(args):
-  if not args.host and not args.target and not args.debug and not args.tree and not args.list:
-    logging.error("None of --host, --target, --debug, --tree nor --list set")
+  if args.tree and args.debug:
+    logging.error("Both of --tree and --debug set")
     return 1
-  if args.tree and (args.host or args.debug):
-    logging.error("Both of --tree and --host|--debug set")
-    return 1
-  if args.list and (args.host or args.debug):
-    logging.error("Both of --list and --host|--debug set")
+  if args.list and args.debug:
+    logging.error("Both of --list and --debug set")
     return 1
   if args.list and args.tree:
     logging.error("Both of --list and --tree set")
     return 1
-  if args.host and (args.target or args.debug):
-    logging.error("Both of --host and --target|--debug set")
-    return 1
-  if args.debug and not args.target:
-    args.target = True
-  if args.target and not args.tmpdir:
+  if not args.tmpdir:
     logging.error("Need a tmpdir.")
     return 1
-  if args.target and not args.debugfs:
+  if not args.host and not args.debugfs:
     logging.error("Need debugfs.")
     return 1
+  if args.bitness not in ['32', '64', 'multilib', 'auto']:
+    logging.error('--bitness needs to be one of 32|64|multilib|auto')
 
   try:
-    apex_provider = TargetApexProvider(args.apex, args.tmpdir, args.debugfs)
+    if args.host:
+      apex_provider = HostApexProvider(args.apex, args.tmpdir)
+    else:
+      apex_provider = TargetApexProvider(args.apex, args.tmpdir, args.debugfs)
   except Exception as e:
     logging.error('Failed to create provider: %s', e)
     return 1
@@ -368,25 +532,52 @@
     return 0
 
   checkers = []
-  if args.host:
-    logging.error('host checking not yet supported')
-    return 1
+  if args.bitness == 'auto':
+    logging.warn('--bitness=auto, trying to autodetect. This may be incorrect!')
+    has_32 = apex_provider.get('lib') is not None
+    has_64 = apex_provider.get('lib64') is not None
+    if has_32 and has_64:
+      logging.warn('  Detected multilib')
+      args.bitness = 'multilib'
+    elif has_32:
+      logging.warn('  Detected 32-only')
+      args.bitness = '32'
+    elif has_64:
+      logging.warn('  Detected 64-only')
+      args.bitness = '64'
+    else:
+      logging.error('  Could not detect bitness, neither lib nor lib64 contained.')
+      print('%s' % (apex_provider._folder_cache))
+      return 1
 
-  checkers.append(ReleaseChecker(apex_provider))
+  if args.bitness == '32':
+    base_checker = Arch32Checker(apex_provider)
+  elif args.bitness == '64':
+    base_checker = Arch64Checker(apex_provider)
+  else:
+    assert args.bitness == 'multilib'
+    base_checker = MultilibChecker(apex_provider)
+
+  checkers.append(ReleaseChecker(base_checker))
+  if args.host:
+    checkers.append(ReleaseHostChecker(base_checker))
+  else:
+    checkers.append(ReleaseTargetChecker(base_checker))
   if args.debug:
-    checkers.append(DebugChecker(apex_provider))
-  if args.debug and args.target:
-    checkers.append(DebugTargetChecker(apex_provider))
+    checkers.append(DebugChecker(base_checker))
+  if args.debug and not args.host:
+    checkers.append(DebugTargetChecker(base_checker))
 
   failed = False
   for checker in checkers:
     logging.info('%s...', checker)
     checker.run()
-    if checker.error_count() > 0:
+    if base_checker.error_count() > 0:
       logging.error('%s FAILED', checker)
       failed = True
     else:
       logging.info('%s SUCCEEDED', checker)
+    base_checker.reset_errors()
 
   return 1 if failed else 0
 
@@ -405,6 +596,7 @@
   args.tmpdir = '.'
   args.tree = False
   args.list = False
+  args.bitness = 'auto'
   failed = False
 
   if not os.path.exists(args.debugfs):
@@ -414,8 +606,8 @@
 
   # TODO: Add host support
   configs= [
-    {'name': 'com.android.runtime.release', 'target': True, 'debug': False, 'host': False},
-    {'name': 'com.android.runtime.debug', 'target': True, 'debug': True, 'host': False},
+    {'name': 'com.android.runtime.release', 'debug': False, 'host': False},
+    {'name': 'com.android.runtime.debug', 'debug': True, 'host': False},
   ]
 
   for config in configs:
@@ -426,7 +618,6 @@
       failed = True
       logging.error("Cannot find APEX %s. Please build it first.", args.apex)
       continue
-    args.target = config['target']
     args.debug = config['debug']
     args.host = config['host']
     exit_code = artApexTestMain(args)
@@ -442,7 +633,7 @@
   parser.add_argument('apex', help='apex file input')
 
   parser.add_argument('--host', help='Check as host apex', action='store_true')
-  parser.add_argument('--target', help='Check as target apex', action='store_true')
+
   parser.add_argument('--debug', help='Check as debug apex', action='store_true')
 
   parser.add_argument('--list', help='List all files', action='store_true')
@@ -451,6 +642,8 @@
   parser.add_argument('--tmpdir', help='Directory for temp files')
   parser.add_argument('--debugfs', help='Path to debugfs')
 
+  parser.add_argument('--bitness', help='Bitness to check, 32|64|multilib|auto', default='auto')
+
   if len(sys.argv) == 1:
     artApexTestDefault(parser)
   else:
diff --git a/build/apex/ld.config.txt b/build/apex/ld.config.txt
index 1f69f91..9bf2ae5 100644
--- a/build/apex/ld.config.txt
+++ b/build/apex/ld.config.txt
@@ -30,7 +30,6 @@
 namespace.platform.asan.search.paths = /data/asan/system/${LIB}
 namespace.platform.links = default
 namespace.platform.link.default.shared_libs  = libart.so:libartd.so
-namespace.platform.link.default.shared_libs += libdexfile_external.so
 namespace.platform.link.default.shared_libs += libnativebridge.so
 namespace.platform.link.default.shared_libs += libnativehelper.so
 namespace.platform.link.default.shared_libs += libnativeloader.so
@@ -62,7 +61,8 @@
 
 namespace.conscrypt.search.paths = /apex/com.android.conscrypt/${LIB}
 namespace.conscrypt.asan.search.paths = /apex/com.android.conscrypt/${LIB}
-namespace.conscrypt.links = platform
+namespace.conscrypt.links = runtime,platform
+namespace.conscrypt.link.runtime.shared_libs   = libjavacore.so
 namespace.conscrypt.link.platform.shared_libs  = libc.so
 namespace.conscrypt.link.platform.shared_libs += libm.so
 namespace.conscrypt.link.platform.shared_libs += libdl.so
diff --git a/build/apex/runtests.sh b/build/apex/runtests.sh
index 48814df..95c1de9 100755
--- a/build/apex/runtests.sh
+++ b/build/apex/runtests.sh
@@ -77,15 +77,6 @@
   shift
 done
 
-if $print_image_tree_p; then
-  which tree >/dev/null || die "This script requires the 'tree' tool.
-On Debian-based systems, this can be installed with:
-
-   sudo apt-get install tree
-"
-fi
-
-
 # build_apex APEX_MODULE
 # ----------------------
 # Build APEX package APEX_MODULE.
@@ -96,24 +87,6 @@
   fi
 }
 
-# maybe_list_apex_contents MOUNT_POINT
-# ------------------------------------
-# If any listing/printing option was used, honor them and display the contents
-# of the APEX payload at MOUNT_POINT.
-function maybe_list_apex_contents {
-  local mount_point=$1
-
-  # List the contents of the mounted image using `find` (optional).
-  if $list_image_files_p; then
-    say "Listing image files" && find "$mount_point"
-  fi
-
-  # List the contents of the mounted image using `tree` (optional).
-  if $print_image_tree_p; then
-    say "Printing image tree" && ls -ld "$mount_point" && tree -aph --du "$mount_point"
-  fi
-}
-
 # maybe_list_apex_contents_apex APEX TMPDIR [other]
 function maybe_list_apex_contents_apex {
   local apex=$1
@@ -139,125 +112,6 @@
   exit_status=1
 }
 
-function check_file {
-  [[ -f "$mount_point/$1" ]] || fail_check "Cannot find file '$1' in mounted image"
-}
-
-function check_binary {
-  [[ -x "$mount_point/bin/$1" ]] || fail_check "Cannot find binary '$1' in mounted image"
-}
-
-function check_multilib_binary {
-  # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
-  # the precision of this test?
-  if ! [[ -L "$mount_point/bin/${1}" ]]; then
-    fail_check "Cannot find symlink for multilib binary '$1' in mounted image"
-  fi
-  [[ -x "$mount_point/bin/${1}32" ]] || [[ -x "$mount_point/bin/${1}64" ]] \
-    || fail_check "Cannot find binary '$1' in mounted image"
-}
-
-function check_binary_symlink {
-  [[ -h "$mount_point/bin/$1" ]] || fail_check "Cannot find symbolic link '$1' in mounted image"
-}
-
-function check_library {
-  # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
-  # the precision of this test?
-  [[ -f "$mount_point/lib/$1" ]] || [[ -f "$mount_point/lib64/$1" ]] \
-    || fail_check "Cannot find library '$1' in mounted image"
-}
-
-function check_java_library {
-  [[ -f "$mount_point/javalib/$1" ]] || fail_check "Cannot find java library '$1' in mounted image"
-}
-
-# !!! NOTE: Please also update art_apex_test.py !!!
-
-# Check contents of APEX payload located in `$mount_point`.
-function check_release_contents {
-  # Check that the mounted image contains an APEX manifest.
-  check_file apex_manifest.json
-
-  # Check that the mounted image contains ART base binaries.
-  check_multilib_binary dalvikvm
-  # TODO: Does not work yet (b/119942078).
-  : check_binary_symlink dalvikvm
-  check_binary dex2oat
-  check_binary dexoptanalyzer
-  check_binary profman
-
-  # oatdump is only in device apex's due to build rules
-  # TODO: Check for it when it is also built for host.
-  : check_binary oatdump
-
-  # Check that the mounted image contains Android Runtime libraries.
-  check_library libart-compiler.so
-  check_library libart-dexlayout.so
-  check_library libart.so
-  check_library libartbase.so
-  check_library libdexfile.so
-  check_library libdexfile_external.so
-  check_library libopenjdkjvm.so
-  check_library libopenjdkjvmti.so
-  check_library libprofile.so
-  # Check that the mounted image contains Android Core libraries.
-  check_library "libexpat${host_suffix}.so"
-  check_library libjavacore.so
-  check_library libopenjdk.so
-  check_library "libz${host_suffix}.so"
-  check_library libziparchive.so
-  # Check that the mounted image contains additional required libraries.
-  check_library libadbconnection.so
-
-  # TODO: Should we check for other libraries, such as:
-  #
-  #   libbacktrace.so
-  #   libbase.so
-  #   liblog.so
-  #   libsigchain.so
-  #   libtombstoned_client.so
-  #   libunwindstack.so
-  #   libvixl.so
-  #   libvixld.so
-  #   ...
-  #
-  # ?
-
-  check_java_library core-oj.jar
-  check_java_library core-libart.jar
-  check_java_library okhttp.jar
-  check_java_library bouncycastle.jar
-  check_java_library apache-xml.jar
-}
-
-# Check debug contents of APEX payload located in `$mount_point`.
-function check_debug_contents {
-  # Check that the mounted image contains ART tools binaries.
-  check_binary dexdiag
-  check_binary dexdump
-  check_binary dexlist
-
-  # Check that the mounted image contains ART debug binaries.
-  check_binary dex2oatd
-  check_binary dexoptanalyzerd
-  check_binary profmand
-
-  # Check that the mounted image contains Android Runtime debug libraries.
-  check_library libartbased.so
-  check_library libartd-compiler.so
-  check_library libartd-dexlayout.so
-  check_library libartd.so
-  check_library libdexfiled.so
-  check_library libopenjdkjvmd.so
-  check_library libopenjdkjvmtid.so
-  check_library libprofiled.so
-  # Check that the mounted image contains Android Core debug libraries.
-  check_library libopenjdkd.so
-  # Check that the mounted image contains additional required debug libraries.
-  check_library libadbconnectiond.so
-}
-
 # Testing target (device) APEX packages.
 # ======================================
 
@@ -290,14 +144,13 @@
 apex_path="$ANDROID_PRODUCT_OUT/system/apex/${apex_module}.apex"
 
 # List the contents of the APEX image (optional).
-maybe_list_apex_contents_apex $apex_path $work_dir --target --debugfs $ANDROID_HOST_OUT/bin/debugfs
+maybe_list_apex_contents_apex $apex_path $work_dir --debugfs $ANDROID_HOST_OUT/bin/debugfs
 
 # Run tests on APEX package.
 say "Checking APEX package $apex_module"
 $SCRIPT_DIR/art_apex_test.py \
   --tmpdir $work_dir \
   --debugfs $ANDROID_HOST_OUT/bin/debugfs \
-  --target \
   $apex_path \
     || fail_check "Release checks failed"
 
@@ -325,14 +178,13 @@
 apex_path="$ANDROID_PRODUCT_OUT/system/apex/${apex_module}.apex"
 
 # List the contents of the APEX image (optional).
-maybe_list_apex_contents_apex $apex_path $work_dir --target --debugfs $ANDROID_HOST_OUT/bin/debugfs
+maybe_list_apex_contents_apex $apex_path $work_dir --debugfs $ANDROID_HOST_OUT/bin/debugfs
 
 # Run tests on APEX package.
 say "Checking APEX package $apex_module"
 $SCRIPT_DIR/art_apex_test.py \
   --tmpdir $work_dir \
   --debugfs $ANDROID_HOST_OUT/bin/debugfs \
-  --target \
   --debug \
   $apex_path \
     || fail_check "Debug checks failed"
@@ -360,51 +212,30 @@
   cleanup_host
 }
 
-# setup_host_apex APEX_MODULE MOUNT_POINT
-# ---------------------------------------
-# Extract Zip file from host APEX_MODULE and extract it in MOUNT_POINT.
-function setup_host_apex {
-  local apex_module=$1
-  local mount_point=$2
-  local system_apexdir="$ANDROID_HOST_OUT/apex"
-  local apex_package="$system_apexdir/$apex_module.zipapex"
-
-  say "Extracting payload"
-
-  # Extract the payload from the Android Runtime APEX.
-  local image_filename="apex_payload.zip"
-  unzip -q "$apex_package" "$image_filename" -d "$work_dir"
-  mkdir "$mount_point"
-  local image_file="$work_dir/$image_filename"
-
-  # Unzipping the payload
-  unzip -q "$image_file" -d "$mount_point"
-}
-
 apex_module="com.android.runtime.host"
 test_status=0
 
 say "Processing APEX package $apex_module"
 
 work_dir=$(mktemp -d)
-mount_point="$work_dir/zip"
-host_suffix="-host"
 
 trap finish_host EXIT
 
 # Build the APEX package (optional).
 build_apex "$apex_module"
-
-# Set up APEX package.
-setup_host_apex "$apex_module" "$mount_point"
+apex_path="$ANDROID_HOST_OUT/apex/${apex_module}.zipapex"
 
 # List the contents of the APEX image (optional).
-maybe_list_apex_contents "$mount_point"
+maybe_list_apex_contents_apex $apex_path $work_dir --host
 
 # Run tests on APEX package.
 say "Checking APEX package $apex_module"
-check_release_contents "$apex_module"
-check_debug_contents
+$SCRIPT_DIR/art_apex_test.py \
+  --tmpdir $work_dir \
+  --host \
+  --debug \
+  $apex_path \
+    || fail_check "Debug checks failed"
 
 # Clean up.
 trap - EXIT
diff --git a/compiler/Android.bp b/compiler/Android.bp
index 0d92b05..0ebaa5f 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -182,7 +182,6 @@
     generated_sources: ["art_compiler_operator_srcs"],
     shared_libs: [
         "libbase",
-        "libcutils",  // for atrace.
     ],
     include_dirs: ["art/disassembler"],
     header_libs: [
@@ -197,7 +196,6 @@
     name: "libart-compiler_static_base_defaults",
     static_libs: [
         "libbase",
-        "libcutils",
     ],
 }
 
@@ -256,9 +254,10 @@
     },
     shared_libs: [
         "libart",
+        "libartbase",
+        "libartpalette",
         "libprofile",
         "libdexfile",
-        "libartbase",
     ],
 
     target: {
@@ -317,10 +316,11 @@
         },
     },
     shared_libs: [
+        "libartbased",
         "libartd",
+        "libartpalette",
         "libprofiled",
         "libdexfiled",
-        "libartbased",
     ],
 }
 
diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc
index 393db3d..e5c09aa 100644
--- a/compiler/debug/elf_debug_writer.cc
+++ b/compiler/debug/elf_debug_writer.cc
@@ -127,15 +127,21 @@
       new linker::ElfBuilder<ElfTypes>(isa, features, &out));
   builder->Start(/* write_program_headers= */ false);
   // Mirror ELF sections as NOBITS since the added symbols will reference them.
-  builder->GetText()->AllocateVirtualMemory(text_section_address, text_section_size);
+  if (text_section_size != 0) {
+    builder->GetText()->AllocateVirtualMemory(text_section_address, text_section_size);
+  }
   if (dex_section_size != 0) {
     builder->GetDex()->AllocateVirtualMemory(dex_section_address, dex_section_size);
   }
-  WriteDebugSymbols(builder.get(), /* mini-debug-info= */ true, debug_info);
-  WriteCFISection(builder.get(),
-                  debug_info.compiled_methods,
-                  dwarf::DW_DEBUG_FRAME_FORMAT,
-                  /* write_oat_patches= */ false);
+  if (!debug_info.Empty()) {
+    WriteDebugSymbols(builder.get(), /* mini-debug-info= */ true, debug_info);
+  }
+  if (!debug_info.compiled_methods.empty()) {
+    WriteCFISection(builder.get(),
+                    debug_info.compiled_methods,
+                    dwarf::DW_DEBUG_FRAME_FORMAT,
+                    /* write_oat_patches= */ false);
+  }
   builder->End();
   CHECK(builder->Good());
   std::vector<uint8_t> compressed_buffer;
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index e440eec..2ab4c60 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -17,13 +17,15 @@
 #include "compiler_driver.h"
 
 #include <unistd.h>
-#include <unordered_set>
-#include <vector>
 
 #ifndef __APPLE__
 #include <malloc.h>  // For mallinfo
 #endif
 
+#include <string_view>
+#include <unordered_set>
+#include <vector>
+
 #include "android-base/logging.h"
 #include "android-base/strings.h"
 
@@ -35,6 +37,7 @@
 #include "base/enums.h"
 #include "base/logging.h"  // For VLOG
 #include "base/stl_util.h"
+#include "base/string_view_cpp20.h"
 #include "base/systrace.h"
 #include "base/time_utils.h"
 #include "base/timing_logger.h"
@@ -1154,7 +1157,7 @@
   const PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
   while (!klass->IsObjectClass()) {
     const char* descriptor = klass->GetDescriptor(&temp);
-    if (image_classes->find(StringPiece(descriptor)) != image_classes->end()) {
+    if (image_classes->find(std::string_view(descriptor)) != image_classes->end()) {
       break;  // Previously inserted.
     }
     image_classes->insert(descriptor);
@@ -1236,7 +1239,7 @@
 
     bool operator()(ObjPtr<mirror::Class> klass) override REQUIRES_SHARED(Locks::mutator_lock_) {
       std::string temp;
-      StringPiece name(klass->GetDescriptor(&temp));
+      std::string_view name(klass->GetDescriptor(&temp));
       auto it = data_->image_class_descriptors_->find(name);
       if (it != data_->image_class_descriptors_->end()) {
         if (LIKELY(klass->IsResolved())) {
@@ -1776,11 +1779,15 @@
     return false;
   }
   TimingLogger::ScopedTiming t("Fast Verify", timings);
+
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<2> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader(
       hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
-  if (!verifier_deps->ValidateDependencies(class_loader, soa.Self())) {
+  std::string error_msg;
+
+  if (!verifier_deps->ValidateDependencies(class_loader, soa.Self(), &error_msg)) {
+    LOG(WARNING) << "Fast verification failed: " << error_msg;
     return false;
   }
 
@@ -2179,7 +2186,7 @@
             // We need to initialize static fields, we only do this for image classes that aren't
             // marked with the $NoPreloadHolder (which implies this should not be initialized
             // early).
-            can_init_static_fields = !StringPiece(descriptor).ends_with("$NoPreloadHolder;");
+            can_init_static_fields = !EndsWith(std::string_view(descriptor), "$NoPreloadHolder;");
           } else {
             CHECK(is_app_image);
             // The boot image case doesn't need to recursively initialize the dependencies with
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 8d1ae3d..7dd743f 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -17,6 +17,7 @@
 #include "compiler_options.h"
 
 #include <fstream>
+#include <string_view>
 
 #include "android-base/stringprintf.h"
 
@@ -144,7 +145,7 @@
   // Historical note: We used to hold the set indirectly and there was a distinction between an
   // empty set and a null, null meaning to include all classes. However, the distiction has been
   // removed; if we don't have a profile, we treat it as an empty set of classes. b/77340429
-  return image_classes_.find(StringPiece(descriptor)) != image_classes_.end();
+  return image_classes_.find(std::string_view(descriptor)) != image_classes_.end();
 }
 
 const VerificationResults* CompilerOptions::GetVerificationResults() const {
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index bd12bf7..fccd9ca 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -361,13 +361,6 @@
 
  private:
   bool ParseDumpInitFailures(const std::string& option, std::string* error_msg);
-  void ParseDumpCfgPasses(const StringPiece& option, UsageFn Usage);
-  void ParseInlineMaxCodeUnits(const StringPiece& option, UsageFn Usage);
-  void ParseNumDexMethods(const StringPiece& option, UsageFn Usage);
-  void ParseTinyMethodMax(const StringPiece& option, UsageFn Usage);
-  void ParseSmallMethodMax(const StringPiece& option, UsageFn Usage);
-  void ParseLargeMethodMax(const StringPiece& option, UsageFn Usage);
-  void ParseHugeMethodMax(const StringPiece& option, UsageFn Usage);
   bool ParseRegisterAllocationStrategy(const std::string& option, std::string* error_msg);
 
   CompilerFilter::Filter compiler_filter_;
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 0d35fec..4d7ae9b 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -99,7 +99,10 @@
       }
     }
   }
+
   if (instruction_set_features == nullptr) {
+    // '--instruction-set-features/--instruction-set-variant' were not used.
+    // Use build-time defined features.
     instruction_set_features = InstructionSetFeatures::FromCppDefines();
   }
   compiler_options_->instruction_set_features_ = std::move(instruction_set_features);
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 2721cb5..2de0f0c 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -20,6 +20,7 @@
 #include "art_method-inl.h"
 #include "base/utils.h"
 #include "class_linker.h"
+#include "class_root.h"
 #include "dex/invoke_type.h"
 #include "driver/compiler_options.h"
 #include "gc/space/image_space.h"
@@ -362,4 +363,13 @@
   return info;
 }
 
+void IntrinsicVisitor::AssertNonMovableStringClass() {
+  if (kIsDebugBuild) {
+    Thread* const self = Thread::Current();
+    ReaderMutexLock mu(self, *Locks::mutator_lock_);
+    ObjPtr<mirror::Class> string_class = GetClassRoot<art::mirror::String>();
+    CHECK(!art::Runtime::Current()->GetHeap()->IsMovableObject(string_class));
+  }
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index 50b13c8..ab68cce 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -142,6 +142,8 @@
  protected:
   IntrinsicVisitor() {}
 
+  static void AssertNonMovableStringClass();
+
  private:
   DISALLOW_COPY_AND_ASSIGN(IntrinsicVisitor);
 };
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index ca790f6..ec5d17a 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -1464,8 +1464,16 @@
     // All string objects must have the same type since String cannot be subclassed.
     // Receiver must be a string object, so its class field is equal to all strings' class fields.
     // If the argument is a string object, its class field must be equal to receiver's class field.
+    //
+    // As the String class is expected to be non-movable, we can read the class
+    // field from String.equals' arguments without read barriers.
+    AssertNonMovableStringClass();
+    // /* HeapReference<Class> */ temp = str->klass_
     __ Ldr(temp, MemOperand(str.X(), class_offset));
+    // /* HeapReference<Class> */ temp1 = arg->klass_
     __ Ldr(temp1, MemOperand(arg.X(), class_offset));
+    // Also, because we use the previously loaded class references only in the
+    // following comparison, we don't need to unpoison them.
     __ Cmp(temp, temp1);
     __ B(&return_false, ne);
   }
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 396ff62..f0aa92e 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -1529,8 +1529,16 @@
     // All string objects must have the same type since String cannot be subclassed.
     // Receiver must be a string object, so its class field is equal to all strings' class fields.
     // If the argument is a string object, its class field must be equal to receiver's class field.
+    //
+    // As the String class is expected to be non-movable, we can read the class
+    // field from String.equals' arguments without read barriers.
+    AssertNonMovableStringClass();
+    // /* HeapReference<Class> */ temp = str->klass_
     __ Ldr(temp, MemOperand(str, class_offset));
+    // /* HeapReference<Class> */ out = arg->klass_
     __ Ldr(out, MemOperand(arg, class_offset));
+    // Also, because we use the previously loaded class references only in the
+    // following comparison, we don't need to unpoison them.
     __ Cmp(temp, out);
     __ B(ne, &return_false, /* is_far_target= */ false);
   }
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 5b35974..3da0e57 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -1575,8 +1575,16 @@
     // All string objects must have the same type since String cannot be subclassed.
     // Receiver must be a string object, so its class field is equal to all strings' class fields.
     // If the argument is a string object, its class field must be equal to receiver's class field.
+    //
+    // As the String class is expected to be non-movable, we can read the class
+    // field from String.equals' arguments without read barriers.
+    AssertNonMovableStringClass();
+    // /* HeapReference<Class> */ temp1 = str->klass_
     __ Lw(temp1, str, class_offset);
+    // /* HeapReference<Class> */ temp2 = arg->klass_
     __ Lw(temp2, arg, class_offset);
+    // Also, because we use the previously loaded class references only in the
+    // following comparison, we don't need to unpoison them.
     __ Bne(temp1, temp2, &return_false);
   }
 
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index afaa4ca..3e68765 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1429,8 +1429,16 @@
     // All string objects must have the same type since String cannot be subclassed.
     // Receiver must be a string object, so its class field is equal to all strings' class fields.
     // If the argument is a string object, its class field must be equal to receiver's class field.
+    //
+    // As the String class is expected to be non-movable, we can read the class
+    // field from String.equals' arguments without read barriers.
+    AssertNonMovableStringClass();
+    // /* HeapReference<Class> */ temp1 = str->klass_
     __ Lw(temp1, str, class_offset);
+    // /* HeapReference<Class> */ temp2 = arg->klass_
     __ Lw(temp2, arg, class_offset);
+    // Also, because we use the previously loaded class references only in the
+    // following comparison, we don't need to unpoison them.
     __ Bnec(temp1, temp2, &return_false);
   }
 
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 8747f06..de697f0 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -1072,7 +1072,15 @@
     // All string objects must have the same type since String cannot be subclassed.
     // Receiver must be a string object, so its class field is equal to all strings' class fields.
     // If the argument is a string object, its class field must be equal to receiver's class field.
+    //
+    // As the String class is expected to be non-movable, we can read the class
+    // field from String.equals' arguments without read barriers.
+    AssertNonMovableStringClass();
+    // Also, because we use the loaded class references only to compare them, we
+    // don't need to unpoison them.
+    // /* HeapReference<Class> */ ecx = str->klass_
     __ movl(ecx, Address(str, class_offset));
+    // if (ecx != /* HeapReference<Class> */ arg->klass_) return false
     __ cmpl(ecx, Address(arg, class_offset));
     __ j(kNotEqual, &return_false);
   }
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 167c1d8..e79c0c9 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -1275,7 +1275,15 @@
     // All string objects must have the same type since String cannot be subclassed.
     // Receiver must be a string object, so its class field is equal to all strings' class fields.
     // If the argument is a string object, its class field must be equal to receiver's class field.
+    //
+    // As the String class is expected to be non-movable, we can read the class
+    // field from String.equals' arguments without read barriers.
+    AssertNonMovableStringClass();
+    // Also, because we use the loaded class references only to compare them, we
+    // don't need to unpoison them.
+    // /* HeapReference<Class> */ rcx = str->klass_
     __ movl(rcx, Address(str, class_offset));
+    // if (rcx != /* HeapReference<Class> */ arg->klass_) return false
     __ cmpl(rcx, Address(arg, class_offset));
     __ j(kNotEqual, &return_false);
   }
diff --git a/compiler/utils/swap_space.cc b/compiler/utils/swap_space.cc
index dee83d1..841ff1c 100644
--- a/compiler/utils/swap_space.cc
+++ b/compiler/utils/swap_space.cc
@@ -115,12 +115,11 @@
       ? free_by_size_.end()
       : free_by_size_.lower_bound(FreeBySizeEntry { size, free_by_start_.begin() });
   if (it != free_by_size_.end()) {
-    auto entry = it->free_by_start_entry;
-    SpaceChunk old_chunk = *entry;
+    SpaceChunk old_chunk = *it->free_by_start_entry;
     if (old_chunk.size == size) {
       RemoveChunk(it);
     } else {
-      // Try to avoid deallocating and allocating the std::set<> nodes.
+      // Avoid deallocating and allocating the std::set<> nodes.
       // This would be much simpler if we could use replace() from Boost.Bimap.
 
       // The free_by_start_ map contains disjoint intervals ordered by the `ptr`.
@@ -128,24 +127,9 @@
       it->free_by_start_entry->ptr += size;
       it->free_by_start_entry->size -= size;
 
-      // The free_by_size_ map is ordered by the `size` and then `free_by_start_entry->ptr`.
-      // Adjusting the `ptr` above does not change that ordering but decreasing `size` can
-      // push the node before the previous node(s).
-      if (it == free_by_size_.begin()) {
-        it->size -= size;
-      } else {
-        auto prev = it;
-        --prev;
-        FreeBySizeEntry new_value(old_chunk.size - size, entry);
-        if (free_by_size_.key_comp()(*prev, new_value)) {
-          it->size -= size;
-        } else {
-          // Changing in place would break the std::set<> ordering, we need to remove and insert.
-          // TODO: When C++17 becomes available, use std::map<>::extract(), modify, insert.
-          free_by_size_.erase(it);
-          free_by_size_.insert(new_value);
-        }
-      }
+      auto node = free_by_size_.extract(it);
+      node.value().size -= size;
+      free_by_size_.insert(std::move(node));
     }
     return old_chunk.ptr;
   } else {
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 092e931..5c6b815 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -449,6 +449,28 @@
            has_unverified_classes;
   }
 
+  // Load the dex file again with a new class loader, decode the VerifierDeps
+  // in `buffer`, allow the caller to modify the deps and then run validation.
+  template<typename Fn>
+  bool RunValidation(Fn fn, const std::vector<uint8_t>& buffer, std::string* error_msg) {
+    ScopedObjectAccess soa(Thread::Current());
+
+    jobject second_loader = LoadDex("VerifierDeps");
+    const auto& second_dex_files = GetDexFiles(second_loader);
+
+    VerifierDeps decoded_deps(second_dex_files, ArrayRef<const uint8_t>(buffer));
+    VerifierDeps::DexFileDeps* decoded_dex_deps =
+        decoded_deps.GetDexFileDeps(*second_dex_files.front());
+
+    // Let the test modify the dependencies.
+    fn(*decoded_dex_deps);
+
+    StackHandleScope<1> hs(soa.Self());
+    Handle<mirror::ClassLoader> new_class_loader =
+        hs.NewHandle<mirror::ClassLoader>(soa.Decode<mirror::ClassLoader>(second_loader));
+    return decoded_deps.ValidateDependencies(new_class_loader, soa.Self(), error_msg);
+  }
+
   std::unique_ptr<verifier::VerifierDeps> verifier_deps_;
   std::vector<const DexFile*> dex_files_;
   const DexFile* primary_dex_file_;
@@ -1177,8 +1199,9 @@
 }
 
 TEST_F(VerifierDepsTest, VerifyDeps) {
-  VerifyDexFile();
+  std::string error_msg;
 
+  VerifyDexFile();
   ASSERT_EQ(1u, NumberOfCompiledDexFiles());
   ASSERT_TRUE(HasEachKindOfRecord());
 
@@ -1186,249 +1209,166 @@
   // the existing `class_loader_` may contain erroneous classes,
   // that ClassLinker::FindClass won't return.
 
-  ScopedObjectAccess soa(Thread::Current());
-  StackHandleScope<1> hs(soa.Self());
-  MutableHandle<mirror::ClassLoader> new_class_loader(hs.NewHandle<mirror::ClassLoader>(nullptr));
-  {
-    new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
-    ASSERT_TRUE(verifier_deps_->ValidateDependencies(new_class_loader, soa.Self()));
-  }
-
   std::vector<uint8_t> buffer;
   verifier_deps_->Encode(dex_files_, &buffer);
   ASSERT_FALSE(buffer.empty());
 
-  {
-    VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
-    new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
-    ASSERT_TRUE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
-  }
+  // Check that dependencies are satisfied after decoding `buffer`.
+  ASSERT_TRUE(RunValidation([](VerifierDeps::DexFileDeps&) {}, buffer, &error_msg))
+      << error_msg;
 
-  // Fiddle with the dependencies to make sure we catch any change and fail to verify.
+  // Mess with the dependencies to make sure we catch any change and fail to verify.
+  ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+        deps.assignable_types_.insert(*deps.unassignable_types_.begin());
+      }, buffer, &error_msg));
 
-  {
-    // Mess up with the assignable_types.
-    VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
-    VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
-    deps->assignable_types_.insert(*deps->unassignable_types_.begin());
-    new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
-    ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
-  }
+  // Mess with the unassignable_types.
+  ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+        deps.unassignable_types_.insert(*deps.assignable_types_.begin());
+      }, buffer, &error_msg));
 
-  {
-    // Mess up with the unassignable_types.
-    VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
-    VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
-    deps->unassignable_types_.insert(*deps->assignable_types_.begin());
-    new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
-    ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
-  }
+  // Mess with classes.
+  ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+        for (const auto& entry : deps.classes_) {
+          if (entry.IsResolved()) {
+            deps.classes_.insert(VerifierDeps::ClassResolution(
+                entry.GetDexTypeIndex(), VerifierDeps::kUnresolvedMarker));
+            return;
+          }
+        }
+        LOG(FATAL) << "Could not find any resolved classes";
+        UNREACHABLE();
+      }, buffer, &error_msg));
+  ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+        for (const auto& entry : deps.classes_) {
+          if (!entry.IsResolved()) {
+            deps.classes_.insert(VerifierDeps::ClassResolution(
+                entry.GetDexTypeIndex(), VerifierDeps::kUnresolvedMarker - 1));
+            return;
+          }
+        }
+        LOG(FATAL) << "Could not find any unresolved classes";
+        UNREACHABLE();
+      }, buffer, &error_msg));
+  ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+        for (const auto& entry : deps.classes_) {
+          if (entry.IsResolved()) {
+            deps.classes_.insert(VerifierDeps::ClassResolution(
+                entry.GetDexTypeIndex(), entry.GetAccessFlags() - 1));
+            return;
+          }
+        }
+        LOG(FATAL) << "Could not find any resolved classes";
+        UNREACHABLE();
+      }, buffer, &error_msg));
 
-  // Mess up with classes.
-  {
-    VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
-    VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
-    bool found = false;
-    for (const auto& entry : deps->classes_) {
-      if (entry.IsResolved()) {
-        deps->classes_.insert(VerifierDeps::ClassResolution(
-            entry.GetDexTypeIndex(), VerifierDeps::kUnresolvedMarker));
-        found = true;
-        break;
-      }
-    }
-    ASSERT_TRUE(found);
-    new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
-    ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
-  }
+  // Mess with fields.
+  ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+        for (const auto& entry : deps.fields_) {
+          if (entry.IsResolved()) {
+            deps.fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(),
+                                                              VerifierDeps::kUnresolvedMarker,
+                                                              entry.GetDeclaringClassIndex()));
+            return;
+          }
+        }
+        LOG(FATAL) << "Could not find any resolved fields";
+        UNREACHABLE();
+      }, buffer, &error_msg));
+  ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+        for (const auto& entry : deps.fields_) {
+          if (!entry.IsResolved()) {
+            constexpr dex::StringIndex kStringIndexZero(0);  // We know there is a class there.
+            deps.fields_.insert(VerifierDeps::FieldResolution(0 /* we know there is a field there */,
+                                                              VerifierDeps::kUnresolvedMarker - 1,
+                                                              kStringIndexZero));
+            return;
+          }
+        }
+        LOG(FATAL) << "Could not find any unresolved fields";
+        UNREACHABLE();
+      }, buffer, &error_msg));
+  ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+        for (const auto& entry : deps.fields_) {
+          if (entry.IsResolved()) {
+            deps.fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(),
+                                                              entry.GetAccessFlags() - 1,
+                                                              entry.GetDeclaringClassIndex()));
+            return;
+          }
+        }
+        LOG(FATAL) << "Could not find any resolved fields";
+        UNREACHABLE();
+      }, buffer, &error_msg));
+  ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+        for (const auto& entry : deps.fields_) {
+          constexpr dex::StringIndex kNewTypeIndex(0);
+          if (entry.GetDeclaringClassIndex() != kNewTypeIndex) {
+            deps.fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(),
+                                                              entry.GetAccessFlags(),
+                                                              kNewTypeIndex));
+            return;
+          }
+        }
+        LOG(FATAL) << "Could not find any suitable fields";
+        UNREACHABLE();
+      }, buffer, &error_msg));
 
-  {
-    VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
-    VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
-    bool found = false;
-    for (const auto& entry : deps->classes_) {
-      if (!entry.IsResolved()) {
-        deps->classes_.insert(VerifierDeps::ClassResolution(
-            entry.GetDexTypeIndex(), VerifierDeps::kUnresolvedMarker - 1));
-        found = true;
-        break;
-      }
-    }
-    ASSERT_TRUE(found);
-    new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
-    ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
-  }
-
-  {
-    VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
-    VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
-    bool found = false;
-    for (const auto& entry : deps->classes_) {
-      if (entry.IsResolved()) {
-        deps->classes_.insert(VerifierDeps::ClassResolution(
-            entry.GetDexTypeIndex(), entry.GetAccessFlags() - 1));
-        found = true;
-        break;
-      }
-    }
-    ASSERT_TRUE(found);
-    new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
-    ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
-  }
-
-  // Mess up with fields.
-  {
-    VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
-    VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
-    bool found = false;
-    for (const auto& entry : deps->fields_) {
-      if (entry.IsResolved()) {
-        deps->fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(),
-                                                           VerifierDeps::kUnresolvedMarker,
-                                                           entry.GetDeclaringClassIndex()));
-        found = true;
-        break;
-      }
-    }
-    ASSERT_TRUE(found);
-    new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
-    ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
-  }
-
-  {
-    VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
-    VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
-    bool found = false;
-    for (const auto& entry : deps->fields_) {
-      if (!entry.IsResolved()) {
-        constexpr dex::StringIndex kStringIndexZero(0);  // We know there is a class there.
-        deps->fields_.insert(VerifierDeps::FieldResolution(0 /* we know there is a field there */,
-                                                           VerifierDeps::kUnresolvedMarker - 1,
-                                                           kStringIndexZero));
-        found = true;
-        break;
-      }
-    }
-    ASSERT_TRUE(found);
-    new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
-    ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
-  }
-
-  {
-    VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
-    VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
-    bool found = false;
-    for (const auto& entry : deps->fields_) {
-      if (entry.IsResolved()) {
-        deps->fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(),
-                                                           entry.GetAccessFlags() - 1,
-                                                           entry.GetDeclaringClassIndex()));
-        found = true;
-        break;
-      }
-    }
-    ASSERT_TRUE(found);
-    new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
-    ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
-  }
-
-  {
-    VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
-    VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
-    bool found = false;
-    for (const auto& entry : deps->fields_) {
-      constexpr dex::StringIndex kNewTypeIndex(0);
-      if (entry.GetDeclaringClassIndex() != kNewTypeIndex) {
-        deps->fields_.insert(VerifierDeps::FieldResolution(entry.GetDexFieldIndex(),
-                                                           entry.GetAccessFlags(),
-                                                           kNewTypeIndex));
-        found = true;
-        break;
-      }
-    }
-    ASSERT_TRUE(found);
-    new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
-    ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
-  }
-
-  // Mess up with methods.
-  {
-    VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
-    VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
-    bool found = false;
-    std::set<VerifierDeps::MethodResolution>* methods = &deps->methods_;
-    for (const auto& entry : *methods) {
-      if (entry.IsResolved()) {
-        methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
-                                                       VerifierDeps::kUnresolvedMarker,
-                                                       entry.GetDeclaringClassIndex()));
-        found = true;
-        break;
-      }
-    }
-    ASSERT_TRUE(found);
-    new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
-    ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
-  }
-
-  {
-    VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
-    VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
-    bool found = false;
-    std::set<VerifierDeps::MethodResolution>* methods = &deps->methods_;
-    for (const auto& entry : *methods) {
-      if (!entry.IsResolved()) {
-        constexpr dex::StringIndex kStringIndexZero(0);  // We know there is a class there.
-        methods->insert(VerifierDeps::MethodResolution(0 /* we know there is a method there */,
-                                                       VerifierDeps::kUnresolvedMarker - 1,
-                                                       kStringIndexZero));
-        found = true;
-        break;
-      }
-    }
-    ASSERT_TRUE(found);
-    new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
-    ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
-  }
-
-  {
-    VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
-    VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
-    bool found = false;
-    std::set<VerifierDeps::MethodResolution>* methods = &deps->methods_;
-    for (const auto& entry : *methods) {
-      if (entry.IsResolved()) {
-        methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
-                                                       entry.GetAccessFlags() - 1,
-                                                       entry.GetDeclaringClassIndex()));
-        found = true;
-        break;
-      }
-    }
-    ASSERT_TRUE(found);
-    new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
-    ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
-  }
-
-  {
-    VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
-    VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
-    bool found = false;
-    std::set<VerifierDeps::MethodResolution>* methods = &deps->methods_;
-    for (const auto& entry : *methods) {
-      constexpr dex::StringIndex kNewTypeIndex(0);
-      if (entry.IsResolved() && entry.GetDeclaringClassIndex() != kNewTypeIndex) {
-        methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
-                                                       entry.GetAccessFlags(),
-                                                       kNewTypeIndex));
-        found = true;
-        break;
-      }
-    }
-    ASSERT_TRUE(found);
-    new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
-    ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
-  }
+  // Mess with methods.
+  ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+        std::set<VerifierDeps::MethodResolution>* methods = &deps.methods_;
+        for (const auto& entry : *methods) {
+          if (entry.IsResolved()) {
+            methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
+                                                          VerifierDeps::kUnresolvedMarker,
+                                                          entry.GetDeclaringClassIndex()));
+            return;
+          }
+        }
+        LOG(FATAL) << "Could not find any resolved methods";
+        UNREACHABLE();
+      }, buffer, &error_msg));
+  ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+        std::set<VerifierDeps::MethodResolution>* methods = &deps.methods_;
+        for (const auto& entry : *methods) {
+          if (!entry.IsResolved()) {
+            constexpr dex::StringIndex kStringIndexZero(0);  // We know there is a class there.
+            methods->insert(VerifierDeps::MethodResolution(0 /* we know there is a method there */,
+                                                          VerifierDeps::kUnresolvedMarker - 1,
+                                                          kStringIndexZero));
+            return;
+          }
+        }
+        LOG(FATAL) << "Could not find any unresolved methods";
+        UNREACHABLE();
+      }, buffer, &error_msg));
+  ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+        std::set<VerifierDeps::MethodResolution>* methods = &deps.methods_;
+        for (const auto& entry : *methods) {
+          if (entry.IsResolved()) {
+            methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
+                                                          entry.GetAccessFlags() - 1,
+                                                          entry.GetDeclaringClassIndex()));
+            return;
+          }
+        }
+        LOG(FATAL) << "Could not find any resolved methods";
+        UNREACHABLE();
+      }, buffer, &error_msg));
+  ASSERT_FALSE(RunValidation([](VerifierDeps::DexFileDeps& deps) {
+        std::set<VerifierDeps::MethodResolution>* methods = &deps.methods_;
+        for (const auto& entry : *methods) {
+          constexpr dex::StringIndex kNewTypeIndex(0);
+          if (entry.IsResolved() && entry.GetDeclaringClassIndex() != kNewTypeIndex) {
+            methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
+                                                          entry.GetAccessFlags(),
+                                                          kNewTypeIndex));
+            return;
+          }
+        }
+        LOG(FATAL) << "Could not find any suitable methods";
+        UNREACHABLE();
+      }, buffer, &error_msg));
 }
 
 TEST_F(VerifierDepsTest, CompilerDriver) {
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index 6a4a88e..20d41b4 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -64,8 +64,6 @@
 
     target: {
         android: {
-            // For atrace.
-            shared_libs: ["libcutils"],
             static_libs: [
                 "libz",
             ],
@@ -93,11 +91,6 @@
 
 cc_defaults {
     name: "libart-dex2oat_static_base_defaults",
-    target: {
-        android: {
-            static_libs: ["libcutils"],
-        },
-    },
     static_libs: [
         "libbase",
         "libz",
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 35af918..ad1dda4 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -296,6 +296,10 @@
   UsageError("      Default: arm");
   UsageError("");
   UsageError("  --instruction-set-features=...,: Specify instruction set features");
+  UsageError("      On target the value 'runtime' can be used to detect features at run time.");
+  UsageError("      If target does not support run-time detection the value 'runtime'");
+  UsageError("      has the same effect as the value 'default'.");
+  UsageError("      Note: the value 'runtime' has no effect if it is used on host.");
   UsageError("      Example: --instruction-set-features=div");
   UsageError("      Default: default");
   UsageError("");
@@ -875,9 +879,9 @@
       oat_unstripped_ = std::move(parser_options->oat_symbols);
     }
 
-    // If no instruction set feature was given, use the default one for the target
-    // instruction set.
-    if (compiler_options_->instruction_set_features_.get() == nullptr) {
+    if (compiler_options_->instruction_set_features_ == nullptr) {
+      // '--instruction-set-features/--instruction-set-variant' were not used.
+      // Use features for the 'default' variant.
       compiler_options_->instruction_set_features_ = InstructionSetFeatures::FromVariant(
           compiler_options_->instruction_set_, "default", &parser_options->error_msg);
       if (compiler_options_->instruction_set_features_ == nullptr) {
@@ -890,9 +894,9 @@
       std::unique_ptr<const InstructionSetFeatures> runtime_features(
           InstructionSetFeatures::FromCppDefines());
       if (!compiler_options_->GetInstructionSetFeatures()->Equals(runtime_features.get())) {
-        LOG(WARNING) << "Mismatch between dex2oat instruction set features ("
+        LOG(WARNING) << "Mismatch between dex2oat instruction set features to use ("
             << *compiler_options_->GetInstructionSetFeatures()
-            << ") and those of dex2oat executable (" << *runtime_features
+            << ") and those from CPP defines (" << *runtime_features
             << ") for the command line:\n" << CommandLine();
       }
     }
@@ -1367,6 +1371,7 @@
           LOG(WARNING) << "Could not open vdex file in DexMetadata archive: " << error_msg;
         } else {
           input_vdex_file_ = std::make_unique<VdexFile>(std::move(input_file));
+          VLOG(verifier) << "Doing fast verification with vdex from DexMetadata archive";
         }
       }
     }
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 524bce0..d3bfb57 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <algorithm>
 #include <regex>
 #include <sstream>
 #include <string>
@@ -28,6 +29,7 @@
 
 #include "common_runtime_test.h"
 
+#include "arch/instruction_set_features.h"
 #include "base/macros.h"
 #include "base/mutex-inl.h"
 #include "base/utils.h"
@@ -2315,4 +2317,38 @@
   }));
 }
 
+class Dex2oatISAFeaturesRuntimeDetectionTest : public Dex2oatTest {
+ protected:
+  void RunTest(const std::vector<std::string>& extra_args = {}) {
+    std::string dex_location = GetScratchDir() + "/Dex2OatSwapTest.jar";
+    std::string odex_location = GetOdexDir() + "/Dex2OatSwapTest.odex";
+
+    Copy(GetTestDexFileName(), dex_location);
+
+    ASSERT_TRUE(GenerateOdexForTest(dex_location,
+                                    odex_location,
+                                    CompilerFilter::kSpeed,
+                                    extra_args));
+  }
+
+  std::string GetTestDexFileName() {
+    return GetDexSrc1();
+  }
+};
+
+TEST_F(Dex2oatISAFeaturesRuntimeDetectionTest, TestCurrentRuntimeFeaturesAsDex2OatArguments) {
+  std::vector<std::string> argv;
+  Runtime::Current()->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
+  auto option_pos =
+      std::find(std::begin(argv), std::end(argv), "--instruction-set-features=runtime");
+  if (InstructionSetFeatures::IsRuntimeDetectionSupported()) {
+    EXPECT_TRUE(kIsTargetBuild);
+    EXPECT_NE(option_pos, std::end(argv));
+  } else {
+    EXPECT_EQ(option_pos, std::end(argv));
+  }
+
+  RunTest();
+}
+
 }  // namespace art
diff --git a/dex2oat/linker/elf_writer_quick.cc b/dex2oat/linker/elf_writer_quick.cc
index 9fbcca4..b3e8290 100644
--- a/dex2oat/linker/elf_writer_quick.cc
+++ b/dex2oat/linker/elf_writer_quick.cc
@@ -261,7 +261,7 @@
 
 template <typename ElfTypes>
 void ElfWriterQuick<ElfTypes>::PrepareDebugInfo(const debug::DebugInfo& debug_info) {
-  if (!debug_info.Empty() && compiler_options_.GetGenerateMiniDebugInfo()) {
+  if (compiler_options_.GetGenerateMiniDebugInfo()) {
     // Prepare the mini-debug-info in background while we do other I/O.
     Thread* self = Thread::Current();
     debug_info_task_ = std::make_unique<DebugInfoTask>(
@@ -280,19 +280,17 @@
 
 template <typename ElfTypes>
 void ElfWriterQuick<ElfTypes>::WriteDebugInfo(const debug::DebugInfo& debug_info) {
-  if (!debug_info.Empty()) {
-    if (compiler_options_.GetGenerateMiniDebugInfo()) {
-      // Wait for the mini-debug-info generation to finish and write it to disk.
-      Thread* self = Thread::Current();
-      DCHECK(debug_info_thread_pool_ != nullptr);
-      debug_info_thread_pool_->Wait(self, true, false);
-      builder_->WriteSection(".gnu_debugdata", debug_info_task_->GetResult());
-    }
-    // The Strip method expects debug info to be last (mini-debug-info is not stripped).
-    if (compiler_options_.GetGenerateDebugInfo()) {
-      // Generate all the debug information we can.
-      debug::WriteDebugInfo(builder_.get(), debug_info, kCFIFormat, true /* write_oat_patches */);
-    }
+  if (compiler_options_.GetGenerateMiniDebugInfo()) {
+    // Wait for the mini-debug-info generation to finish and write it to disk.
+    Thread* self = Thread::Current();
+    DCHECK(debug_info_thread_pool_ != nullptr);
+    debug_info_thread_pool_->Wait(self, true, false);
+    builder_->WriteSection(".gnu_debugdata", debug_info_task_->GetResult());
+  }
+  // The Strip method expects debug info to be last (mini-debug-info is not stripped).
+  if (!debug_info.Empty() && compiler_options_.GetGenerateDebugInfo()) {
+    // Generate all the debug information we can.
+    debug::WriteDebugInfo(builder_.get(), debug_info, kCFIFormat, true /* write_oat_patches */);
   }
 }
 
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index 8c9dfb8..431ab90 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -21,6 +21,7 @@
 
 #include <memory>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include "android-base/stringprintf.h"
@@ -498,7 +499,7 @@
       ObjPtr<mirror::Class> klass = class_linker_->FindSystemClass(soa.Self(), descriptor);
       EXPECT_TRUE(klass != nullptr) << descriptor;
       uint8_t* raw_klass = reinterpret_cast<uint8_t*>(klass.Ptr());
-      if (image_classes.find(StringPiece(descriptor)) == image_classes.end()) {
+      if (image_classes.find(std::string_view(descriptor)) == image_classes.end()) {
         EXPECT_TRUE(raw_klass >= image_end || raw_klass < image_begin) << descriptor;
       } else {
         // Image classes should be located inside the image.
diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp
index 5aa8236..838510b 100644
--- a/dexlayout/Android.bp
+++ b/dexlayout/Android.bp
@@ -29,16 +29,18 @@
     target: {
         android: {
             shared_libs: [
-                "libdexfile",
                 "libartbase",
+                "libartpalette",
+                "libdexfile",
                 "libprofile",
                 "libbase",
             ],
         },
         not_windows: {
             shared_libs: [
-                "libdexfile",
                 "libartbase",
+                "libartpalette",
+                "libdexfile",
                 "libprofile",
                 "libbase",
             ],
@@ -46,8 +48,9 @@
         windows: {
             cflags: ["-Wno-thread-safety"],
             static_libs: [
-                "libdexfile",
                 "libartbase",
+                "libartpalette",
+                "libdexfile",
                 "libprofile",
                 "libbase",
             ],
diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc
index 92850f7..988c612 100644
--- a/dexoptanalyzer/dexoptanalyzer.cc
+++ b/dexoptanalyzer/dexoptanalyzer.cc
@@ -15,14 +15,16 @@
  */
 
 #include <string>
+#include <string_view>
 
-#include "base/logging.h"  // For InitLogging.
-#include "base/mutex.h"
-#include "base/os.h"
-#include "base/utils.h"
 #include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 #include "base/file_utils.h"
+#include "base/logging.h"  // For InitLogging.
+#include "base/mutex.h"
+#include "base/os.h"
+#include "base/string_view_cpp20.h"
+#include "base/utils.h"
 #include "compiler_filter.h"
 #include "class_loader_context.h"
 #include "dex/dex_file.h"
@@ -155,56 +157,57 @@
     }
 
     for (int i = 0; i < argc; ++i) {
-      const StringPiece option(argv[i]);
+      const char* raw_option = argv[i];
+      const std::string_view option(raw_option);
       if (option == "--assume-profile-changed") {
         assume_profile_changed_ = true;
-      } else if (option.starts_with("--dex-file=")) {
-        dex_file_ = option.substr(strlen("--dex-file=")).ToString();
-      } else if (option.starts_with("--compiler-filter=")) {
-        std::string filter_str = option.substr(strlen("--compiler-filter=")).ToString();
-        if (!CompilerFilter::ParseCompilerFilter(filter_str.c_str(), &compiler_filter_)) {
-          Usage("Invalid compiler filter '%s'", option.data());
+      } else if (StartsWith(option, "--dex-file=")) {
+        dex_file_ = std::string(option.substr(strlen("--dex-file=")));
+      } else if (StartsWith(option, "--compiler-filter=")) {
+        const char* filter_str = raw_option + strlen("--compiler-filter=");
+        if (!CompilerFilter::ParseCompilerFilter(filter_str, &compiler_filter_)) {
+          Usage("Invalid compiler filter '%s'", raw_option);
         }
-      } else if (option.starts_with("--isa=")) {
-        std::string isa_str = option.substr(strlen("--isa=")).ToString();
-        isa_ = GetInstructionSetFromString(isa_str.c_str());
+      } else if (StartsWith(option, "--isa=")) {
+        const char* isa_str = raw_option + strlen("--isa=");
+        isa_ = GetInstructionSetFromString(isa_str);
         if (isa_ == InstructionSet::kNone) {
-          Usage("Invalid isa '%s'", option.data());
+          Usage("Invalid isa '%s'", raw_option);
         }
-      } else if (option.starts_with("--image=")) {
-        image_ = option.substr(strlen("--image=")).ToString();
+      } else if (StartsWith(option, "--image=")) {
+        image_ = std::string(option.substr(strlen("--image=")));
       } else if (option == "--runtime-arg") {
         if (i + 1 == argc) {
           Usage("Missing argument for --runtime-arg\n");
         }
         ++i;
         runtime_args_.push_back(argv[i]);
-      } else if (option.starts_with("--android-data=")) {
+      } else if (StartsWith(option, "--android-data=")) {
         // Overwrite android-data if needed (oat file assistant relies on a valid directory to
         // compute dalvik-cache folder). This is mostly used in tests.
-        std::string new_android_data = option.substr(strlen("--android-data=")).ToString();
-        setenv("ANDROID_DATA", new_android_data.c_str(), 1);
-      } else if (option.starts_with("--downgrade")) {
+        const char* new_android_data = raw_option + strlen("--android-data=");
+        setenv("ANDROID_DATA", new_android_data, 1);
+      } else if (option == "--downgrade") {
         downgrade_ = true;
-      } else if (option.starts_with("--oat-fd")) {
-        oat_fd_ = std::stoi(option.substr(strlen("--oat-fd=")).ToString(), nullptr, 0);
+      } else if (StartsWith(option, "--oat-fd=")) {
+        oat_fd_ = std::stoi(std::string(option.substr(strlen("--oat-fd="))), nullptr, 0);
         if (oat_fd_ < 0) {
           Usage("Invalid --oat-fd %d", oat_fd_);
         }
-      } else if (option.starts_with("--vdex-fd")) {
-        vdex_fd_ = std::stoi(option.substr(strlen("--vdex-fd=")).ToString(), nullptr, 0);
+      } else if (StartsWith(option, "--vdex-fd=")) {
+        vdex_fd_ = std::stoi(std::string(option.substr(strlen("--vdex-fd="))), nullptr, 0);
         if (vdex_fd_ < 0) {
           Usage("Invalid --vdex-fd %d", vdex_fd_);
         }
-      } else if (option.starts_with("--zip-fd")) {
-          zip_fd_ = std::stoi(option.substr(strlen("--zip-fd=")).ToString(), nullptr, 0);
-          if (zip_fd_ < 0) {
-            Usage("Invalid --zip-fd %d", zip_fd_);
-          }
-      } else if (option.starts_with("--class-loader-context=")) {
-        context_str_ = option.substr(strlen("--class-loader-context=")).ToString();
+      } else if (StartsWith(option, "--zip-fd=")) {
+        zip_fd_ = std::stoi(std::string(option.substr(strlen("--zip-fd="))), nullptr, 0);
+        if (zip_fd_ < 0) {
+          Usage("Invalid --zip-fd %d", zip_fd_);
+        }
+      } else if (StartsWith(option, "--class-loader-context=")) {
+        context_str_ = std::string(option.substr(strlen("--class-loader-context=")));
       } else {
-        Usage("Unknown argument '%s'", option.data());
+        Usage("Unknown argument '%s'", raw_option);
       }
     }
 
diff --git a/imgdiag/Android.bp b/imgdiag/Android.bp
index 972c8f7..39720a0 100644
--- a/imgdiag/Android.bp
+++ b/imgdiag/Android.bp
@@ -31,9 +31,6 @@
         "libbase",
     ],
     target: {
-        android: {
-            shared_libs: ["libcutils"],
-        },
         host: {
             shared_libs: ["libziparchive"],
         },
diff --git a/libartbase/Android.bp b/libartbase/Android.bp
index 509b072..1ca7011 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -61,7 +61,7 @@
             shared_libs: [
                 "liblog",
                 // For ashmem.
-                "libcutils",
+                "libartpalette",
                 // For common macros.
                 "libbase",
             ],
@@ -80,7 +80,7 @@
                 "libz",
                 "liblog",
                 // For ashmem.
-                "libcutils",
+                "libartpalette",
                 // For common macros.
                 "libbase",
             ],
@@ -99,7 +99,7 @@
                 "libz",
                 "liblog",
                 // For ashmem.
-                "libcutils",
+                "libartpalette",
                 // For common macros.
                 "libbase",
             ],
@@ -128,7 +128,7 @@
     name: "libartbase_static_base_defaults",
     static_libs: [
         "libbase",
-        "libcutils",
+        "libartpalette",
         "liblog",
         "libz",
         "libziparchive",
diff --git a/libartbase/base/hash_set.h b/libartbase/base/hash_set.h
index 42aa46f..99b3df4 100644
--- a/libartbase/base/hash_set.h
+++ b/libartbase/base/hash_set.h
@@ -139,7 +139,8 @@
                                                 std::hash<T>>::type;
 
 struct DefaultStringEquals {
-  // Allow comparison with anything that can be compared to std::string, for example StringPiece.
+  // Allow comparison with anything that can be compared to std::string,
+  // for example std::string_view.
   template <typename T>
   bool operator()(const std::string& lhs, const T& rhs) const {
     return lhs == rhs;
diff --git a/libartbase/base/hash_set_test.cc b/libartbase/base/hash_set_test.cc
index 782a68b..0646967 100644
--- a/libartbase/base/hash_set_test.cc
+++ b/libartbase/base/hash_set_test.cc
@@ -20,12 +20,12 @@
 #include <map>
 #include <sstream>
 #include <string>
+#include <string_view>
 #include <unordered_set>
 #include <vector>
 
 #include <gtest/gtest.h>
 
-#include "base/stringpiece.h"
 #include "hash_map.h"
 
 namespace art {
@@ -365,11 +365,11 @@
   ASSERT_EQ(*it, *cit);
 }
 
-TEST_F(HashSetTest, StringSearchyStringPiece) {
+TEST_F(HashSetTest, StringSearchyStringView) {
   const char* test_string = "dummy";
   HashSet<std::string> hash_set;
   HashSet<std::string>::iterator insert_pos = hash_set.insert(test_string);
-  HashSet<std::string>::iterator it = hash_set.find(StringPiece(test_string));
+  HashSet<std::string>::iterator it = hash_set.find(std::string_view(test_string));
   ASSERT_TRUE(it == insert_pos);
 }
 
diff --git a/libartbase/base/mem_map.cc b/libartbase/base/mem_map.cc
index 2833750..ba2a7c6 100644
--- a/libartbase/base/mem_map.cc
+++ b/libartbase/base/mem_map.cc
@@ -796,13 +796,13 @@
     // Shrink the reservation MemMap and update its `gMaps` entry.
     std::lock_guard<std::mutex> mu(*mem_maps_lock_);
     auto it = GetGMapsEntry(*this);
-    // TODO: When C++17 becomes available, use std::map<>::extract(), modify, insert.
-    gMaps->erase(it);
+    auto node = gMaps->extract(it);
     begin_ += byte_count;
     size_ -= byte_count;
     base_begin_ = begin_;
     base_size_ = size_;
-    gMaps->emplace(base_begin_, this);
+    node.key() = base_begin_;
+    gMaps->insert(std::move(node));
   }
 }
 
@@ -1266,9 +1266,9 @@
   std::lock_guard<std::mutex> mu(*mem_maps_lock_);
   if (base_begin < aligned_base_begin) {
     auto it = GetGMapsEntry(*this);
-    // TODO: When C++17 becomes available, use std::map<>::extract(), modify, insert.
-    gMaps->erase(it);
-    gMaps->insert(std::make_pair(aligned_base_begin, this));
+    auto node = gMaps->extract(it);
+    node.key() = aligned_base_begin;
+    gMaps->insert(std::move(node));
   }
   base_begin_ = aligned_base_begin;
   base_size_ = aligned_base_size;
diff --git a/libartbase/base/string_view_cpp20.h b/libartbase/base/string_view_cpp20.h
new file mode 100644
index 0000000..2c11a2f
--- /dev/null
+++ b/libartbase/base/string_view_cpp20.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef ART_LIBARTBASE_BASE_STRING_VIEW_CPP20_H_
+#define ART_LIBARTBASE_BASE_STRING_VIEW_CPP20_H_
+
+#include <string_view>
+
+namespace art {
+
+// Replacement functions for std::string_view::starts_with(), ends_with()
+// which shall be available in C++20.
+#if __cplusplus >= 202000L
+#error "When upgrading to C++20, remove this error and file a bug to remove this workaround."
+#endif
+
+inline bool StartsWith(std::string_view sv, std::string_view prefix) {
+  return sv.substr(0u, prefix.size()) == prefix;
+}
+
+inline bool EndsWith(std::string_view sv, std::string_view suffix) {
+  return sv.size() >= suffix.size() && sv.substr(sv.size() - suffix.size()) == suffix;
+}
+
+}  // namespace art
+
+#endif  // ART_LIBARTBASE_BASE_STRING_VIEW_CPP20_H_
diff --git a/libartbase/base/systrace.h b/libartbase/base/systrace.h
index d995dce..30bba49 100644
--- a/libartbase/base/systrace.h
+++ b/libartbase/base/systrace.h
@@ -17,33 +17,52 @@
 #ifndef ART_LIBARTBASE_BASE_SYSTRACE_H_
 #define ART_LIBARTBASE_BASE_SYSTRACE_H_
 
-#define ATRACE_TAG ATRACE_TAG_DALVIK
-#include <cutils/trace.h>
-
 #include <sstream>
 #include <string>
 
 #include "android-base/stringprintf.h"
 #include "macros.h"
+#include "palette/palette.h"
 
 namespace art {
 
+inline bool ATraceEnabled() {
+  int enabled = 0;
+  if (UNLIKELY(PaletteTraceEnabled(&enabled) == PaletteStatus::kOkay && enabled != 0)) {
+    return true;
+  } else {
+    return false;
+  }
+}
+
+inline void ATraceBegin(const char* name) {
+  PaletteTraceBegin(name);
+}
+
+inline void ATraceEnd() {
+  PaletteTraceEnd();
+}
+
+inline void ATraceIntegerValue(const char* name, int32_t value) {
+  PaletteTraceIntegerValue(name, value);
+}
+
 class ScopedTrace {
  public:
   explicit ScopedTrace(const char* name) {
-    ATRACE_BEGIN(name);
+    ATraceBegin(name);
   }
   template <typename Fn>
   explicit ScopedTrace(Fn fn) {
-    if (ATRACE_ENABLED()) {
-      ATRACE_BEGIN(fn().c_str());
+    if (UNLIKELY(ATraceEnabled())) {
+      ATraceBegin(fn().c_str());
     }
   }
 
   explicit ScopedTrace(const std::string& name) : ScopedTrace(name.c_str()) {}
 
   ~ScopedTrace() {
-    ATRACE_END();
+    ATraceEnd();
   }
 };
 
@@ -54,7 +73,7 @@
   }
 
   ~ScopedTraceNoStart() {
-    ATRACE_END();
+    ATraceEnd();
   }
 
   // Message helper for the macro. Do not use directly.
@@ -63,7 +82,7 @@
     ScopedTraceMessageHelper() {
     }
     ~ScopedTraceMessageHelper() {
-      ATRACE_BEGIN(buffer_.str().c_str());
+      ATraceBegin(buffer_.str().c_str());
     }
 
     std::ostream& stream() {
@@ -77,7 +96,7 @@
 
 #define SCOPED_TRACE \
   ::art::ScopedTraceNoStart APPEND_TOKENS_AFTER_EVAL(trace, __LINE__) ; \
-  (ATRACE_ENABLED()) && ::art::ScopedTraceNoStart::ScopedTraceMessageHelper().stream()
+  (ATraceEnabled()) && ::art::ScopedTraceNoStart::ScopedTraceMessageHelper().stream()
 
 }  // namespace art
 
diff --git a/libartbase/base/utils.cc b/libartbase/base/utils.cc
index b989d9e..30423a4 100644
--- a/libartbase/base/utils.cc
+++ b/libartbase/base/utils.cc
@@ -190,45 +190,6 @@
 #endif
 }
 
-static void ParseStringAfterChar(const std::string& s,
-                                 char c,
-                                 std::string* parsed_value,
-                                 UsageFn Usage) {
-  std::string::size_type colon = s.find(c);
-  if (colon == std::string::npos) {
-    Usage("Missing char %c in option %s\n", c, s.c_str());
-  }
-  // Add one to remove the char we were trimming until.
-  *parsed_value = s.substr(colon + 1);
-}
-
-void ParseDouble(const std::string& option,
-                 char after_char,
-                 double min,
-                 double max,
-                 double* parsed_value,
-                 UsageFn Usage) {
-  std::string substring;
-  ParseStringAfterChar(option, after_char, &substring, Usage);
-  bool sane_val = true;
-  double value;
-  if ((false)) {
-    // TODO: this doesn't seem to work on the emulator.  b/15114595
-    std::stringstream iss(substring);
-    iss >> value;
-    // Ensure that we have a value, there was no cruft after it and it satisfies a sensible range.
-    sane_val = iss.eof() && (value >= min) && (value <= max);
-  } else {
-    char* end = nullptr;
-    value = strtod(substring.c_str(), &end);
-    sane_val = *end == '\0' && value >= min && value <= max;
-  }
-  if (!sane_val) {
-    Usage("Invalid double value %s for option %s\n", substring.c_str(), option.c_str());
-  }
-  *parsed_value = value;
-}
-
 void SleepForever() {
   while (true) {
     usleep(1000000);
diff --git a/libartbase/base/utils.h b/libartbase/base/utils.h
index 11472a8..9284950 100644
--- a/libartbase/base/utils.h
+++ b/libartbase/base/utils.h
@@ -30,7 +30,6 @@
 #include "enums.h"
 #include "globals.h"
 #include "macros.h"
-#include "stringpiece.h"
 
 namespace art {
 
@@ -91,44 +90,6 @@
   return reinterpret_cast<const void*>(code);
 }
 
-using UsageFn = void (*)(const char*, ...);
-
-template <typename T>
-static void ParseIntOption(const StringPiece& option,
-                            const std::string& option_name,
-                            T* out,
-                            UsageFn usage,
-                            bool is_long_option = true) {
-  std::string option_prefix = option_name + (is_long_option ? "=" : "");
-  DCHECK(option.starts_with(option_prefix)) << option << " " << option_prefix;
-  const char* value_string = option.substr(option_prefix.size()).data();
-  int64_t parsed_integer_value = 0;
-  if (!android::base::ParseInt(value_string, &parsed_integer_value)) {
-    usage("Failed to parse %s '%s' as an integer", option_name.c_str(), value_string);
-  }
-  *out = dchecked_integral_cast<T>(parsed_integer_value);
-}
-
-template <typename T>
-static void ParseUintOption(const StringPiece& option,
-                            const std::string& option_name,
-                            T* out,
-                            UsageFn usage,
-                            bool is_long_option = true) {
-  ParseIntOption(option, option_name, out, usage, is_long_option);
-  if (*out < 0) {
-    usage("%s passed a negative value %d", option_name.c_str(), *out);
-    *out = 0;
-  }
-}
-
-void ParseDouble(const std::string& option,
-                 char after_char,
-                 double min,
-                 double max,
-                 double* parsed_value,
-                 UsageFn Usage);
-
 #if defined(__BIONIC__)
 struct Arc4RandomGenerator {
   typedef uint32_t result_type;
diff --git a/libartpalette/Android.bp b/libartpalette/Android.bp
new file mode 100644
index 0000000..778109d
--- /dev/null
+++ b/libartpalette/Android.bp
@@ -0,0 +1,116 @@
+//
+// Copyright (C) 2019 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.
+//
+
+cc_defaults {
+  name: "libartpalette_defaults",
+  defaults: ["art_defaults"],
+  host_supported: true,
+  export_include_dirs: ["include"],
+}
+
+// libartpalette-system is the implementation of the abstraction layer. It is
+// only available as a shared library on Android.
+art_cc_library {
+    name: "libartpalette-system",
+    defaults: ["libartpalette_defaults"],
+
+    target: {
+        android: {
+          srcs: ["system/palette_android.cc",],
+          header_libs: ["libbase_headers"],
+          shared_libs: [
+            "libcutils",
+            "liblog",
+            "libprocessgroup",
+          ],
+        },
+        host: {
+          header_libs: ["libbase_headers"],
+          srcs: ["system/palette_fake.cc",],
+        },
+        darwin: {
+            enabled: false,
+        },
+        windows: {
+            enabled: false,
+        },
+    },
+    static: {
+        enabled: false,
+    },
+    version_script: "libartpalette.map.txt",
+}
+
+// libartpalette is the dynamic loader of the platform abstraction
+// layer. It is only used on Android. For other targets, it just
+// implements a fake platform implementation.
+art_cc_library {
+    name: "libartpalette",
+    defaults: ["libartpalette_defaults"],
+    required: ["libartpalette-system"],  // libartpalette.so dlopen()'s libartpalette-system.
+    header_libs: ["libbase_headers"],
+    target: {
+        // Targets supporting dlopen build the client library which loads
+        // and binds the methods in the libartpalette-system library.
+        android: {
+            srcs: ["apex/palette.cc"],
+            shared: {
+                shared_libs: ["liblog"],
+            },
+            static: {
+                static_libs: ["liblog"],
+            },
+            version_script: "libartpalette.map.txt",
+        },
+        linux_bionic: {
+          header_libs: ["libbase_headers"],
+            srcs: ["system/palette_fake.cc"],
+            shared: {
+                shared_libs: ["liblog"],
+            },
+            version_script: "libartpalette.map.txt",
+        },
+        linux_glibc: {
+          header_libs: ["libbase_headers"],
+            srcs: ["system/palette_fake.cc"],
+            shared: {
+                shared_libs: ["liblog"],
+            },
+            version_script: "libartpalette.map.txt",
+        },
+        // Targets without support for dlopen just use the sources for
+        // the system library which actually implements functionality.
+        darwin: {
+            enabled: true,
+            header_libs: ["libbase_headers"],
+            srcs: ["system/palette_fake.cc"],
+        },
+        windows: {
+            enabled: true,
+            header_libs: ["libbase_headers"],
+            srcs: ["system/palette_fake.cc"],
+        },
+    }
+}
+
+art_cc_test {
+    name: "art_libartpalette_tests",
+    defaults: ["art_gtest_defaults"],
+    host_supported: true,
+    srcs: ["apex/palette_test.cc"],
+    shared_libs: ["libartpalette"],
+    test_per_src: true,
+}
diff --git a/libartpalette/apex/palette.cc b/libartpalette/apex/palette.cc
new file mode 100644
index 0000000..0b391f8
--- /dev/null
+++ b/libartpalette/apex/palette.cc
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "palette/palette.h"
+
+#include <dlfcn.h>
+#include <stdlib.h>
+
+#include <android/log.h>
+#include <android-base/macros.h>
+
+namespace {
+
+// Logging tag.
+static constexpr const char* kLogTag = "libartpalette";
+
+// Name of the palette library present in the /system partition.
+static constexpr const char* kPaletteSystemLibrary = "libartpalette-system.so";
+
+// Generic method used when a dynamically loaded palette instance does not
+// support a method.
+enum PaletteStatus PaletteMethodNotSupported() {
+  return PaletteStatus::kNotSupported;
+}
+
+// Declare type aliases for pointers to each function in the interface.
+#define PALETTE_METHOD_TYPE_ALIAS(Name, ...) \
+  using Name ## Method = PaletteStatus(*)(__VA_ARGS__);
+PALETTE_METHOD_LIST(PALETTE_METHOD_TYPE_ALIAS)
+#undef PALETTE_METHOD_TYPE_ALIAS
+
+// Singleton class responsible for dynamically loading the palette library and
+// binding functions there to method pointers.
+class PaletteLoader {
+ public:
+  static PaletteLoader& Instance() {
+    static PaletteLoader instance;
+    return instance;
+  }
+
+  // Accessor methods to get instances of palette methods.
+#define PALETTE_LOADER_METHOD_ACCESSOR(Name, ...)                       \
+  Name ## Method Get ## Name ## Method() const { return Name ## Method ## _; }
+PALETTE_METHOD_LIST(PALETTE_LOADER_METHOD_ACCESSOR)
+#undef PALETTE_LOADER_METHOD_ACCESSOR
+
+ private:
+  PaletteLoader();
+
+  static void* OpenLibrary();
+  static void* GetMethod(void* palette_lib, const char* name);
+
+  // Handle to the palette library from dlopen().
+  void* palette_lib_;
+
+  // Fields to store pointers to palette methods.
+#define PALETTE_LOADER_METHOD_FIELD(Name, ...) \
+  const Name ## Method Name ## Method ## _;
+  PALETTE_METHOD_LIST(PALETTE_LOADER_METHOD_FIELD)
+#undef PALETTE_LOADER_METHOD_FIELD
+
+  DISALLOW_COPY_AND_ASSIGN(PaletteLoader);
+};
+
+void* PaletteLoader::OpenLibrary() {
+  void* handle = dlopen(kPaletteSystemLibrary, RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE);
+  if (handle == nullptr) {
+    // dlerror message includes details of error and file being opened.
+    __android_log_assert(nullptr, kLogTag, "%s", dlerror());
+  }
+  return handle;
+}
+
+void* PaletteLoader::GetMethod(void* palette_lib, const char* name) {
+  void* method = nullptr;
+  if (palette_lib != nullptr) {
+    method = dlsym(palette_lib, name);
+  }
+  if (method == nullptr) {
+    return reinterpret_cast<void*>(PaletteMethodNotSupported);
+  }
+  // TODO(oth): consider new GetMethodSignature() in the Palette API which
+  // would allow sanity checking the type signatures.
+  return method;
+}
+
+PaletteLoader::PaletteLoader() :
+    palette_lib_(OpenLibrary())
+#define PALETTE_LOADER_BIND_METHOD(Name, ...)                           \
+    , Name ## Method ## _(reinterpret_cast<Name ## Method>(GetMethod(palette_lib_, #Name)))
+    PALETTE_METHOD_LIST(PALETTE_LOADER_BIND_METHOD)
+#undef PALETTE_LOADER_BIND_METHOD
+{
+}
+
+}  // namespace
+
+extern "C" {
+
+enum PaletteStatus PaletteGetVersion(/*out*/int32_t* version) {
+  PaletteGetVersionMethod m = PaletteLoader::Instance().GetPaletteGetVersionMethod();
+  return m(version);
+}
+
+enum PaletteStatus PaletteSchedSetPriority(int32_t tid, int32_t java_priority) {
+  PaletteSchedSetPriorityMethod m = PaletteLoader::Instance().GetPaletteSchedSetPriorityMethod();
+  return m(tid, java_priority);
+}
+
+enum PaletteStatus PaletteSchedGetPriority(int32_t tid, /*out*/int32_t* java_priority) {
+  PaletteSchedGetPriorityMethod m = PaletteLoader::Instance().GetPaletteSchedGetPriorityMethod();
+  return m(tid, java_priority);
+}
+
+enum PaletteStatus PaletteTraceEnabled(/*out*/int32_t* enabled) {
+  PaletteTraceEnabledMethod m = PaletteLoader::Instance().GetPaletteTraceEnabledMethod();
+  return m(enabled);
+}
+
+enum PaletteStatus PaletteTraceBegin(/*in*/const char* name) {
+  PaletteTraceBeginMethod m = PaletteLoader::Instance().GetPaletteTraceBeginMethod();
+  return m(name);
+}
+
+enum PaletteStatus PaletteTraceEnd() {
+  PaletteTraceEndMethod m = PaletteLoader::Instance().GetPaletteTraceEndMethod();
+  return m();
+}
+
+enum PaletteStatus PaletteTraceIntegerValue(/*in*/const char* name, int32_t value) {
+  PaletteTraceIntegerValueMethod m = PaletteLoader::Instance().GetPaletteTraceIntegerValueMethod();
+  return m(name, value);
+}
+
+}  // extern "C"
diff --git a/libartpalette/apex/palette_test.cc b/libartpalette/apex/palette_test.cc
new file mode 100644
index 0000000..8bbe0ee
--- /dev/null
+++ b/libartpalette/apex/palette_test.cc
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "palette/palette.h"
+
+#include <unistd.h>
+#include <sys/syscall.h>
+
+#include "gtest/gtest.h"
+
+namespace {
+
+pid_t GetTid() {
+#ifdef __BIONIC__
+  return gettid();
+#else  // __BIONIC__
+  return syscall(__NR_gettid);
+#endif  // __BIONIC__
+}
+
+}  // namespace
+
+class PaletteClientTest : public testing::Test {};
+
+TEST_F(PaletteClientTest, GetVersion) {
+  int32_t version = -1;
+  PaletteStatus status = PaletteGetVersion(&version);
+  ASSERT_EQ(PaletteStatus::kOkay, status);
+  ASSERT_GE(version, 1);
+}
+
+TEST_F(PaletteClientTest, SchedPriority) {
+  int32_t tid = GetTid();
+  int32_t saved_priority;
+  EXPECT_EQ(PaletteStatus::kOkay, PaletteSchedGetPriority(tid, &saved_priority));
+
+  EXPECT_EQ(PaletteStatus::kInvalidArgument, PaletteSchedSetPriority(tid, /*java_priority=*/ 0));
+  EXPECT_EQ(PaletteStatus::kInvalidArgument, PaletteSchedSetPriority(tid, /*java_priority=*/ -1));
+  EXPECT_EQ(PaletteStatus::kInvalidArgument, PaletteSchedSetPriority(tid, /*java_priority=*/ 11));
+
+  EXPECT_EQ(PaletteStatus::kOkay, PaletteSchedSetPriority(tid, /*java_priority=*/ 1));
+  EXPECT_EQ(PaletteStatus::kOkay, PaletteSchedSetPriority(tid, saved_priority));
+}
+
+TEST_F(PaletteClientTest, Trace) {
+  int32_t enabled;
+  EXPECT_EQ(PaletteStatus::kOkay, PaletteTraceEnabled(&enabled));
+  EXPECT_EQ(PaletteStatus::kOkay, PaletteTraceBegin("Hello world!"));
+  EXPECT_EQ(PaletteStatus::kOkay, PaletteTraceEnd());
+  EXPECT_EQ(PaletteStatus::kOkay, PaletteTraceIntegerValue("Beans", /*value=*/ 3));
+}
diff --git a/libartpalette/include/palette/palette.h b/libartpalette/include/palette/palette.h
new file mode 100644
index 0000000..1f58403
--- /dev/null
+++ b/libartpalette/include/palette/palette.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef ART_LIBARTPALETTE_INCLUDE_PALETTE_PALETTE_H_
+#define ART_LIBARTPALETTE_INCLUDE_PALETTE_PALETTE_H_
+
+#include "palette_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif  // __cplusplus
+
+// Palette method signatures are defined in palette_method_list.h.
+
+#define PALETTE_METHOD_DECLARATION(Name, ...) \
+  enum PaletteStatus Name(__VA_ARGS__);
+#include "palette_method_list.h"
+PALETTE_METHOD_LIST(PALETTE_METHOD_DECLARATION)
+#undef PALETTE_METHOD_DECLARATION
+
+#ifdef __cplusplus
+}
+#endif  // __cplusplus
+
+#endif  // ART_LIBARTPALETTE_INCLUDE_PALETTE_PALETTE_H_
diff --git a/libartpalette/include/palette/palette_method_list.h b/libartpalette/include/palette/palette_method_list.h
new file mode 100644
index 0000000..dc4ec52
--- /dev/null
+++ b/libartpalette/include/palette/palette_method_list.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef ART_LIBARTPALETTE_INCLUDE_PALETTE_PALETTE_METHOD_LIST_H_
+#define ART_LIBARTPALETTE_INCLUDE_PALETTE_PALETTE_METHOD_LIST_H_
+
+#include <stdint.h>
+
+// Methods in version 1 API
+#define PALETTE_METHOD_LIST(M)                                              \
+  M(PaletteGetVersion, /*out*/int32_t* version)                             \
+  M(PaletteSchedSetPriority, int32_t tid, int32_t java_priority)            \
+  M(PaletteSchedGetPriority, int32_t tid, /*out*/int32_t* java_priority)    \
+  M(PaletteTraceEnabled, /*out*/int32_t* enabled)                           \
+  M(PaletteTraceBegin, const char* name)                                    \
+  M(PaletteTraceEnd)                                                        \
+  M(PaletteTraceIntegerValue, const char* name, int32_t value)
+
+#endif  // ART_LIBARTPALETTE_INCLUDE_PALETTE_PALETTE_METHOD_LIST_H_
diff --git a/libartpalette/include/palette/palette_types.h b/libartpalette/include/palette/palette_types.h
new file mode 100644
index 0000000..837086e
--- /dev/null
+++ b/libartpalette/include/palette/palette_types.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef ART_LIBARTPALETTE_INCLUDE_PALETTE_PALETTE_TYPES_H_
+#define ART_LIBARTPALETTE_INCLUDE_PALETTE_PALETTE_TYPES_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif  // __cplusplus
+
+// Return values for palette functions.
+enum PaletteStatus {
+  kOkay = 0,
+  kCheckErrno = 1,
+  kInvalidArgument = 2,
+  kNotSupported = 3,
+};
+
+#ifdef __cplusplus
+}
+#endif  // __cplusplus
+
+#endif  // ART_LIBARTPALETTE_INCLUDE_PALETTE_PALETTE_TYPES_H_
diff --git a/libartpalette/libartpalette.map.txt b/libartpalette/libartpalette.map.txt
new file mode 100644
index 0000000..0920835
--- /dev/null
+++ b/libartpalette/libartpalette.map.txt
@@ -0,0 +1,30 @@
+#
+# Copyright (C) 2019 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.
+#
+
+LIBARTPALETTE_1 {
+  global:
+    # --- VERSION 01 API ---
+    PaletteGetVersion;
+    PaletteSchedSetPriority;
+    PaletteSchedGetPriority;
+    PaletteTraceEnabled;
+    PaletteTraceBegin;
+    PaletteTraceEnd;
+    PaletteTraceIntegerValue;
+
+  local:
+    *;
+};
diff --git a/libartpalette/system/palette_android.cc b/libartpalette/system/palette_android.cc
new file mode 100644
index 0000000..aed3862
--- /dev/null
+++ b/libartpalette/system/palette_android.cc
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_DALVIK
+
+#include "palette/palette.h"
+
+#include <errno.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include <mutex>
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <cutils/sched_policy.h>
+#include <cutils/trace.h>
+#include <log/event_tag_map.h>
+#include <utils/Thread.h>
+
+#include "palette_system.h"
+
+enum PaletteStatus PaletteGetVersion(int32_t* version) {
+  *version = art::palette::kPaletteVersion;
+  return PaletteStatus::kOkay;
+}
+
+// Conversion map for "nice" values.
+//
+// We use Android thread priority constants to be consistent with the rest
+// of the system.  In some cases adjacent entries may overlap.
+//
+static const int kNiceValues[art::palette::kNumManagedThreadPriorities] = {
+  ANDROID_PRIORITY_LOWEST,                // 1 (MIN_PRIORITY)
+  ANDROID_PRIORITY_BACKGROUND + 6,
+  ANDROID_PRIORITY_BACKGROUND + 3,
+  ANDROID_PRIORITY_BACKGROUND,
+  ANDROID_PRIORITY_NORMAL,                // 5 (NORM_PRIORITY)
+  ANDROID_PRIORITY_NORMAL - 2,
+  ANDROID_PRIORITY_NORMAL - 4,
+  ANDROID_PRIORITY_URGENT_DISPLAY + 3,
+  ANDROID_PRIORITY_URGENT_DISPLAY + 2,
+  ANDROID_PRIORITY_URGENT_DISPLAY         // 10 (MAX_PRIORITY)
+};
+
+enum PaletteStatus PaletteSchedSetPriority(int32_t tid, int32_t managed_priority) {
+  if (managed_priority < art::palette::kMinManagedThreadPriority ||
+      managed_priority > art::palette::kMaxManagedThreadPriority) {
+    return PaletteStatus::kInvalidArgument;
+  }
+  int new_nice = kNiceValues[managed_priority - art::palette::kMinManagedThreadPriority];
+
+  // TODO: b/18249098 The code below is broken. It uses getpriority() as a proxy for whether a
+  // thread is already in the SP_FOREGROUND cgroup. This is not necessarily true for background
+  // processes, where all threads are in the SP_BACKGROUND cgroup. This means that callers will
+  // have to call setPriority twice to do what they want :
+  //
+  //     Thread.setPriority(Thread.MIN_PRIORITY);  // no-op wrt to cgroups
+  //     Thread.setPriority(Thread.MAX_PRIORITY);  // will actually change cgroups.
+  if (new_nice >= ANDROID_PRIORITY_BACKGROUND) {
+    set_sched_policy(tid, SP_BACKGROUND);
+  } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) {
+    set_sched_policy(tid, SP_FOREGROUND);
+  }
+
+  if (setpriority(PRIO_PROCESS, tid, new_nice) != 0) {
+    return PaletteStatus::kCheckErrno;
+  }
+  return PaletteStatus::kOkay;
+}
+
+enum PaletteStatus PaletteSchedGetPriority(int32_t tid, /*out*/int32_t* managed_priority) {
+  errno = 0;
+  int native_priority = getpriority(PRIO_PROCESS, tid);
+  if (native_priority == -1 && errno != 0) {
+    *managed_priority = art::palette::kNormalManagedThreadPriority;
+    return PaletteStatus::kCheckErrno;
+  }
+
+  for (int p = art::palette::kMinManagedThreadPriority;
+       p <= art::palette::kMaxManagedThreadPriority;
+       p = p + 1) {
+    int index = p - art::palette::kMinManagedThreadPriority;
+    if (native_priority >= kNiceValues[index]) {
+      *managed_priority = p;
+      return PaletteStatus::kOkay;
+    }
+  }
+  *managed_priority = art::palette::kMaxManagedThreadPriority;
+  return PaletteStatus::kOkay;
+}
+
+enum PaletteStatus PaletteTraceEnabled(/*out*/int32_t* enabled) {
+  *enabled = (ATRACE_ENABLED() != 0) ? 1 : 0;
+  return PaletteStatus::kOkay;
+}
+
+enum PaletteStatus PaletteTraceBegin(const char* name) {
+  ATRACE_BEGIN(name);
+  return PaletteStatus::kOkay;
+}
+
+enum PaletteStatus PaletteTraceEnd() {
+  ATRACE_END();
+  return PaletteStatus::kOkay;
+}
+
+enum PaletteStatus PaletteTraceIntegerValue(const char* name, int32_t value) {
+  ATRACE_INT(name, value);
+  return PaletteStatus::kOkay;
+}
diff --git a/libartpalette/system/palette_fake.cc b/libartpalette/system/palette_fake.cc
new file mode 100644
index 0000000..0961e77
--- /dev/null
+++ b/libartpalette/system/palette_fake.cc
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "palette/palette.h"
+
+#include <map>
+#include <mutex>
+
+#include <android-base/macros.h>  // For ATTRIBUTE_UNUSED
+
+#include "palette_system.h"
+
+enum PaletteStatus PaletteGetVersion(int32_t* version) {
+  *version = art::palette::kPaletteVersion;
+  return PaletteStatus::kOkay;
+}
+
+// Cached thread priority for testing. No thread priorities are ever affected.
+static std::mutex g_tid_priority_map_mutex;
+static std::map<int32_t, int32_t> g_tid_priority_map;
+
+enum PaletteStatus PaletteSchedSetPriority(int32_t tid, int32_t priority) {
+  if (priority < art::palette::kMinManagedThreadPriority ||
+      priority > art::palette::kMaxManagedThreadPriority) {
+    return PaletteStatus::kInvalidArgument;
+  }
+  std::lock_guard guard(g_tid_priority_map_mutex);
+  g_tid_priority_map[tid] = priority;
+  return PaletteStatus::kOkay;
+}
+
+enum PaletteStatus PaletteSchedGetPriority(int32_t tid,
+                                           /*out*/int32_t* priority) {
+  std::lock_guard guard(g_tid_priority_map_mutex);
+  if (g_tid_priority_map.find(tid) == g_tid_priority_map.end()) {
+    g_tid_priority_map[tid] = art::palette::kNormalManagedThreadPriority;
+  }
+  *priority = g_tid_priority_map[tid];
+  return PaletteStatus::kOkay;
+}
+
+enum PaletteStatus PaletteTraceEnabled(/*out*/int32_t* enabled) {
+  *enabled = 0;
+  return PaletteStatus::kOkay;
+}
+
+enum PaletteStatus PaletteTraceBegin(const char* name ATTRIBUTE_UNUSED) {
+  return PaletteStatus::kOkay;
+}
+
+enum PaletteStatus PaletteTraceEnd() {
+  return PaletteStatus::kOkay;
+}
+
+enum PaletteStatus PaletteTraceIntegerValue(const char* name ATTRIBUTE_UNUSED,
+                                            int32_t value ATTRIBUTE_UNUSED) {
+  return PaletteStatus::kOkay;
+}
diff --git a/libartpalette/system/palette_system.h b/libartpalette/system/palette_system.h
new file mode 100644
index 0000000..b28e00d
--- /dev/null
+++ b/libartpalette/system/palette_system.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef ART_LIBARTPALETTE_SYSTEM_PALETTE_SYSTEM_H_
+#define ART_LIBARTPALETTE_SYSTEM_PALETTE_SYSTEM_H_
+
+#include <stdint.h>
+
+namespace art {
+namespace palette {
+
+static constexpr int32_t kPaletteVersion = 1;
+
+// Managed thread definitions
+static constexpr int32_t kNormalManagedThreadPriority = 5;
+static constexpr int32_t kMinManagedThreadPriority = 1;
+static constexpr int32_t kMaxManagedThreadPriority = 10;
+static constexpr int32_t kNumManagedThreadPriorities =
+    kMaxManagedThreadPriority - kMinManagedThreadPriority + 1;
+
+}  // namespace palette
+}  // namespace art
+
+#endif  // ART_LIBARTPALETTE_SYSTEM_PALETTE_SYSTEM_H_
diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp
index 428c2c8..f83f18c 100644
--- a/libdexfile/Android.bp
+++ b/libdexfile/Android.bp
@@ -47,9 +47,8 @@
             shared_libs: [
                  // For MemMap.
                  "libartbase",
+                 "libartpalette",
                  "liblog",
-                 // For atrace.
-                 "libcutils",
                  // For common macros.
                  "libbase",
             ],
@@ -64,9 +63,8 @@
                 "libz",
                  // For MemMap.
                  "libartbase",
+                 "libartpalette",
                  "liblog",
-                 // For atrace.
-                 "libcutils",
                  // For common macros.
                  "libbase",
             ],
@@ -81,9 +79,8 @@
                 "libz",
                  // For MemMap.
                  "libartbase",
+                 "libartpalette",
                  "liblog",
-                 // For atrace.
-                 "libcutils",
                  // For common macros.
                  "libbase",
             ],
@@ -102,7 +99,6 @@
     name: "libdexfile_static_base_defaults",
     static_libs: [
         "libbase",
-        "libcutils",
         "liblog",
         "libz",
         "libziparchive",
@@ -232,9 +228,28 @@
         "libdexfile",
     ],
 
-    stubs: {
-        symbol_file: "external/libdexfile_external.map.txt",
-        versions: ["1"],
+    // TODO(b/120670568): Enable this when linking bug is fixed.
+    // stubs: {
+    //     symbol_file: "external/libdexfile_external.map.txt",
+    //     versions: ["1"],
+    // },
+
+    // Hide symbols using version scripts for targets that support it, i.e. all
+    // but Darwin.
+    // TODO(b/120670568): Clean this up when stubs above is enabled.
+    target: {
+        android: {
+            version_script: "external/libdexfile_external.map.txt",
+        },
+        linux_bionic: {
+            version_script: "external/libdexfile_external.map.txt",
+        },
+        linux_glibc: {
+            version_script: "external/libdexfile_external.map.txt",
+        },
+        windows: {
+            version_script: "external/libdexfile_external.map.txt",
+        },
     },
 }
 
diff --git a/libdexfile/dex/dex_file-inl.h b/libdexfile/dex/dex_file-inl.h
index 2af1e04..15ba9cc 100644
--- a/libdexfile/dex/dex_file-inl.h
+++ b/libdexfile/dex/dex_file-inl.h
@@ -22,7 +22,6 @@
 #include "base/casts.h"
 #include "base/iteration_range.h"
 #include "base/leb128.h"
-#include "base/stringpiece.h"
 #include "base/utils.h"
 #include "class_iterator.h"
 #include "compact_dex_file.h"
diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc
index 39377a3..7db4de0 100644
--- a/libdexfile/dex/dex_file.cc
+++ b/libdexfile/dex/dex_file.cc
@@ -400,7 +400,7 @@
 }
 
 // Given a signature place the type ids into the given vector
-bool DexFile::CreateTypeList(const StringPiece& signature,
+bool DexFile::CreateTypeList(std::string_view signature,
                              dex::TypeIndex* return_type_idx,
                              std::vector<dex::TypeIndex>* param_type_idxs) const {
   if (signature[0] != '(') {
@@ -450,20 +450,6 @@
   return false;  // failed to correctly parse return type
 }
 
-const Signature DexFile::CreateSignature(const StringPiece& signature) const {
-  dex::TypeIndex return_type_idx;
-  std::vector<dex::TypeIndex> param_type_indices;
-  bool success = CreateTypeList(signature, &return_type_idx, &param_type_indices);
-  if (!success) {
-    return Signature::NoSignature();
-  }
-  const ProtoId* proto_id = FindProtoId(return_type_idx, param_type_indices);
-  if (proto_id == nullptr) {
-    return Signature::NoSignature();
-  }
-  return Signature(this, *proto_id);
-}
-
 int32_t DexFile::FindTryItem(const TryItem* try_items, uint32_t tries_size, uint32_t address) {
   uint32_t min = 0;
   uint32_t max = tries_size;
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index c7fbe78..4dae1c0 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -19,6 +19,7 @@
 
 #include <memory>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include <android-base/logging.h>
@@ -44,7 +45,6 @@
 class MemMap;
 class OatDexFile;
 class StandardDexFile;
-class StringPiece;
 class ZipArchive;
 
 // Some instances of DexFile own the storage referred to by DexFile.  Clients who create
@@ -479,14 +479,10 @@
   }
 
   // Given a signature place the type ids into the given vector, returns true on success
-  bool CreateTypeList(const StringPiece& signature,
+  bool CreateTypeList(std::string_view signature,
                       dex::TypeIndex* return_type_idx,
                       std::vector<dex::TypeIndex>* param_type_idxs) const;
 
-  // Create a Signature from the given string signature or return Signature::NoSignature if not
-  // possible.
-  const Signature CreateSignature(const StringPiece& signature) const;
-
   // Returns the short form method descriptor for the given prototype.
   const char* GetShorty(dex::ProtoIndex proto_idx) const;
 
@@ -618,6 +614,10 @@
     return hiddenapi_class_data_;
   }
 
+  ALWAYS_INLINE bool HasHiddenapiClassData() const {
+    return hiddenapi_class_data_ != nullptr;
+  }
+
   const dex::AnnotationItem* GetAnnotationItem(const dex::AnnotationSetItem* set_item,
                                                uint32_t index) const {
     DCHECK_LE(index, set_item->size_);
diff --git a/libdexfile/dex/signature-inl.h b/libdexfile/dex/signature-inl.h
index ccc7ea9..12ad1b3 100644
--- a/libdexfile/dex/signature-inl.h
+++ b/libdexfile/dex/signature-inl.h
@@ -19,7 +19,6 @@
 
 #include "signature.h"
 
-#include "base/stringpiece.h"
 #include "dex_file-inl.h"
 
 namespace art {
@@ -37,13 +36,13 @@
   uint32_t lhs_shorty_len;  // For a shorty utf16 length == mutf8 length.
   const char* lhs_shorty_data = dex_file_->StringDataAndUtf16LengthByIdx(proto_id_->shorty_idx_,
                                                                          &lhs_shorty_len);
-  StringPiece lhs_shorty(lhs_shorty_data, lhs_shorty_len);
+  std::string_view lhs_shorty(lhs_shorty_data, lhs_shorty_len);
   {
     uint32_t rhs_shorty_len;
     const char* rhs_shorty_data =
         rhs.dex_file_->StringDataAndUtf16LengthByIdx(rhs.proto_id_->shorty_idx_,
                                                      &rhs_shorty_len);
-    StringPiece rhs_shorty(rhs_shorty_data, rhs_shorty_len);
+    std::string_view rhs_shorty(rhs_shorty_data, rhs_shorty_len);
     if (lhs_shorty != rhs_shorty) {
       return false;  // Shorty mismatch.
     }
@@ -57,7 +56,7 @@
       return false;  // Return type mismatch.
     }
   }
-  if (lhs_shorty.find('L', 1) != StringPiece::npos) {
+  if (lhs_shorty.find('L', 1) != std::string_view::npos) {
     const dex::TypeList* params = dex_file_->GetProtoParameters(*proto_id_);
     const dex::TypeList* rhs_params = rhs.dex_file_->GetProtoParameters(*rhs.proto_id_);
     // We found a reference parameter in the matching shorty, so both lists must be non-empty.
diff --git a/libdexfile/dex/signature.cc b/libdexfile/dex/signature.cc
index 34b4b55..ac00428 100644
--- a/libdexfile/dex/signature.cc
+++ b/libdexfile/dex/signature.cc
@@ -21,6 +21,8 @@
 #include <ostream>
 #include <type_traits>
 
+#include "base/string_view_cpp20.h"
+
 namespace art {
 
 using dex::TypeList;
@@ -55,26 +57,26 @@
   return strcmp(return_type, "V") == 0;
 }
 
-bool Signature::operator==(const StringPiece& rhs) const {
+bool Signature::operator==(std::string_view rhs) const {
   if (dex_file_ == nullptr) {
     return false;
   }
-  StringPiece tail(rhs);
-  if (!tail.starts_with("(")) {
+  std::string_view tail(rhs);
+  if (!StartsWith(tail, "(")) {
     return false;  // Invalid signature
   }
   tail.remove_prefix(1);  // "(";
   const TypeList* params = dex_file_->GetProtoParameters(*proto_id_);
   if (params != nullptr) {
     for (uint32_t i = 0; i < params->Size(); ++i) {
-      StringPiece param(dex_file_->StringByTypeIdx(params->GetTypeItem(i).type_idx_));
-      if (!tail.starts_with(param)) {
+      std::string_view param(dex_file_->StringByTypeIdx(params->GetTypeItem(i).type_idx_));
+      if (!StartsWith(tail, param)) {
         return false;
       }
       tail.remove_prefix(param.length());
     }
   }
-  if (!tail.starts_with(")")) {
+  if (!StartsWith(tail, ")")) {
     return false;
   }
   tail.remove_prefix(1);  // ")";
diff --git a/libdexfile/dex/signature.h b/libdexfile/dex/signature.h
index 235f37c..3fbb543 100644
--- a/libdexfile/dex/signature.h
+++ b/libdexfile/dex/signature.h
@@ -19,6 +19,7 @@
 
 #include <iosfwd>
 #include <string>
+#include <string_view>
 
 #include <android-base/logging.h>
 
@@ -30,7 +31,6 @@
 struct ProtoId;
 }  // namespace dex
 class DexFile;
-class StringPiece;
 
 // Abstract the signature of a method.
 class Signature : public ValueObject {
@@ -49,7 +49,7 @@
     return !(*this == rhs);
   }
 
-  bool operator==(const StringPiece& rhs) const;
+  bool operator==(std::string_view rhs) const;
 
  private:
   Signature(const DexFile* dex, const dex::ProtoId& proto) : dex_file_(dex), proto_id_(&proto) {
diff --git a/libdexfile/dex/standard_dex_file.h b/libdexfile/dex/standard_dex_file.h
index 838d4e3..48671c9 100644
--- a/libdexfile/dex/standard_dex_file.h
+++ b/libdexfile/dex/standard_dex_file.h
@@ -84,7 +84,10 @@
   uint32_t GetCodeItemSize(const dex::CodeItem& item) const override;
 
   size_t GetDequickenedSize() const override {
-    return Size();
+    // JVMTI will run dex layout on standard dex files that have hidden API data,
+    // in order to remove that data. As dexlayout may increase the size of the dex file,
+    // be (very) conservative and add one MB to the size.
+    return Size() + (HasHiddenapiClassData() ? 1 * MB : 0);
   }
 
  private:
diff --git a/libprofile/Android.bp b/libprofile/Android.bp
index fd32c5f..986adce 100644
--- a/libprofile/Android.bp
+++ b/libprofile/Android.bp
@@ -25,10 +25,8 @@
         android: {
             shared_libs: [
                 "libartbase",
+                "libartpalette",
                 "libdexfile",
-                "libartbase",
-	        // For atrace.
-                "libcutils",
                 "libbase",
             ],
             static_libs: [
@@ -41,10 +39,8 @@
         not_windows: {
             shared_libs: [
                 "libartbase",
+                "libartpalette",
                 "libdexfile",
-                "libartbase",
-	        // For atrace.
-                "libcutils",
                 "libziparchive",
                 "libz",
                 "libbase",
@@ -55,10 +51,8 @@
 	    cflags: ["-Wno-thread-safety"],
             static_libs: [
                 "libartbase",
+                "libartpalette",
                 "libdexfile",
-                "libartbase",
-	        // For atrace.
-                "libcutils",
                 "libziparchive",
                 "libz",
                 "libbase",
@@ -78,7 +72,6 @@
     name: "libprofile_static_base_defaults",
     static_libs: [
         "libbase",
-        "libcutils",
         "libz",
         "libziparchive",
     ],
diff --git a/oatdump/Android.bp b/oatdump/Android.bp
index 0e4d5b3..8849a7a 100644
--- a/oatdump/Android.bp
+++ b/oatdump/Android.bp
@@ -19,11 +19,6 @@
     defaults: ["art_defaults"],
     host_supported: true,
     srcs: ["oatdump.cc"],
-    target: {
-        android: {
-            shared_libs: ["libcutils"],
-        },
-    },
     // b/79417743, oatdump 32-bit tests failed with clang lld
     use_clang_lld: false,
     header_libs: [
@@ -102,8 +97,8 @@
         "libart-disassembler",
         "libvixl",
     ],
-    // TODO(b/122885634): This is necessary for the static lib ordering bug with
-    // APEX stubs.
+    // We need this to resolve libartpalette symbols
+    // correctly. Multiple source libraries depend on it.
     group_static_libs: true,
 }
 
diff --git a/oatdump/Android.mk b/oatdump/Android.mk
index e82cd97..b50aa1c 100644
--- a/oatdump/Android.mk
+++ b/oatdump/Android.mk
@@ -66,7 +66,12 @@
 .PHONY: dump-oat-boot-$(TARGET_ARCH)
 ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
 dump-oat-boot-$(TARGET_ARCH): $(DEFAULT_DEX_PREOPT_BUILT_IMAGE_FILENAME) $(OATDUMP)
-	$(OATDUMP) $(addprefix --image=,$(DEFAULT_DEX_PREOPT_BUILT_IMAGE_LOCATION)) \
+	$(OATDUMP) \
+	  --runtime-arg \
+	  -Xbootclasspath:$(call normalize-path-list, $(DEXPREOPT_BOOTCLASSPATH_DEX_FILES)) \
+	  --runtime-arg \
+	  -Xbootclasspath-locations:$(call normalize-path-list, $(DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS)) \
+	  $(addprefix --image=,$(DEFAULT_DEX_PREOPT_BUILT_IMAGE_LOCATION)) \
 	  --output=$(ART_DUMP_OAT_PATH)/boot.$(TARGET_ARCH).oatdump.txt --instruction-set=$(TARGET_ARCH)
 	@echo Output in $(ART_DUMP_OAT_PATH)/boot.$(TARGET_ARCH).oatdump.txt
 endif
@@ -74,7 +79,12 @@
 ifdef TARGET_2ND_ARCH
 .PHONY: dump-oat-boot-$(TARGET_2ND_ARCH)
 dump-oat-boot-$(TARGET_2ND_ARCH): $(2ND_DEFAULT_DEX_PREOPT_BUILT_IMAGE_FILENAME) $(OATDUMP)
-	$(OATDUMP) $(addprefix --image=,$(2ND_DEFAULT_DEX_PREOPT_BUILT_IMAGE_LOCATION)) \
+	$(OATDUMP) \
+	  --runtime-arg \
+	  -Xbootclasspath:$(call normalize-path-list, $(DEXPREOPT_BOOTCLASSPATH_DEX_FILES)) \
+	  --runtime-arg \
+	  -Xbootclasspath-locations:$(call normalize-path-list, $(DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS)) \
+	  $(addprefix --image=,$(2ND_DEFAULT_DEX_PREOPT_BUILT_IMAGE_LOCATION)) \
 	  --output=$(ART_DUMP_OAT_PATH)/boot.$(TARGET_2ND_ARCH).oatdump.txt --instruction-set=$(TARGET_2ND_ARCH)
 	@echo Output in $(ART_DUMP_OAT_PATH)/boot.$(TARGET_2ND_ARCH).oatdump.txt
 endif
diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc
index 079cd98..da7eef9 100644
--- a/openjdkjvmti/fixed_up_dex_file.cc
+++ b/openjdkjvmti/fixed_up_dex_file.cc
@@ -87,8 +87,7 @@
   }
 }
 
-std::unique_ptr<FixedUpDexFile> FixedUpDexFile::Create(jobject class_loader,
-                                                       const art::DexFile& original,
+std::unique_ptr<FixedUpDexFile> FixedUpDexFile::Create(const art::DexFile& original,
                                                        const char* descriptor) {
   // Copy the data into mutable memory.
   std::vector<unsigned char> data;
@@ -101,11 +100,11 @@
   // property from `original` to `new_dex_file`.
   const art::DexFileLoader dex_file_loader;
 
-  if (original.IsCompactDexFile() || class_loader == nullptr) {
+  if (original.IsCompactDexFile() || original.HasHiddenapiClassData()) {
     // Since we are supposed to return a standard dex, convert back using dexlayout. It's OK to do
     // this before unquickening.
-    // We also do dex layout for boot classpath dex files, as they contain hidden API flags which
-    // we want to remove.
+    // We also do dex layout for dex files that have hidden API data, as we want to remove that
+    // data.
     art::Options options;
     options.compact_dex_level_ = art::CompactDexLevel::kCompactDexLevelNone;
     // Add a filter to only include the class that has the matching descriptor.
diff --git a/openjdkjvmti/fixed_up_dex_file.h b/openjdkjvmti/fixed_up_dex_file.h
index e09d70b..594e8a7 100644
--- a/openjdkjvmti/fixed_up_dex_file.h
+++ b/openjdkjvmti/fixed_up_dex_file.h
@@ -49,8 +49,7 @@
 // are running on.
 class FixedUpDexFile {
  public:
-  static std::unique_ptr<FixedUpDexFile> Create(jobject class_loader,
-                                                const art::DexFile& original,
+  static std::unique_ptr<FixedUpDexFile> Create(const art::DexFile& original,
                                                 const char* descriptor);
 
   const art::DexFile& GetDexFile() {
diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc
index a8e220c..3ad1112 100644
--- a/openjdkjvmti/ti_class.cc
+++ b/openjdkjvmti/ti_class.cc
@@ -34,6 +34,7 @@
 #include "android-base/stringprintf.h"
 
 #include <mutex>
+#include <string_view>
 #include <unordered_set>
 
 #include "art_jvmti.h"
@@ -872,7 +873,7 @@
                                        /*out*/jint* count_ptr,
                                        /*out*/char*** classes) {
   jvmtiError res = OK;
-  std::set<art::StringPiece> unique_descriptors;
+  std::set<std::string_view> unique_descriptors;
   std::vector<const char*> descriptors;
   auto add_descriptor = [&](const char* desc) {
     // Don't add duplicates.
diff --git a/openjdkjvmti/ti_class_definition.cc b/openjdkjvmti/ti_class_definition.cc
index 2345a0a..20feb78 100644
--- a/openjdkjvmti/ti_class_definition.cc
+++ b/openjdkjvmti/ti_class_definition.cc
@@ -57,7 +57,7 @@
 
   std::string desc = std::string("L") + name_ + ";";
   std::unique_ptr<FixedUpDexFile>
-      fixed_dex_file(FixedUpDexFile::Create(loader_, *initial_dex_file_unquickened_, desc.c_str()));
+      fixed_dex_file(FixedUpDexFile::Create(*initial_dex_file_unquickened_, desc.c_str()));
   CHECK(fixed_dex_file.get() != nullptr);
   CHECK_LE(fixed_dex_file->Size(), temp_mmap_.Size());
   CHECK_EQ(temp_mmap_.Size(), dex_data_mmap_.Size());
@@ -132,20 +132,18 @@
   return OK;
 }
 
-static void DequickenDexFile(jobject class_loader,
-                             const art::DexFile* dex_file,
+static void DequickenDexFile(const art::DexFile* dex_file,
                              const char* descriptor,
                              /*out*/std::vector<unsigned char>* dex_data)
     REQUIRES_SHARED(art::Locks::mutator_lock_) {
   std::unique_ptr<FixedUpDexFile> fixed_dex_file(
-      FixedUpDexFile::Create(class_loader, *dex_file, descriptor));
+      FixedUpDexFile::Create(*dex_file, descriptor));
   dex_data->resize(fixed_dex_file->Size());
   memcpy(dex_data->data(), fixed_dex_file->Begin(), fixed_dex_file->Size());
 }
 
 // Gets the data surrounding the given class.
-static void GetDexDataForRetransformation(art::ScopedObjectAccess& soa,
-                                          art::Handle<art::mirror::Class> klass,
+static void GetDexDataForRetransformation(art::Handle<art::mirror::Class> klass,
                                           /*out*/std::vector<unsigned char>* dex_data)
     REQUIRES_SHARED(art::Locks::mutator_lock_) {
   art::StackHandleScope<3> hs(art::Thread::Current());
@@ -182,8 +180,7 @@
     dex_file = &klass->GetDexFile();
   }
   std::string storage;
-  jobject loader = soa.AddLocalReference<jobject>(klass->GetClassLoader());
-  DequickenDexFile(loader, dex_file, klass->GetDescriptor(&storage), dex_data);
+  DequickenDexFile(dex_file, klass->GetDescriptor(&storage), dex_data);
 }
 
 static bool DexNeedsDequickening(art::Handle<art::mirror::Class> klass,
@@ -336,7 +333,7 @@
   const art::DexFile* quick_dex = GetQuickenedDexFile(m_klass);
   auto get_original = [&](/*out*/std::vector<unsigned char>* dex_data)
       REQUIRES_SHARED(art::Locks::mutator_lock_) {
-    GetDexDataForRetransformation(soa, m_klass, dex_data);
+    GetDexDataForRetransformation(m_klass, dex_data);
   };
   InitWithDex(get_original, quick_dex);
   return OK;
@@ -369,7 +366,7 @@
   protection_domain_ = nullptr;
   auto get_original = [&](/*out*/std::vector<unsigned char>* dex_data)
       REQUIRES_SHARED(art::Locks::mutator_lock_) {
-    DequickenDexFile(loader_, &dex_file, descriptor, dex_data);
+    DequickenDexFile(&dex_file, descriptor, dex_data);
   };
   InitWithDex(get_original, &dex_file);
 }
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index 3d175a8..eb4bada 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -32,6 +32,7 @@
 #include "ti_redefine.h"
 
 #include <limits>
+#include <string_view>
 
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
@@ -40,7 +41,6 @@
 #include "art_jvmti.h"
 #include "art_method-inl.h"
 #include "base/array_ref.h"
-#include "base/stringpiece.h"
 #include "class_linker-inl.h"
 #include "class_root.h"
 #include "debugger.h"
@@ -597,7 +597,7 @@
 // Try and get the declared method. First try to get a virtual method then a direct method if that's
 // not found.
 static art::ArtMethod* FindMethod(art::Handle<art::mirror::Class> klass,
-                                  art::StringPiece name,
+                                  std::string_view name,
                                   art::Signature sig) REQUIRES_SHARED(art::Locks::mutator_lock_) {
   DCHECK(!klass->IsProxyClass());
   for (art::ArtMethod& m : klass->GetDeclaredMethodsSlice(art::kRuntimePointerSize)) {
diff --git a/profman/profman.cc b/profman/profman.cc
index 82d9df0..b29e743 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -25,6 +25,7 @@
 #include <iostream>
 #include <set>
 #include <string>
+#include <string_view>
 #include <unordered_set>
 #include <vector>
 
@@ -36,7 +37,7 @@
 #include "base/mem_map.h"
 #include "base/scoped_flock.h"
 #include "base/stl_util.h"
-#include "base/stringpiece.h"
+#include "base/string_view_cpp20.h"
 #include "base/time_utils.h"
 #include "base/unix_file/fd_file.h"
 #include "base/utils.h"
@@ -188,6 +189,33 @@
   exit(1);
 }
 
+template <typename T>
+static void ParseUintOption(const char* raw_option,
+                            std::string_view option_prefix,
+                            T* out) {
+  DCHECK(EndsWith(option_prefix, "="));
+  DCHECK(StartsWith(raw_option, option_prefix)) << raw_option << " " << option_prefix;
+  const char* value_string = raw_option + option_prefix.size();
+  int64_t parsed_integer_value = 0;
+  if (!android::base::ParseInt(value_string, &parsed_integer_value)) {
+    std::string option_name(option_prefix.substr(option_prefix.size() - 1u));
+    Usage("Failed to parse %s '%s' as an integer", option_name.c_str(), value_string);
+  }
+  if (parsed_integer_value < 0) {
+    std::string option_name(option_prefix.substr(option_prefix.size() - 1u));
+    Usage("%s passed a negative value %" PRId64, option_name.c_str(), parsed_integer_value);
+  }
+  if (static_cast<uint64_t>(parsed_integer_value) >
+      static_cast<std::make_unsigned_t<T>>(std::numeric_limits<T>::max())) {
+    std::string option_name(option_prefix.substr(option_prefix.size() - 1u));
+    Usage("%s passed a value %" PRIu64 " above max (%" PRIu64 ")",
+          option_name.c_str(),
+          static_cast<uint64_t>(parsed_integer_value),
+          static_cast<uint64_t>(std::numeric_limits<T>::max()));
+  }
+  *out = dchecked_integral_cast<T>(parsed_integer_value);
+}
+
 // TODO(calin): This class has grown too much from its initial design. Split the functionality
 // into smaller, more contained pieces.
 class ProfMan final {
@@ -226,7 +254,8 @@
     }
 
     for (int i = 0; i < argc; ++i) {
-      const StringPiece option(argv[i]);
+      const char* raw_option = argv[i];
+      const std::string_view option(raw_option);
       const bool log_options = false;
       if (log_options) {
         LOG(INFO) << "profman: option[" << i << "]=" << argv[i];
@@ -235,66 +264,60 @@
         dump_only_ = true;
       } else if (option == "--dump-classes-and-methods") {
         dump_classes_and_methods_ = true;
-      } else if (option.starts_with("--create-profile-from=")) {
-        create_profile_from_file_ = option.substr(strlen("--create-profile-from=")).ToString();
-      } else if (option.starts_with("--dump-output-to-fd=")) {
-        ParseUintOption(option, "--dump-output-to-fd", &dump_output_to_fd_, Usage);
+      } else if (StartsWith(option, "--create-profile-from=")) {
+        create_profile_from_file_ = std::string(option.substr(strlen("--create-profile-from=")));
+      } else if (StartsWith(option, "--dump-output-to-fd=")) {
+        ParseUintOption(raw_option, "--dump-output-to-fd=", &dump_output_to_fd_);
       } else if (option == "--generate-boot-image-profile") {
         generate_boot_image_profile_ = true;
-      } else if (option.starts_with("--boot-image-class-threshold=")) {
-        ParseUintOption(option,
-                        "--boot-image-class-threshold",
-                        &boot_image_options_.image_class_theshold,
-                        Usage);
-      } else if (option.starts_with("--boot-image-clean-class-threshold=")) {
-        ParseUintOption(option,
-                        "--boot-image-clean-class-threshold",
-                        &boot_image_options_.image_class_clean_theshold,
-                        Usage);
-      } else if (option.starts_with("--boot-image-sampled-method-threshold=")) {
-        ParseUintOption(option,
-                        "--boot-image-sampled-method-threshold",
-                        &boot_image_options_.compiled_method_threshold,
-                        Usage);
-      } else if (option.starts_with("--profile-file=")) {
-        profile_files_.push_back(option.substr(strlen("--profile-file=")).ToString());
-      } else if (option.starts_with("--profile-file-fd=")) {
-        ParseFdForCollection(option, "--profile-file-fd", &profile_files_fd_);
-      } else if (option.starts_with("--reference-profile-file=")) {
-        reference_profile_file_ = option.substr(strlen("--reference-profile-file=")).ToString();
-      } else if (option.starts_with("--reference-profile-file-fd=")) {
-        ParseUintOption(option, "--reference-profile-file-fd", &reference_profile_file_fd_, Usage);
-      } else if (option.starts_with("--dex-location=")) {
-        dex_locations_.push_back(option.substr(strlen("--dex-location=")).ToString());
-      } else if (option.starts_with("--apk-fd=")) {
-        ParseFdForCollection(option, "--apk-fd", &apks_fd_);
-      } else if (option.starts_with("--apk=")) {
-        apk_files_.push_back(option.substr(strlen("--apk=")).ToString());
-      } else if (option.starts_with("--generate-test-profile=")) {
-        test_profile_ = option.substr(strlen("--generate-test-profile=")).ToString();
-      } else if (option.starts_with("--generate-test-profile-num-dex=")) {
-        ParseUintOption(option,
-                        "--generate-test-profile-num-dex",
-                        &test_profile_num_dex_,
-                        Usage);
-      } else if (option.starts_with("--generate-test-profile-method-percentage")) {
-        ParseUintOption(option,
-                        "--generate-test-profile-method-percentage",
-                        &test_profile_method_percerntage_,
-                        Usage);
-      } else if (option.starts_with("--generate-test-profile-class-percentage")) {
-        ParseUintOption(option,
-                        "--generate-test-profile-class-percentage",
-                        &test_profile_class_percentage_,
-                        Usage);
-      } else if (option.starts_with("--generate-test-profile-seed=")) {
-        ParseUintOption(option, "--generate-test-profile-seed", &test_profile_seed_, Usage);
-      } else if (option.starts_with("--copy-and-update-profile-key")) {
+      } else if (StartsWith(option, "--boot-image-class-threshold=")) {
+        ParseUintOption(raw_option,
+                        "--boot-image-class-threshold=",
+                        &boot_image_options_.image_class_theshold);
+      } else if (StartsWith(option, "--boot-image-clean-class-threshold=")) {
+        ParseUintOption(raw_option,
+                        "--boot-image-clean-class-threshold=",
+                        &boot_image_options_.image_class_clean_theshold);
+      } else if (StartsWith(option, "--boot-image-sampled-method-threshold=")) {
+        ParseUintOption(raw_option,
+                        "--boot-image-sampled-method-threshold=",
+                        &boot_image_options_.compiled_method_threshold);
+      } else if (StartsWith(option, "--profile-file=")) {
+        profile_files_.push_back(std::string(option.substr(strlen("--profile-file="))));
+      } else if (StartsWith(option, "--profile-file-fd=")) {
+        ParseFdForCollection(raw_option, "--profile-file-fd=", &profile_files_fd_);
+      } else if (StartsWith(option, "--reference-profile-file=")) {
+        reference_profile_file_ = std::string(option.substr(strlen("--reference-profile-file=")));
+      } else if (StartsWith(option, "--reference-profile-file-fd=")) {
+        ParseUintOption(raw_option, "--reference-profile-file-fd=", &reference_profile_file_fd_);
+      } else if (StartsWith(option, "--dex-location=")) {
+        dex_locations_.push_back(std::string(option.substr(strlen("--dex-location="))));
+      } else if (StartsWith(option, "--apk-fd=")) {
+        ParseFdForCollection(raw_option, "--apk-fd=", &apks_fd_);
+      } else if (StartsWith(option, "--apk=")) {
+        apk_files_.push_back(std::string(option.substr(strlen("--apk="))));
+      } else if (StartsWith(option, "--generate-test-profile=")) {
+        test_profile_ = std::string(option.substr(strlen("--generate-test-profile=")));
+      } else if (StartsWith(option, "--generate-test-profile-num-dex=")) {
+        ParseUintOption(raw_option,
+                        "--generate-test-profile-num-dex=",
+                        &test_profile_num_dex_);
+      } else if (StartsWith(option, "--generate-test-profile-method-percentage=")) {
+        ParseUintOption(raw_option,
+                        "--generate-test-profile-method-percentage=",
+                        &test_profile_method_percerntage_);
+      } else if (StartsWith(option, "--generate-test-profile-class-percentage=")) {
+        ParseUintOption(raw_option,
+                        "--generate-test-profile-class-percentage=",
+                        &test_profile_class_percentage_);
+      } else if (StartsWith(option, "--generate-test-profile-seed=")) {
+        ParseUintOption(raw_option, "--generate-test-profile-seed=", &test_profile_seed_);
+      } else if (option == "--copy-and-update-profile-key") {
         copy_and_update_profile_key_ = true;
-      } else if (option.starts_with("--store-aggregation-counters")) {
+      } else if (option == "--store-aggregation-counters") {
         store_aggregation_counters_ = true;
       } else {
-        Usage("Unknown argument '%s'", option.data());
+        Usage("Unknown argument '%s'", raw_option);
       }
     }
 
@@ -1265,11 +1288,11 @@
   }
 
  private:
-  static void ParseFdForCollection(const StringPiece& option,
-                                   const char* arg_name,
+  static void ParseFdForCollection(const char* raw_option,
+                                   std::string_view option_prefix,
                                    std::vector<int>* fds) {
     int fd;
-    ParseUintOption(option, arg_name, &fd, Usage);
+    ParseUintOption(raw_option, option_prefix, &fd);
     fds->push_back(fd);
   }
 
diff --git a/runtime/Android.bp b/runtime/Android.bp
index a3081e9..a08ba70 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -392,14 +392,11 @@
         "jni_platform_headers",
     ],
     shared_libs: [
+        "libartpalette",
         "libnativebridge",
         "libnativeloader",
         "libbacktrace",
         "liblog",
-        // For atrace, properties, ashmem.
-        "libcutils",
-        // For set_sched_policy.
-        "libprocessgroup",
         // For common macros.
         "libbase",
     ],
@@ -424,9 +421,9 @@
         },
     },
     static_libs: [
+        "libartpalette",
         "libbacktrace",
         "libbase",
-        "libcutils",
         "libdexfile_external",  // libunwindstack dependency
         "libdexfile_support",  // libunwindstack dependency
         "liblog",
diff --git a/runtime/arch/arm/instruction_set_features_arm.cc b/runtime/arch/arm/instruction_set_features_arm.cc
index fcf3c75..fdf4dbd 100644
--- a/runtime/arch/arm/instruction_set_features_arm.cc
+++ b/runtime/arch/arm/instruction_set_features_arm.cc
@@ -319,8 +319,9 @@
   bool has_atomic_ldrd_strd = has_atomic_ldrd_strd_;
   bool has_div = has_div_;
   bool has_armv8a = has_armv8a_;
-  for (auto i = features.begin(); i != features.end(); i++) {
-    std::string feature = android::base::Trim(*i);
+  for (const std::string& feature : features) {
+    DCHECK_EQ(android::base::Trim(feature), feature)
+        << "Feature name is not trimmed: '" << feature << "'";
     if (feature == "div") {
       has_div = true;
     } else if (feature == "-div") {
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc
index 4a2b9d5..196f358 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64.cc
@@ -315,8 +315,9 @@
   bool has_lse = has_lse_;
   bool has_fp16 = has_fp16_;
   bool has_dotprod = has_dotprod_;
-  for (auto i = features.begin(); i != features.end(); i++) {
-    std::string feature = android::base::Trim(*i);
+  for (const std::string& feature : features) {
+    DCHECK_EQ(android::base::Trim(feature), feature)
+        << "Feature name is not trimmed: '" << feature << "'";
     if (feature == "a53") {
       is_a53 = true;
     } else if (feature == "-a53") {
@@ -367,4 +368,17 @@
                                       has_dotprod));
 }
 
+std::unique_ptr<const InstructionSetFeatures>
+Arm64InstructionSetFeatures::AddRuntimeDetectedFeatures(
+    const InstructionSetFeatures *features) const {
+  const Arm64InstructionSetFeatures *arm64_features = features->AsArm64InstructionSetFeatures();
+  return std::unique_ptr<const InstructionSetFeatures>(
+      new Arm64InstructionSetFeatures(fix_cortex_a53_835769_,
+                                      fix_cortex_a53_843419_,
+                                      arm64_features->has_crc_,
+                                      arm64_features->has_lse_,
+                                      arm64_features->has_fp16_,
+                                      arm64_features->has_dotprod_));
+}
+
 }  // namespace art
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.h b/runtime/arch/arm64/instruction_set_features_arm64.h
index 4ec8fa2..432b9ef 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.h
+++ b/runtime/arch/arm64/instruction_set_features_arm64.h
@@ -98,6 +98,9 @@
       AddFeaturesFromSplitString(const std::vector<std::string>& features,
                                  std::string* error_msg) const override;
 
+  std::unique_ptr<const InstructionSetFeatures>
+      AddRuntimeDetectedFeatures(const InstructionSetFeatures *features) const override;
+
  private:
   Arm64InstructionSetFeatures(bool needs_a53_835769_fix,
                               bool needs_a53_843419_fix,
diff --git a/runtime/arch/arm64/instruction_set_features_arm64_test.cc b/runtime/arch/arm64/instruction_set_features_arm64_test.cc
index 99d6b0d..eef8f08 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64_test.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64_test.cc
@@ -170,4 +170,54 @@
   EXPECT_EQ(armv8_2a_cpu_features->AsBitmap(), 14U);
 }
 
+TEST(Arm64InstructionSetFeaturesTest, IsRuntimeDetectionSupported) {
+  if (kRuntimeISA == InstructionSet::kArm64) {
+    EXPECT_TRUE(InstructionSetFeatures::IsRuntimeDetectionSupported());
+  }
+}
+
+TEST(Arm64InstructionSetFeaturesTest, FeaturesFromRuntimeDetection) {
+  if (kRuntimeISA != InstructionSet::kArm64) {
+    return;
+  }
+
+  std::unique_ptr<const InstructionSetFeatures> hwcap_features(
+      InstructionSetFeatures::FromHwcap());
+  std::unique_ptr<const InstructionSetFeatures> runtime_detected_features(
+      InstructionSetFeatures::FromRuntimeDetection());
+  std::unique_ptr<const InstructionSetFeatures> cpp_defined_features(
+      InstructionSetFeatures::FromCppDefines());
+  EXPECT_NE(runtime_detected_features, nullptr);
+  EXPECT_TRUE(InstructionSetFeatures::IsRuntimeDetectionSupported());
+  EXPECT_TRUE(runtime_detected_features->Equals(hwcap_features.get()));
+  EXPECT_TRUE(runtime_detected_features->HasAtLeast(cpp_defined_features.get()));
+}
+
+TEST(Arm64InstructionSetFeaturesTest, AddFeaturesFromStringRuntime) {
+  std::unique_ptr<const InstructionSetFeatures> features(
+      InstructionSetFeatures::FromBitmap(InstructionSet::kArm64, 0x0));
+  std::unique_ptr<const InstructionSetFeatures> hwcap_features(
+      InstructionSetFeatures::FromHwcap());
+
+  std::string error_msg;
+  features = features->AddFeaturesFromString("runtime", &error_msg);
+
+  EXPECT_NE(features, nullptr);
+  EXPECT_TRUE(error_msg.empty());
+
+  if (kRuntimeISA == InstructionSet::kArm64) {
+    EXPECT_TRUE(features->Equals(hwcap_features.get()));
+    EXPECT_EQ(features->GetFeatureString(), hwcap_features->GetFeatureString());
+  }
+
+  std::unique_ptr<const InstructionSetFeatures> a53_features(
+      Arm64InstructionSetFeatures::FromVariant("cortex-a53", &error_msg));
+  features = a53_features->AddFeaturesFromString("runtime", &error_msg);
+  EXPECT_NE(features, nullptr);
+  EXPECT_TRUE(error_msg.empty()) << error_msg;
+  const Arm64InstructionSetFeatures *arm64_features = features->AsArm64InstructionSetFeatures();
+  EXPECT_TRUE(arm64_features->NeedFixCortexA53_835769());
+  EXPECT_TRUE(arm64_features->NeedFixCortexA53_843419());
+}
+
 }  // namespace art
diff --git a/runtime/arch/instruction_set_features.cc b/runtime/arch/instruction_set_features.cc
index 886b40a..c5c2d31 100644
--- a/runtime/arch/instruction_set_features.cc
+++ b/runtime/arch/instruction_set_features.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <algorithm>
+
 #include "instruction_set_features.h"
 
 #include <algorithm>
@@ -113,6 +115,16 @@
   UNREACHABLE();
 }
 
+std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::FromRuntimeDetection() {
+  switch (kRuntimeISA) {
+#ifdef ART_TARGET_ANDROID
+    case InstructionSet::kArm64:
+      return Arm64InstructionSetFeatures::FromHwcap();
+#endif
+    default:
+      return nullptr;
+  }
+}
 
 std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::FromCpuInfo() {
   switch (kRuntimeISA) {
@@ -184,44 +196,57 @@
 }
 
 std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::AddFeaturesFromString(
-    const std::string& feature_list, std::string* error_msg) const {
-  if (feature_list.empty()) {
-    *error_msg = "No instruction set features specified";
-    return std::unique_ptr<const InstructionSetFeatures>();
-  }
+    const std::string& feature_list, /* out */ std::string* error_msg) const {
   std::vector<std::string> features;
   Split(feature_list, ',', &features);
-  bool use_default = false;  // Have we seen the 'default' feature?
-  bool first = false;  // Is this first feature?
-  for (auto it = features.begin(); it != features.end();) {
-    if (use_default) {
-      *error_msg = "Unexpected instruction set features after 'default'";
-      return std::unique_ptr<const InstructionSetFeatures>();
-    }
-    std::string feature = android::base::Trim(*it);
-    bool erase = false;
-    if (feature == "default") {
-      if (!first) {
-        use_default = true;
-        erase = true;
-      } else {
-        *error_msg = "Unexpected instruction set features before 'default'";
-        return std::unique_ptr<const InstructionSetFeatures>();
-      }
-    }
-    if (!erase) {
-      ++it;
-    } else {
-      it = features.erase(it);
-    }
-    first = true;
+  std::transform(std::begin(features), std::end(features), std::begin(features),
+                 [](const std::string &s) { return android::base::Trim(s); });
+  auto empty_strings_begin = std::copy_if(std::begin(features), std::end(features),
+                                          std::begin(features),
+                                          [](const std::string& s) { return !s.empty(); });
+  features.erase(empty_strings_begin, std::end(features));
+  if (features.empty()) {
+    *error_msg = "No instruction set features specified";
+    return nullptr;
   }
-  // Expectation: "default" is standalone, no other flags. But an empty features vector after
-  // processing can also come along if the handled flags are the only ones in the list. So
-  // logically, we check "default -> features.empty."
-  DCHECK(!use_default || features.empty());
 
-  return AddFeaturesFromSplitString(features, error_msg);
+  bool use_default = false;
+  bool use_runtime_detection = false;
+  for (const std::string& feature : features) {
+    if (feature == "default") {
+      if (features.size() > 1) {
+        *error_msg = "Specific instruction set feature(s) cannot be used when 'default' is used.";
+        return nullptr;
+      }
+      use_default = true;
+      features.pop_back();
+      break;
+    } else if (feature == "runtime") {
+      if (features.size() > 1) {
+        *error_msg = "Specific instruction set feature(s) cannot be used when 'runtime' is used.";
+        return nullptr;
+      }
+      use_runtime_detection = true;
+      features.pop_back();
+      break;
+    }
+  }
+  // Expectation: "default" and "runtime" are standalone, no other feature names.
+  // But an empty features vector after processing can also come along if the
+  // handled feature names  are the only ones in the list. So
+  // logically, we check "default or runtime => features.empty."
+  DCHECK((!use_default && !use_runtime_detection) || features.empty());
+
+  std::unique_ptr<const InstructionSetFeatures> runtime_detected_features;
+  if (use_runtime_detection) {
+    runtime_detected_features = FromRuntimeDetection();
+  }
+
+  if (runtime_detected_features != nullptr) {
+    return AddRuntimeDetectedFeatures(runtime_detected_features.get());
+  } else {
+    return AddFeaturesFromSplitString(features, error_msg);
+  }
 }
 
 const ArmInstructionSetFeatures* InstructionSetFeatures::AsArmInstructionSetFeatures() const {
@@ -262,6 +287,12 @@
   return std::find(begin, end, variant) != end;
 }
 
+std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::AddRuntimeDetectedFeatures(
+    const InstructionSetFeatures *features ATTRIBUTE_UNUSED) const {
+  UNIMPLEMENTED(FATAL) << kRuntimeISA;
+  UNREACHABLE();
+}
+
 std::ostream& operator<<(std::ostream& os, const InstructionSetFeatures& rhs) {
   os << "ISA: " << rhs.GetInstructionSet() << " Feature string: " << rhs.GetFeatureString();
   return os;
diff --git a/runtime/arch/instruction_set_features.h b/runtime/arch/instruction_set_features.h
index f910a41..9222a7b 100644
--- a/runtime/arch/instruction_set_features.h
+++ b/runtime/arch/instruction_set_features.h
@@ -48,6 +48,20 @@
   // Turn C pre-processor #defines into the equivalent instruction set features for kRuntimeISA.
   static std::unique_ptr<const InstructionSetFeatures> FromCppDefines();
 
+  // Check if run-time detection of instruction set features is supported.
+  //
+  // Return: true - if run-time detection is supported on a target device.
+  //         false - otherwise
+  static bool IsRuntimeDetectionSupported() {
+    return FromRuntimeDetection() != nullptr;
+  }
+
+  // Use run-time detection to get instruction set features.
+  //
+  // Return: a set of detected features or nullptr if runtime detection is not
+  //         supported on a target.
+  static std::unique_ptr<const InstructionSetFeatures> FromRuntimeDetection();
+
   // Process /proc/cpuinfo and use kRuntimeISA to produce InstructionSetFeatures.
   static std::unique_ptr<const InstructionSetFeatures> FromCpuInfo();
 
@@ -126,6 +140,10 @@
       AddFeaturesFromSplitString(const std::vector<std::string>& features,
                                  std::string* error_msg) const = 0;
 
+  // Add run-time detected architecture specific features in sub-classes.
+  virtual std::unique_ptr<const InstructionSetFeatures>
+      AddRuntimeDetectedFeatures(const InstructionSetFeatures *features ATTRIBUTE_UNUSED) const;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(InstructionSetFeatures);
 };
diff --git a/runtime/arch/instruction_set_features_test.cc b/runtime/arch/instruction_set_features_test.cc
index 3a39a2a..d9b2e3f 100644
--- a/runtime/arch/instruction_set_features_test.cc
+++ b/runtime/arch/instruction_set_features_test.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <array>
+
 #include "instruction_set_features.h"
 
 #include <gtest/gtest.h>
@@ -161,4 +163,145 @@
       << "\nFeatures from build: " << *instruction_set_features.get();
 }
 
+TEST(InstructionSetFeaturesTest, FeaturesFromRuntimeDetection) {
+  if (!InstructionSetFeatures::IsRuntimeDetectionSupported()) {
+    EXPECT_EQ(InstructionSetFeatures::FromRuntimeDetection(), nullptr);
+  }
+}
+
+// The instruction set feature string must not contain 'default' together with
+// other feature names.
+//
+// Test that InstructionSetFeatures::AddFeaturesFromString returns nullptr and
+// an error is reported when the value 'default' is specified together
+// with other feature names in an instruction set feature string.
+TEST(InstructionSetFeaturesTest, AddFeaturesFromStringWithDefaultAndOtherNames) {
+  std::unique_ptr<const InstructionSetFeatures> cpp_defined_features(
+      InstructionSetFeatures::FromCppDefines());
+  std::vector<std::string> invalid_feature_strings = {
+    "a,default",
+    "default,a",
+    "a,default,b",
+    "a,b,default",
+    "default,a,b,c",
+    "a,b,default,c,d",
+    "a, default ",
+    " default , a",
+    "a, default , b",
+    "default,runtime"
+  };
+
+  for (const std::string& invalid_feature_string : invalid_feature_strings) {
+    std::string error_msg;
+    EXPECT_EQ(cpp_defined_features->AddFeaturesFromString(invalid_feature_string, &error_msg),
+              nullptr) << " Invalid feature string: '" << invalid_feature_string << "'";
+    EXPECT_EQ(error_msg,
+              "Specific instruction set feature(s) cannot be used when 'default' is used.");
+  }
+}
+
+// The instruction set feature string must not contain 'runtime' together with
+// other feature names.
+//
+// Test that InstructionSetFeatures::AddFeaturesFromString returns nullptr and
+// an error is reported when the value 'runtime' is specified together
+// with other feature names in an instruction set feature string.
+TEST(InstructionSetFeaturesTest, AddFeaturesFromStringWithRuntimeAndOtherNames) {
+  std::unique_ptr<const InstructionSetFeatures> cpp_defined_features(
+      InstructionSetFeatures::FromCppDefines());
+  std::vector<std::string> invalid_feature_strings = {
+    "a,runtime",
+    "runtime,a",
+    "a,runtime,b",
+    "a,b,runtime",
+    "runtime,a,b,c",
+    "a,b,runtime,c,d",
+    "a, runtime ",
+    " runtime , a",
+    "a, runtime , b",
+    "runtime,default"
+  };
+
+  for (const std::string& invalid_feature_string : invalid_feature_strings) {
+    std::string error_msg;
+    EXPECT_EQ(cpp_defined_features->AddFeaturesFromString(invalid_feature_string, &error_msg),
+              nullptr) << " Invalid feature string: '" << invalid_feature_string << "'";
+    EXPECT_EQ(error_msg,
+              "Specific instruction set feature(s) cannot be used when 'runtime' is used.");
+  }
+}
+
+// Spaces and multiple commas are ignores in a instruction set feature string.
+//
+// Test that a use of spaces and multiple commas with 'default' and 'runtime'
+// does not cause errors.
+TEST(InstructionSetFeaturesTest, AddFeaturesFromValidStringContainingDefaultOrRuntime) {
+  std::unique_ptr<const InstructionSetFeatures> cpp_defined_features(
+      InstructionSetFeatures::FromCppDefines());
+  std::vector<std::string> valid_feature_strings = {
+    "default",
+    ",,,default",
+    "default,,,,",
+    ",,,default,,,,",
+    "default, , , ",
+    " , , ,default",
+    " , , ,default, , , ",
+    " default , , , ",
+    ",,,runtime",
+    "runtime,,,,",
+    ",,,runtime,,,,",
+    "runtime, , , ",
+    " , , ,runtime",
+    " , , ,runtime, , , ",
+    " runtime , , , "
+  };
+  for (const std::string& valid_feature_string : valid_feature_strings) {
+    std::string error_msg;
+    EXPECT_NE(cpp_defined_features->AddFeaturesFromString(valid_feature_string, &error_msg),
+              nullptr) << " Valid feature string: '" << valid_feature_string << "'";
+    EXPECT_TRUE(error_msg.empty()) << error_msg;
+  }
+}
+
+// Spaces and multiple commas are ignores in a instruction set feature string.
+//
+// Test that a use of spaces and multiple commas without any feature names
+// causes errors.
+TEST(InstructionSetFeaturesTest, AddFeaturesFromInvalidStringWithoutFeatureNames) {
+  std::unique_ptr<const InstructionSetFeatures> cpp_defined_features(
+      InstructionSetFeatures::FromCppDefines());
+  std::vector<std::string> invalid_feature_strings = {
+    " ",
+    "       ",
+    ",",
+    ",,",
+    " , , ,,,,,,",
+    "\t",
+    "  \t     ",
+    ",",
+    ",,",
+    " , , ,,,,,,"
+  };
+  for (const std::string& invalid_feature_string : invalid_feature_strings) {
+    std::string error_msg;
+    EXPECT_EQ(cpp_defined_features->AddFeaturesFromString(invalid_feature_string, &error_msg),
+              nullptr) << " Invalid feature string: '" << invalid_feature_string << "'";
+    EXPECT_EQ(error_msg, "No instruction set features specified");
+  }
+}
+
+TEST(InstructionSetFeaturesTest, AddFeaturesFromStringRuntime) {
+  std::unique_ptr<const InstructionSetFeatures> cpp_defined_features(
+      InstructionSetFeatures::FromCppDefines());
+  std::string error_msg;
+
+  const std::unique_ptr<const InstructionSetFeatures> features =
+      cpp_defined_features->AddFeaturesFromString("runtime", &error_msg);
+  EXPECT_NE(features, nullptr);
+  EXPECT_TRUE(error_msg.empty()) << error_msg;
+  if (!InstructionSetFeatures::IsRuntimeDetectionSupported()) {
+    EXPECT_TRUE(features->Equals(cpp_defined_features.get()));
+  }
+}
+
 }  // namespace art
diff --git a/runtime/arch/mips/instruction_set_features_mips.cc b/runtime/arch/mips/instruction_set_features_mips.cc
index 952ed25..99ce536 100644
--- a/runtime/arch/mips/instruction_set_features_mips.cc
+++ b/runtime/arch/mips/instruction_set_features_mips.cc
@@ -214,8 +214,9 @@
   bool mips_isa_gte2 = mips_isa_gte2_;
   bool r6 = r6_;
   bool msa = msa_;
-  for (auto i = features.begin(); i != features.end(); i++) {
-    std::string feature = android::base::Trim(*i);
+  for (const std::string& feature : features) {
+    DCHECK_EQ(android::base::Trim(feature), feature)
+        << "Feature name is not trimmed: '" << feature << "'";
     if (feature == "fpu32") {
       fpu_32bit = true;
     } else if (feature == "-fpu32") {
diff --git a/runtime/arch/mips64/instruction_set_features_mips64.cc b/runtime/arch/mips64/instruction_set_features_mips64.cc
index ea9f84b..2031433 100644
--- a/runtime/arch/mips64/instruction_set_features_mips64.cc
+++ b/runtime/arch/mips64/instruction_set_features_mips64.cc
@@ -114,8 +114,9 @@
 Mips64InstructionSetFeatures::AddFeaturesFromSplitString(
     const std::vector<std::string>& features, std::string* error_msg) const {
   bool msa = msa_;
-  for (auto i = features.begin(); i != features.end(); i++) {
-    std::string feature = android::base::Trim(*i);
+  for (const std::string& feature : features) {
+    DCHECK_EQ(android::base::Trim(feature), feature)
+        << "Feature name is not trimmed: '" << feature << "'";
     if (feature == "msa") {
       msa = true;
     } else if (feature == "-msa") {
diff --git a/runtime/arch/x86/instruction_set_features_x86.cc b/runtime/arch/x86/instruction_set_features_x86.cc
index e9e983c..0c3d26e 100644
--- a/runtime/arch/x86/instruction_set_features_x86.cc
+++ b/runtime/arch/x86/instruction_set_features_x86.cc
@@ -311,8 +311,9 @@
   bool has_AVX = has_AVX_;
   bool has_AVX2 = has_AVX2_;
   bool has_POPCNT = has_POPCNT_;
-  for (auto i = features.begin(); i != features.end(); i++) {
-    std::string feature = android::base::Trim(*i);
+  for (const std::string& feature : features) {
+    DCHECK_EQ(android::base::Trim(feature), feature)
+        << "Feature name is not trimmed: '" << feature << "'";
     if (feature == "ssse3") {
       has_SSSE3 = true;
     } else if (feature == "-ssse3") {
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index b0bed56..306c4eb 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1804,6 +1804,7 @@
     PUSH ESI
     PUSH EDX
     movl 16(%esp), %edi         // Load referrer.
+    movd %xmm7, %esi            // Get target method index stored in xmm7, remember it in ESI.
     // If the method is obsolete, just go through the dex cache miss slow path.
     // The obsolete flag is set with suspended threads, so we do not need an acquire operation here.
     testl LITERAL(ACC_OBSOLETE_METHOD), ART_METHOD_ACCESS_FLAGS_OFFSET(%edi)
@@ -1814,8 +1815,7 @@
     movl MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET(%edi), %edi  // Load the resolved methods.
     pushl ART_METHOD_JNI_OFFSET_32(%eax)  // Push ImtConflictTable.
     CFI_ADJUST_CFA_OFFSET(4)
-    movd %xmm7, %eax            // Get target method index stored in xmm7.
-    movl %eax, %esi             // Remember method index in ESI.
+    movl %esi, %eax             // Copy the method index from ESI.
     andl LITERAL(METHOD_DEX_CACHE_SIZE_MINUS_ONE), %eax  // Calculate DexCache method slot index.
     leal 0(%edi, %eax, 2 * __SIZEOF_POINTER__), %edi  // Load DexCache method slot address.
     mov %ecx, %edx              // Make EDX:EAX == ECX:EBX so that LOCK CMPXCHG8B makes no changes.
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index a8a648f..39bf6e8 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1654,7 +1654,7 @@
      * rdi is the conflict ArtMethod.
      * rax is a hidden argument that holds the target interface method's dex method index.
      *
-     * Note that this stub writes to r10 and rdi.
+     * Note that this stub writes to r10, r11, rax and rdi.
      */
 DEFINE_FUNCTION art_quick_imt_conflict_trampoline
 #if defined(__APPLE__)
@@ -1662,6 +1662,8 @@
     int3
 #else
     movq __SIZEOF_POINTER__(%rsp), %r10 // Load referrer.
+    mov %eax, %r11d             // Remember method index in R11.
+    PUSH rdx                    // Preserve RDX as we need to clobber it by LOCK CMPXCHG16B.
     // If the method is obsolete, just go through the dex cache miss slow path.
     // The obsolete flag is set with suspended threads, so we do not need an acquire operation here.
     testl LITERAL(ACC_OBSOLETE_METHOD), ART_METHOD_ACCESS_FLAGS_OFFSET(%r10)
@@ -1670,11 +1672,9 @@
     movl MIRROR_CLASS_DEX_CACHE_OFFSET(%r10), %r10d    // Load the DexCache (without read barrier).
     UNPOISON_HEAP_REF r10d
     movq MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET(%r10), %r10  // Load the resolved methods.
-    mov %eax, %r11d  // Remember method index in R11.
     andl LITERAL(METHOD_DEX_CACHE_SIZE_MINUS_ONE), %eax  // Calculate DexCache method slot index.
     shll LITERAL(1), %eax       // Multiply by 2 as entries have size 2 * __SIZEOF_POINTER__.
     leaq 0(%r10, %rax, __SIZEOF_POINTER__), %r10 // Load DexCache method slot address.
-    PUSH rdx                    // Preserve RDX as we need to clobber it by LOCK CMPXCHG16B.
     mov %rcx, %rdx              // Make RDX:RAX == RCX:RBX so that LOCK CMPXCHG16B makes no changes.
     mov %rbx, %rax              // (The actual value does not matter.)
     lock cmpxchg16b (%r10)      // Relaxed atomic load RDX:RAX from the dex cache slot.
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 44b80df..c7e41be 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -22,7 +22,6 @@
 
 #include "arch/context.h"
 #include "art_method-inl.h"
-#include "base/stringpiece.h"
 #include "class_linker-inl.h"
 #include "class_root.h"
 #include "debugger.h"
@@ -166,12 +165,11 @@
   }
 }
 
-size_t ArtMethod::NumArgRegisters(const StringPiece& shorty) {
-  CHECK_LE(1U, shorty.length());
+size_t ArtMethod::NumArgRegisters(const char* shorty) {
+  CHECK_NE(shorty[0], '\0');
   uint32_t num_registers = 0;
-  for (size_t i = 1; i < shorty.length(); ++i) {
-    char ch = shorty[i];
-    if (ch == 'D' || ch == 'J') {
+  for (const char* s = shorty + 1; *s != '\0'; ++s) {
+    if (*s == 'D' || *s == 'J') {
       num_registers += 2;
     } else {
       num_registers += 1;
diff --git a/runtime/art_method.h b/runtime/art_method.h
index aabd093..feff91a 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -49,7 +49,6 @@
 class OatQuickMethodHeader;
 class ProfilingInfo;
 class ScopedObjectAccessAlreadyRunnable;
-class StringPiece;
 class ShadowFrame;
 
 namespace mirror {
@@ -386,7 +385,7 @@
   }
 
   // Number of 32bit registers that would be required to hold all the arguments
-  static size_t NumArgRegisters(const StringPiece& shorty);
+  static size_t NumArgRegisters(const char* shorty);
 
   ALWAYS_INLINE uint32_t GetDexMethodIndex() {
     return dex_method_index_;
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 5a52818..7aec661 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -107,15 +107,15 @@
         blocked_tid_(kLogLockContentions ? blocked_tid : 0),
         owner_tid_(kLogLockContentions ? owner_tid : 0),
         start_nano_time_(kLogLockContentions ? NanoTime() : 0) {
-    if (ATRACE_ENABLED()) {
+    if (ATraceEnabled()) {
       std::string msg = StringPrintf("Lock contention on %s (owner tid: %" PRIu64 ")",
                                      mutex->GetName(), owner_tid);
-      ATRACE_BEGIN(msg.c_str());
+      ATraceBegin(msg.c_str());
     }
   }
 
   ~ScopedContentionRecorder() {
-    ATRACE_END();
+    ATraceEnd();
     if (kLogLockContentions) {
       uint64_t end_nano_time = NanoTime();
       mutex_->RecordContention(blocked_tid_, owner_tid_, end_nano_time - start_nano_time_);
diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc
index 0a4cddd..bd39192 100644
--- a/runtime/base/timing_logger.cc
+++ b/runtime/base/timing_logger.cc
@@ -144,12 +144,12 @@
 void TimingLogger::StartTiming(const char* label) {
   DCHECK(label != nullptr);
   timings_.push_back(Timing(kind_, label));
-  ATRACE_BEGIN(label);
+  ATraceBegin(label);
 }
 
 void TimingLogger::EndTiming() {
   timings_.push_back(Timing(kind_, nullptr));
-  ATRACE_END();
+  ATraceEnd();
 }
 
 uint64_t TimingLogger::GetTotalNs() const {
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index d29a6b7..148fdba 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -25,6 +25,7 @@
 #include <memory>
 #include <queue>
 #include <string>
+#include <string_view>
 #include <tuple>
 #include <unordered_map>
 #include <utility>
@@ -1448,6 +1449,7 @@
 
   static void UpdateInternStrings(
       gc::space::ImageSpace* space,
+      bool use_preresolved_strings,
       const SafeMap<mirror::String*, mirror::String*>& intern_remap)
       REQUIRES_SHARED(Locks::mutator_lock_);
 };
@@ -1463,8 +1465,10 @@
   ScopedTrace app_image_timing("AppImage:Updating");
 
   Thread* const self = Thread::Current();
-  gc::Heap* const heap = Runtime::Current()->GetHeap();
+  Runtime* const runtime = Runtime::Current();
+  gc::Heap* const heap = runtime->GetHeap();
   const ImageHeader& header = space->GetImageHeader();
+  bool load_app_image_startup_cache = runtime->LoadAppImageStartupCache();
   {
     // Register dex caches with the class loader.
     WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
@@ -1478,6 +1482,10 @@
         class_linker->RegisterDexFileLocked(*dex_file, dex_cache, class_loader.Get());
       }
 
+      if (!load_app_image_startup_cache) {
+        dex_cache->ClearPreResolvedStrings();
+      }
+
       if (kIsDebugBuild) {
         CHECK(new_class_set != nullptr);
         mirror::TypeDexCacheType* const types = dex_cache->GetResolvedTypes();
@@ -1544,11 +1552,13 @@
 
 void AppImageLoadingHelper::UpdateInternStrings(
     gc::space::ImageSpace* space,
+    bool use_preresolved_strings,
     const SafeMap<mirror::String*, mirror::String*>& intern_remap) {
   const uint8_t* target_base = space->Begin();
   const ImageSection& sro_section =
       space->GetImageHeader().GetImageStringReferenceOffsetsSection();
   const size_t num_string_offsets = sro_section.Size() / sizeof(AppImageReferenceOffsetInfo);
+  InternTable* const intern_table = Runtime::Current()->GetInternTable();
 
   VLOG(image)
       << "ClassLinker:AppImage:InternStrings:imageStringReferenceOffsetCount = "
@@ -1581,24 +1591,29 @@
         WriteBarrier::ForEveryFieldWrite(dex_cache);
         dex_cache->GetStrings()[string_index].store(
             mirror::StringDexCachePair(it->second, source.index));
+      } else if (!use_preresolved_strings) {
+        dex_cache->GetStrings()[string_index].store(
+            mirror::StringDexCachePair(intern_table->InternStrong(referred_string), source.index));
       }
     } else if (HasDexCachePreResolvedStringNativeRefTag(base_offset)) {
-      base_offset = ClearDexCacheNativeRefTags(base_offset);
-      DCHECK_ALIGNED(base_offset, 2);
+      if (use_preresolved_strings) {
+        base_offset = ClearDexCacheNativeRefTags(base_offset);
+        DCHECK_ALIGNED(base_offset, 2);
 
-      ObjPtr<mirror::DexCache> dex_cache =
-          reinterpret_cast<mirror::DexCache*>(space->Begin() + base_offset);
-      uint32_t string_index = sro_base[offset_index].second;
+        ObjPtr<mirror::DexCache> dex_cache =
+            reinterpret_cast<mirror::DexCache*>(space->Begin() + base_offset);
+        uint32_t string_index = sro_base[offset_index].second;
 
-      ObjPtr<mirror::String> referred_string =
-          dex_cache->GetPreResolvedStrings()[string_index].Read();
-      DCHECK(referred_string != nullptr);
+        ObjPtr<mirror::String> referred_string =
+            dex_cache->GetPreResolvedStrings()[string_index].Read();
+        DCHECK(referred_string != nullptr);
 
-      auto it = intern_remap.find(referred_string.Ptr());
-      if (it != intern_remap.end()) {
-        // Because we are not using a helper function we need to mark the GC card manually.
-        WriteBarrier::ForEveryFieldWrite(dex_cache);
-        dex_cache->GetPreResolvedStrings()[string_index] = GcRoot<mirror::String>(it->second);
+        auto it = intern_remap.find(referred_string.Ptr());
+        if (it != intern_remap.end()) {
+          // Because we are not using a helper function we need to mark the GC card manually.
+          WriteBarrier::ForEveryFieldWrite(dex_cache);
+          dex_cache->GetPreResolvedStrings()[string_index] = GcRoot<mirror::String>(it->second);
+        }
       }
     } else {
       uint32_t raw_member_offset = sro_base[offset_index].second;
@@ -1621,6 +1636,13 @@
                                 /* kCheckTransaction= */ false,
                                 kVerifyNone,
                                 /* kIsVolatile= */ false>(member_offset, it->second);
+      } else if (!use_preresolved_strings) {
+        obj_ptr->SetFieldObject</* kTransactionActive= */ false,
+                                /* kCheckTransaction= */ false,
+                                kVerifyNone,
+                                /* kIsVolatile= */ false>(
+            member_offset,
+            intern_table->InternStrong(referred_string));
       }
     }
   }
@@ -1631,13 +1653,16 @@
   // the strings they point to.
   ScopedTrace timing("AppImage:InternString");
 
-  InternTable* const intern_table = Runtime::Current()->GetInternTable();
+  Runtime* const runtime = Runtime::Current();
+  InternTable* const intern_table = runtime->GetInternTable();
+
+  const bool load_startup_cache = runtime->LoadAppImageStartupCache();
 
   // Add the intern table, removing any conflicts. For conflicts, store the new address in a map
   // for faster lookup.
   // TODO: Optimize with a bitmap or bloom filter
   SafeMap<mirror::String*, mirror::String*> intern_remap;
-  intern_table->AddImageStringsToTable(space, [&](InternTable::UnorderedSet& interns)
+  auto func = [&](InternTable::UnorderedSet& interns)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(Locks::intern_table_lock_) {
     const size_t non_boot_image_strings = intern_table->CountInterns(
@@ -1680,14 +1705,23 @@
         CHECK(intern_table->LookupStrongLocked(string) == nullptr) << string->ToModifiedUtf8();
       }
     }
-  });
+  };
 
-  VLOG(image) << "AppImage:conflictingInternStrings = " << intern_remap.size();
+  bool update_intern_strings;
+  if (load_startup_cache) {
+    // Only add the intern table if we are using the startup cache. Otherwise,
+    // UpdateInternStrings adds the strings to the intern table.
+    intern_table->AddImageStringsToTable(space, func);
+    update_intern_strings = kIsDebugBuild || !intern_remap.empty();
+    VLOG(image) << "AppImage:conflictingInternStrings = " << intern_remap.size();
+  } else {
+    update_intern_strings = true;
+  }
 
   // For debug builds, always run the code below to get coverage.
-  if (kIsDebugBuild || !intern_remap.empty()) {
+  if (update_intern_strings) {
     // Slow path case is when there are conflicting intern strings to fix up.
-    UpdateInternStrings(space, intern_remap);
+    UpdateInternStrings(space, /*use_preresolved_strings=*/ load_startup_cache, intern_remap);
   }
 }
 
@@ -3727,32 +3761,7 @@
     }
   }
   if (initialize_oat_file_data) {
-    // Initialize the .data.bimg.rel.ro section.
-    if (!oat_file->GetBootImageRelocations().empty()) {
-      uint8_t* reloc_begin = const_cast<uint8_t*>(oat_file->DataBimgRelRoBegin());
-      CheckedCall(mprotect,
-                  "un-protect boot image relocations",
-                  reloc_begin,
-                  oat_file->DataBimgRelRoSize(),
-                  PROT_READ | PROT_WRITE);
-      uint32_t boot_image_begin = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(
-          Runtime::Current()->GetHeap()->GetBootImageSpaces().front()->Begin()));
-      for (const uint32_t& relocation : oat_file->GetBootImageRelocations()) {
-        const_cast<uint32_t&>(relocation) += boot_image_begin;
-      }
-      CheckedCall(mprotect,
-                  "protect boot image relocations",
-                  reloc_begin,
-                  oat_file->DataBimgRelRoSize(),
-                  PROT_READ);
-    }
-
-    // Initialize the .bss section.
-    // TODO: Pre-initialize from boot/app image?
-    ArtMethod* resolution_method = Runtime::Current()->GetResolutionMethod();
-    for (ArtMethod*& entry : oat_file->GetBssMethods()) {
-      entry = resolution_method;
-    }
+    oat_file->InitializeRelocations();
   }
   jweak dex_cache_jweak = vm->AddWeakGlobalRef(self, dex_cache);
   dex_cache->SetDexFile(&dex_file);
@@ -7040,11 +7049,13 @@
   return FindSameNameAndSignature(cmp, rest...);
 }
 
+namespace {
+
 // Check that all vtable entries are present in this class's virtuals or are the same as a
 // superclasses vtable entry.
-static void CheckClassOwnsVTableEntries(Thread* self,
-                                        Handle<mirror::Class> klass,
-                                        PointerSize pointer_size)
+void CheckClassOwnsVTableEntries(Thread* self,
+                                 Handle<mirror::Class> klass,
+                                 PointerSize pointer_size)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   StackHandleScope<2> hs(self);
   Handle<mirror::PointerArray> check_vtable(hs.NewHandle(klass->GetVTableDuringLinking()));
@@ -7074,47 +7085,185 @@
 
 // Check to make sure the vtable does not have duplicates. Duplicates could cause problems when a
 // method is overridden in a subclass.
-static void CheckVTableHasNoDuplicates(Thread* self,
-                                       Handle<mirror::Class> klass,
-                                       PointerSize pointer_size)
+template <PointerSize kPointerSize>
+void CheckVTableHasNoDuplicates(Thread* self, Handle<mirror::Class> klass)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   StackHandleScope<1> hs(self);
   Handle<mirror::PointerArray> vtable(hs.NewHandle(klass->GetVTableDuringLinking()));
   int32_t num_entries = vtable->GetLength();
-  for (int32_t i = 0; i < num_entries; i++) {
-    ArtMethod* vtable_entry = vtable->GetElementPtrSize<ArtMethod*>(i, pointer_size);
-    // Don't bother if we cannot 'see' the vtable entry (i.e. it is a package-private member maybe).
+
+  // Observations:
+  //   * The older implementation was O(n^2) and got too expensive for apps with larger classes.
+  //   * Many classes do not override Object functions (e.g., equals/hashCode/toString). Thus,
+  //     for many classes outside of libcore a cross-dexfile check has to be run anyways.
+  //   * In the cross-dexfile case, with the O(n^2), in the best case O(n) cross checks would have
+  //     to be done. It is thus OK in a single-pass algorithm to read all data, anyways.
+  //   * The single-pass algorithm will trade memory for speed, but that is OK.
+
+  CHECK_GT(num_entries, 0);
+
+  auto log_fn = [&vtable, &klass](int32_t i, int32_t j) REQUIRES_SHARED(Locks::mutator_lock_) {
+    ArtMethod* m1 = vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(i);
+    ArtMethod* m2 = vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(j);
+    LOG(WARNING) << "vtable entries " << i << " and " << j << " are identical for "
+                 << klass->PrettyClass() << " in method " << m1->PrettyMethod()
+                << " (0x" << std::hex << reinterpret_cast<uintptr_t>(m2) << ") and "
+                << m2->PrettyMethod() << "  (0x" << std::hex
+                << reinterpret_cast<uintptr_t>(m2) << ")";
+  };
+  struct BaseHashType {
+    static size_t HashCombine(size_t seed, size_t val) {
+      return seed ^ (val + 0x9e3779b9 + (seed << 6) + (seed >> 2));
+    }
+  };
+
+  // Check assuming all entries come from the same dex file.
+  {
+    // Find the first interesting method and its dex file.
+    int32_t start = 0;
+    for (; start < num_entries; ++start) {
+      ArtMethod* vtable_entry = vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(start);
+      // Don't bother if we cannot 'see' the vtable entry (i.e. it is a package-private member
+      // maybe).
+      if (!klass->CanAccessMember(vtable_entry->GetDeclaringClass(),
+                                  vtable_entry->GetAccessFlags())) {
+        continue;
+      }
+      break;
+    }
+    if (start == num_entries) {
+      return;
+    }
+    const DexFile* dex_file =
+        vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(start)->
+            GetInterfaceMethodIfProxy(kPointerSize)->GetDexFile();
+
+    // Helper function to avoid logging if we have to run the cross-file checks.
+    auto check_fn = [&](bool log_warn) REQUIRES_SHARED(Locks::mutator_lock_) {
+      // Use a map to store seen entries, as the storage space is too large for a bitvector.
+      using PairType = std::pair<uint32_t, uint16_t>;
+      struct PairHash : BaseHashType {
+        size_t operator()(const PairType& key) const {
+          return BaseHashType::HashCombine(BaseHashType::HashCombine(0, key.first), key.second);
+        }
+      };
+      std::unordered_map<PairType, int32_t, PairHash> seen;
+      seen.reserve(2 * num_entries);
+      bool need_slow_path = false;
+      bool found_dup = false;
+      for (int i = start; i < num_entries; ++i) {
+        // Can use Unchecked here as the start loop already ensured that the arrays are correct
+        // wrt/ kPointerSize.
+        ArtMethod* vtable_entry = vtable->GetElementPtrSizeUnchecked<ArtMethod*, kPointerSize>(i);
+        if (!klass->CanAccessMember(vtable_entry->GetDeclaringClass(),
+                                    vtable_entry->GetAccessFlags())) {
+          continue;
+        }
+        ArtMethod* m = vtable_entry->GetInterfaceMethodIfProxy(kPointerSize);
+        if (dex_file != m->GetDexFile()) {
+          need_slow_path = true;
+          break;
+        }
+        const dex::MethodId* m_mid = &dex_file->GetMethodId(m->GetDexMethodIndex());
+        PairType pair = std::make_pair(m_mid->name_idx_.index_, m_mid->proto_idx_.index_);
+        auto it = seen.find(pair);
+        if (it != seen.end()) {
+          found_dup = true;
+          if (log_warn) {
+            log_fn(it->second, i);
+          }
+        } else {
+          seen.emplace(pair, i);
+        }
+      }
+      return std::make_pair(need_slow_path, found_dup);
+    };
+    std::pair<bool, bool> result = check_fn(/* log_warn= */ false);
+    if (!result.first) {
+      if (result.second) {
+        check_fn(/* log_warn= */ true);
+      }
+      return;
+    }
+  }
+
+  // Need to check across dex files.
+  struct Entry {
+    size_t cached_hash = 0;
+    const char* name = nullptr;
+    Signature signature = Signature::NoSignature();
+    uint32_t name_len = 0;
+
+    Entry(const DexFile* dex_file, const dex::MethodId& mid)
+        : name(dex_file->StringDataAndUtf16LengthByIdx(mid.name_idx_, &name_len)),
+          signature(dex_file->GetMethodSignature(mid)) {
+    }
+
+    bool operator==(const Entry& other) const {
+      if (name_len != other.name_len || strcmp(name, other.name) != 0) {
+        return false;
+      }
+      return signature == other.signature;
+    }
+  };
+  struct EntryHash {
+    size_t operator()(const Entry& key) const {
+      return key.cached_hash;
+    }
+  };
+  std::unordered_map<Entry, int32_t, EntryHash> map;
+  for (int32_t i = 0; i < num_entries; ++i) {
+    // Can use Unchecked here as the first loop already ensured that the arrays are correct
+    // wrt/ kPointerSize.
+    ArtMethod* vtable_entry = vtable->GetElementPtrSizeUnchecked<ArtMethod*, kPointerSize>(i);
+    // Don't bother if we cannot 'see' the vtable entry (i.e. it is a package-private member
+    // maybe).
     if (!klass->CanAccessMember(vtable_entry->GetDeclaringClass(),
                                 vtable_entry->GetAccessFlags())) {
       continue;
     }
-    MethodNameAndSignatureComparator name_comparator(
-        vtable_entry->GetInterfaceMethodIfProxy(pointer_size));
-    for (int32_t j = i + 1; j < num_entries; j++) {
-      ArtMethod* other_entry = vtable->GetElementPtrSize<ArtMethod*>(j, pointer_size);
-      if (!klass->CanAccessMember(other_entry->GetDeclaringClass(),
-                                  other_entry->GetAccessFlags())) {
-        continue;
-      }
-      if (vtable_entry == other_entry ||
-          name_comparator.HasSameNameAndSignature(
-               other_entry->GetInterfaceMethodIfProxy(pointer_size))) {
-        LOG(WARNING) << "vtable entries " << i << " and " << j << " are identical for "
-                     << klass->PrettyClass() << " in method " << vtable_entry->PrettyMethod()
-                     << " (0x" << std::hex << reinterpret_cast<uintptr_t>(vtable_entry) << ") and "
-                     << other_entry->PrettyMethod() << "  (0x" << std::hex
-                     << reinterpret_cast<uintptr_t>(other_entry) << ")";
-      }
+    ArtMethod* m = vtable_entry->GetInterfaceMethodIfProxy(kPointerSize);
+    const DexFile* dex_file = m->GetDexFile();
+    const dex::MethodId& mid = dex_file->GetMethodId(m->GetDexMethodIndex());
+
+    Entry e(dex_file, mid);
+
+    size_t string_hash = std::hash<std::string_view>()(std::string_view(e.name, e.name_len));
+    size_t sig_hash = std::hash<std::string>()(e.signature.ToString());
+    e.cached_hash = BaseHashType::HashCombine(BaseHashType::HashCombine(0u, string_hash),
+                                              sig_hash);
+
+    auto it = map.find(e);
+    if (it != map.end()) {
+      log_fn(it->second, i);
+    } else {
+      map.emplace(e, i);
     }
   }
 }
 
+void CheckVTableHasNoDuplicates(Thread* self,
+                                Handle<mirror::Class> klass,
+                                PointerSize pointer_size)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  switch (pointer_size) {
+    case PointerSize::k64:
+      CheckVTableHasNoDuplicates<PointerSize::k64>(self, klass);
+      break;
+    case PointerSize::k32:
+      CheckVTableHasNoDuplicates<PointerSize::k32>(self, klass);
+      break;
+  }
+}
+
 static void SanityCheckVTable(Thread* self, Handle<mirror::Class> klass, PointerSize pointer_size)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   CheckClassOwnsVTableEntries(self, klass, pointer_size);
   CheckVTableHasNoDuplicates(self, klass, pointer_size);
 }
 
+}  // namespace
+
 void ClassLinker::FillImtFromSuperClass(Handle<mirror::Class> klass,
                                         ArtMethod* unimplemented_method,
                                         ArtMethod* imt_conflict_method,
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 2f37123..91cea79 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -18,6 +18,7 @@
 
 #include <memory>
 #include <string>
+#include <string_view>
 
 #include "android-base/strings.h"
 
@@ -408,7 +409,7 @@
 
   void AssertDexFileClass(ObjPtr<mirror::ClassLoader> class_loader, const std::string& descriptor)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    ASSERT_TRUE(descriptor != nullptr);
+    ASSERT_FALSE(descriptor.empty());
     Thread* self = Thread::Current();
     StackHandleScope<1> hs(self);
     Handle<mirror::Class> klass(
@@ -509,7 +510,7 @@
 
     for (size_t i = 0; i < offsets.size(); i++) {
       ArtField* field = is_static ? klass->GetStaticField(i) : klass->GetInstanceField(i);
-      StringPiece field_name(field->GetName());
+      std::string_view field_name(field->GetName());
       if (field_name != offsets[i].java_name) {
         error = true;
       }
@@ -518,7 +519,7 @@
       for (size_t i = 0; i < offsets.size(); i++) {
         CheckOffset& offset = offsets[i];
         ArtField* field = is_static ? klass->GetStaticField(i) : klass->GetInstanceField(i);
-        StringPiece field_name(field->GetName());
+        std::string_view field_name(field->GetName());
         if (field_name != offsets[i].java_name) {
           LOG(ERROR) << "JAVA FIELD ORDER MISMATCH NEXT LINE:";
         }
@@ -1254,7 +1255,7 @@
   EXPECT_TRUE(K->IsAssignableFrom(B.Get()));
   EXPECT_TRUE(J->IsAssignableFrom(B.Get()));
 
-  const Signature void_sig = I->GetDexCache()->GetDexFile()->CreateSignature("()V");
+  const std::string_view void_sig("()V");
   ArtMethod* Ii = I->FindClassMethod("i", void_sig, kRuntimePointerSize);
   ArtMethod* Jj1 = J->FindClassMethod("j1", void_sig, kRuntimePointerSize);
   ArtMethod* Jj2 = J->FindClassMethod("j2", void_sig, kRuntimePointerSize);
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 62788b1..499bf28 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -390,8 +390,10 @@
 
 // NoSuchFieldError
 
-void ThrowNoSuchFieldError(const StringPiece& scope, ObjPtr<mirror::Class> c,
-                           const StringPiece& type, const StringPiece& name) {
+void ThrowNoSuchFieldError(std::string_view scope,
+                           ObjPtr<mirror::Class> c,
+                           std::string_view type,
+                           std::string_view name) {
   std::ostringstream msg;
   std::string temp;
   msg << "No " << scope << "field " << name << " of type " << type
@@ -399,7 +401,7 @@
   ThrowException("Ljava/lang/NoSuchFieldError;", c, msg.str().c_str());
 }
 
-void ThrowNoSuchFieldException(ObjPtr<mirror::Class> c, const StringPiece& name) {
+void ThrowNoSuchFieldException(ObjPtr<mirror::Class> c, std::string_view name) {
   std::ostringstream msg;
   std::string temp;
   msg << "No field " << name << " in class " << c->GetDescriptor(&temp);
@@ -408,7 +410,9 @@
 
 // NoSuchMethodError
 
-void ThrowNoSuchMethodError(InvokeType type, ObjPtr<mirror::Class> c, const StringPiece& name,
+void ThrowNoSuchMethodError(InvokeType type,
+                            ObjPtr<mirror::Class> c,
+                            std::string_view name,
                             const Signature& signature) {
   std::ostringstream msg;
   std::string temp;
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index ca9c96a..c167d1b 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -17,6 +17,8 @@
 #ifndef ART_RUNTIME_COMMON_THROWS_H_
 #define ART_RUNTIME_COMMON_THROWS_H_
 
+#include <string_view>
+
 #include "base/locks.h"
 #include "obj_ptr.h"
 
@@ -31,7 +33,6 @@
 class DexFile;
 enum InvokeType : uint32_t;
 class Signature;
-class StringPiece;
 
 // AbstractMethodError
 
@@ -196,20 +197,20 @@
 
 // NoSuchFieldError
 
-void ThrowNoSuchFieldError(const StringPiece& scope,
+void ThrowNoSuchFieldError(std::string_view scope,
                            ObjPtr<mirror::Class> c,
-                           const StringPiece& type,
-                           const StringPiece& name)
+                           std::string_view type,
+                           std::string_view name)
     REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
 
-void ThrowNoSuchFieldException(ObjPtr<mirror::Class> c, const StringPiece& name)
+void ThrowNoSuchFieldException(ObjPtr<mirror::Class> c, std::string_view name)
     REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
 
 // NoSuchMethodError
 
 void ThrowNoSuchMethodError(InvokeType type,
                             ObjPtr<mirror::Class> c,
-                            const StringPiece& name,
+                            std::string_view name,
                             const Signature& signature)
     REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
 
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 8020f86..8335799 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -2637,7 +2637,7 @@
 }
 
 void Heap::TraceHeapSize(size_t heap_size) {
-  ATRACE_INT("Heap size (KB)", heap_size / KB);
+  ATraceIntegerValue("Heap size (KB)", heap_size / KB);
 }
 
 size_t Heap::GetNativeBytes() {
@@ -2772,13 +2772,6 @@
   } else {
     LOG(FATAL) << "Invalid current allocator " << current_allocator_;
   }
-  if (IsGcConcurrent()) {
-    // Disable concurrent GC check so that we don't have spammy JNI requests.
-    // This gets recalculated in GrowForUtilization. It is important that it is disabled /
-    // calculated in the same thread so that there aren't any races that can cause it to become
-    // permanantly disabled. b/17942071
-    concurrent_start_bytes_ = std::numeric_limits<size_t>::max();
-  }
 
   CHECK(collector != nullptr)
       << "Could not find garbage collector with collector_type="
@@ -3662,14 +3655,15 @@
 
     // If the throughput of the current sticky GC >= throughput of the non sticky collector, then
     // do another sticky collection next.
-    // We also check that the bytes allocated aren't over the footprint limit in order to prevent a
+    // We also check that the bytes allocated aren't over the target_footprint, or
+    // concurrent_start_bytes in case of concurrent GCs, in order to prevent a
     // pathological case where dead objects which aren't reclaimed by sticky could get accumulated
     // if the sticky GC throughput always remained >= the full/partial throughput.
     size_t target_footprint = target_footprint_.load(std::memory_order_relaxed);
     if (current_gc_iteration_.GetEstimatedThroughput() * sticky_gc_throughput_adjustment >=
         non_sticky_collector->GetEstimatedMeanThroughput() &&
         non_sticky_collector->NumberOfIterations() > 0 &&
-        bytes_allocated <= target_footprint) {
+        bytes_allocated <= (IsGcConcurrent() ? concurrent_start_bytes_ : target_footprint)) {
       next_gc_type_ = collector::kGcTypeSticky;
     } else {
       next_gc_type_ = non_sticky_gc_type;
@@ -4005,7 +3999,7 @@
 // Return the ratio of the weighted native + java allocated bytes to its target value.
 // A return value > 1.0 means we should collect. Significantly larger values mean we're falling
 // behind.
-inline float Heap::NativeMemoryOverTarget(size_t current_native_bytes) {
+inline float Heap::NativeMemoryOverTarget(size_t current_native_bytes, bool is_gc_concurrent) {
   // Collection check for native allocation. Does not enforce Java heap bounds.
   // With adj_start_bytes defined below, effectively checks
   // <java bytes allocd> + c1*<old native allocd> + c2*<new native allocd) >= adj_start_bytes,
@@ -4022,17 +4016,22 @@
         + old_native_bytes / kOldNativeDiscountFactor;
     size_t add_bytes_allowed = static_cast<size_t>(
         NativeAllocationGcWatermark() * HeapGrowthMultiplier());
-    size_t adj_start_bytes = concurrent_start_bytes_ + add_bytes_allowed / kNewNativeDiscountFactor;
+    size_t java_gc_start_bytes = is_gc_concurrent
+        ? concurrent_start_bytes_
+        : target_footprint_.load(std::memory_order_relaxed);
+    size_t adj_start_bytes = UnsignedSum(java_gc_start_bytes,
+                                         add_bytes_allowed / kNewNativeDiscountFactor);
     return static_cast<float>(GetBytesAllocated() + weighted_native_bytes)
          / static_cast<float>(adj_start_bytes);
   }
 }
 
-inline void Heap::CheckConcurrentGCForNative(Thread* self) {
+inline void Heap::CheckGCForNative(Thread* self) {
+  bool is_gc_concurrent = IsGcConcurrent();
   size_t current_native_bytes = GetNativeBytes();
-  float gc_urgency = NativeMemoryOverTarget(current_native_bytes);
+  float gc_urgency = NativeMemoryOverTarget(current_native_bytes, is_gc_concurrent);
   if (UNLIKELY(gc_urgency >= 1.0)) {
-    if (IsGcConcurrent()) {
+    if (is_gc_concurrent) {
       RequestConcurrentGC(self, kGcCauseForNativeAlloc, /*force_full=*/true);
       if (gc_urgency > kStopForNativeFactor
           && current_native_bytes > kHugeNativeAllocs) {
@@ -4051,7 +4050,7 @@
 // About kNotifyNativeInterval allocations have occurred. Check whether we should garbage collect.
 void Heap::NotifyNativeAllocations(JNIEnv* env) {
   native_objects_notified_.fetch_add(kNotifyNativeInterval, std::memory_order_relaxed);
-  CheckConcurrentGCForNative(ThreadForEnv(env));
+  CheckGCForNative(ThreadForEnv(env));
 }
 
 // Register a native allocation with an explicit size.
@@ -4063,7 +4062,7 @@
       native_objects_notified_.fetch_add(1, std::memory_order_relaxed);
   if (objects_notified % kNotifyNativeInterval == kNotifyNativeInterval - 1
       || bytes > kCheckImmediatelyThreshold) {
-    CheckConcurrentGCForNative(ThreadForEnv(env));
+    CheckGCForNative(ThreadForEnv(env));
   }
 }
 
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 6bdba12..18dfbf5 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -917,9 +917,13 @@
     return main_space_backup_ != nullptr;
   }
 
+  // Size_t saturating arithmetic
   static ALWAYS_INLINE size_t UnsignedDifference(size_t x, size_t y) {
     return x > y ? x - y : 0;
   }
+  static ALWAYS_INLINE size_t UnsignedSum(size_t x, size_t y) {
+    return x + y >= x ? x + y : std::numeric_limits<size_t>::max();
+  }
 
   static ALWAYS_INLINE bool AllocatorHasAllocationStack(AllocatorType allocator_type) {
     return
@@ -950,13 +954,13 @@
 
   // Checks whether we should garbage collect:
   ALWAYS_INLINE bool ShouldConcurrentGCForJava(size_t new_num_bytes_allocated);
-  float NativeMemoryOverTarget(size_t current_native_bytes);
+  float NativeMemoryOverTarget(size_t current_native_bytes, bool is_gc_concurrent);
   ALWAYS_INLINE void CheckConcurrentGCForJava(Thread* self,
                                               size_t new_num_bytes_allocated,
                                               ObjPtr<mirror::Object>* obj)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!*pending_task_lock_, !*gc_complete_lock_);
-  void CheckConcurrentGCForNative(Thread* self)
+  void CheckGCForNative(Thread* self)
       REQUIRES(!*pending_task_lock_, !*gc_complete_lock_);
 
   accounting::ObjectStack* GetMarkStack() {
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index e15e9f3..0bdb5c8 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -354,7 +354,7 @@
 // This function might print warnings into the log if the member is hidden.
 template<typename T>
 inline bool ShouldDenyAccessToMember(T* member,
-                                     std::function<AccessContext()> fn_get_access_context,
+                                     const std::function<AccessContext()>& fn_get_access_context,
                                      AccessMethod access_method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   DCHECK(member != nullptr);
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 2e41a9d..aa11562 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -17,6 +17,7 @@
 #include "interpreter.h"
 
 #include <limits>
+#include <string_view>
 
 #include "common_dex_operations.h"
 #include "common_throws.h"
@@ -46,7 +47,7 @@
 
 static void InterpreterJni(Thread* self,
                            ArtMethod* method,
-                           const StringPiece& shorty,
+                           std::string_view shorty,
                            ObjPtr<mirror::Object> receiver,
                            uint32_t* args,
                            JValue* result)
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 72a8727..7a1b7eb 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -181,17 +181,21 @@
   return param->AsString();
 }
 
+static std::function<hiddenapi::AccessContext()> GetHiddenapiAccessContextFunction(
+    ShadowFrame* frame) {
+  return [=]() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return hiddenapi::AccessContext(frame->GetMethod()->GetDeclaringClass());
+  };
+}
+
 template<typename T>
 static ALWAYS_INLINE bool ShouldDenyAccessToMember(T* member, ShadowFrame* frame)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   // All uses in this file are from reflection
   constexpr hiddenapi::AccessMethod kAccessMethod = hiddenapi::AccessMethod::kReflection;
-  return hiddenapi::ShouldDenyAccessToMember(
-      member,
-      [&]() REQUIRES_SHARED(Locks::mutator_lock_) {
-        return hiddenapi::AccessContext(frame->GetMethod()->GetDeclaringClass());
-      },
-      kAccessMethod);
+  return hiddenapi::ShouldDenyAccessToMember(member,
+                                             GetHiddenapiAccessContextFunction(frame),
+                                             kAccessMethod);
 }
 
 void UnstartedRuntime::UnstartedClassForNameCommon(Thread* self,
@@ -390,22 +394,23 @@
   Runtime* runtime = Runtime::Current();
   bool transaction = runtime->IsActiveTransaction();
   PointerSize pointer_size = runtime->GetClassLinker()->GetImagePointerSize();
+  auto fn_hiddenapi_access_context = GetHiddenapiAccessContextFunction(shadow_frame);
   ObjPtr<mirror::Method> method;
   if (transaction) {
     if (pointer_size == PointerSize::k64) {
       method = mirror::Class::GetDeclaredMethodInternal<PointerSize::k64, true>(
-          self, klass, name, args);
+          self, klass, name, args, fn_hiddenapi_access_context);
     } else {
       method = mirror::Class::GetDeclaredMethodInternal<PointerSize::k32, true>(
-          self, klass, name, args);
+          self, klass, name, args, fn_hiddenapi_access_context);
     }
   } else {
     if (pointer_size == PointerSize::k64) {
       method = mirror::Class::GetDeclaredMethodInternal<PointerSize::k64, false>(
-          self, klass, name, args);
+          self, klass, name, args, fn_hiddenapi_access_context);
     } else {
       method = mirror::Class::GetDeclaredMethodInternal<PointerSize::k32, false>(
-          self, klass, name, args);
+          self, klass, name, args, fn_hiddenapi_access_context);
     }
   }
   if (method != nullptr && ShouldDenyAccessToMember(method->GetArtMethod(), shadow_frame)) {
diff --git a/runtime/jni/java_vm_ext.cc b/runtime/jni/java_vm_ext.cc
index e54b807..df96d28 100644
--- a/runtime/jni/java_vm_ext.cc
+++ b/runtime/jni/java_vm_ext.cc
@@ -17,6 +17,7 @@
 #include "java_vm_ext.h"
 
 #include <dlfcn.h>
+#include <string_view>
 
 #include "android-base/stringprintf.h"
 
@@ -25,6 +26,7 @@
 #include "base/mutex-inl.h"
 #include "base/sdk_version.h"
 #include "base/stl_util.h"
+#include "base/string_view_cpp20.h"
 #include "base/systrace.h"
 #include "check_jni.h"
 #include "dex/dex_file-inl.h"
@@ -566,8 +568,8 @@
     return false;
   }
   // Perform checks based on class name.
-  StringPiece class_name(method->GetDeclaringClassDescriptor());
-  if (!trace_.empty() && class_name.find(trace_) != std::string::npos) {
+  std::string_view class_name(method->GetDeclaringClassDescriptor());
+  if (!trace_.empty() && class_name.find(trace_) != std::string_view::npos) {
     return true;
   }
   if (!VLOG_IS_ON(third_party_jni)) {
@@ -575,7 +577,7 @@
   }
   // Return true if we're trying to log all third-party JNI activity and 'method' doesn't look
   // like part of Android.
-  static const char* gBuiltInPrefixes[] = {
+  static const char* const gBuiltInPrefixes[] = {
       "Landroid/",
       "Lcom/android/",
       "Lcom/google/android/",
@@ -586,7 +588,7 @@
       "Lorg/apache/harmony/",
   };
   for (size_t i = 0; i < arraysize(gBuiltInPrefixes); ++i) {
-    if (class_name.starts_with(gBuiltInPrefixes[i])) {
+    if (StartsWith(class_name, gBuiltInPrefixes[i])) {
       return false;
     }
   }
diff --git a/runtime/jni/jni_internal.cc b/runtime/jni/jni_internal.cc
index af86cc0..7c0db30 100644
--- a/runtime/jni/jni_internal.cc
+++ b/runtime/jni/jni_internal.cc
@@ -442,8 +442,8 @@
 
 template <bool kNative>
 static ArtMethod* FindMethod(ObjPtr<mirror::Class> c,
-                             const StringPiece& name,
-                             const StringPiece& sig)
+                             std::string_view name,
+                             std::string_view sig)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
   for (auto& method : c->GetMethods(pointer_size)) {
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index a6a5ba2..d9d88e1 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -224,15 +224,31 @@
   }
 }
 
-template<typename T, VerifyObjectFlags kVerifyFlags>
-inline T PointerArray::GetElementPtrSize(uint32_t idx, PointerSize ptr_size) {
+template<typename T, PointerSize kPointerSize, VerifyObjectFlags kVerifyFlags>
+inline T PointerArray::GetElementPtrSize(uint32_t idx) {
   // C style casts here since we sometimes have T be a pointer, or sometimes an integer
   // (for stack traces).
-  if (ptr_size == PointerSize::k64) {
+  if (kPointerSize == PointerSize::k64) {
     return (T)static_cast<uintptr_t>(AsLongArray<kVerifyFlags>()->GetWithoutChecks(idx));
   }
   return (T)static_cast<uintptr_t>(AsIntArray<kVerifyFlags>()->GetWithoutChecks(idx));
 }
+template<typename T, PointerSize kPointerSize, VerifyObjectFlags kVerifyFlags>
+inline T PointerArray::GetElementPtrSizeUnchecked(uint32_t idx) {
+  // C style casts here since we sometimes have T be a pointer, or sometimes an integer
+  // (for stack traces).
+  if (kPointerSize == PointerSize::k64) {
+    return (T)static_cast<uintptr_t>(AsLongArrayUnchecked<kVerifyFlags>()->GetWithoutChecks(idx));
+  }
+  return (T)static_cast<uintptr_t>(AsIntArrayUnchecked<kVerifyFlags>()->GetWithoutChecks(idx));
+}
+template<typename T, VerifyObjectFlags kVerifyFlags>
+inline T PointerArray::GetElementPtrSize(uint32_t idx, PointerSize ptr_size) {
+  if (ptr_size == PointerSize::k64) {
+    return GetElementPtrSize<T, PointerSize::k64, kVerifyFlags>(idx);
+  }
+  return GetElementPtrSize<T, PointerSize::k32, kVerifyFlags>(idx);
+}
 
 template<bool kTransactionActive, bool kUnchecked>
 inline void PointerArray::SetElementPtrSize(uint32_t idx, uint64_t element, PointerSize ptr_size) {
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 8816c61..2e894d5 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -32,6 +32,8 @@
 
 class MANAGED Array : public Object {
  public:
+  static constexpr size_t kFirstElementOffset = 12u;
+
   // The size of a java.lang.Class representing an array.
   static uint32_t ClassSize(PointerSize pointer_size);
 
@@ -79,6 +81,17 @@
         << "Array data offset isn't aligned with component size";
     return MemberOffset(data_offset);
   }
+  template <size_t kComponentSize>
+  static constexpr MemberOffset DataOffset() {
+    static_assert(IsPowerOfTwo(kComponentSize), "Invalid component size");
+    constexpr size_t data_offset = RoundUp(kFirstElementOffset, kComponentSize);
+    static_assert(RoundUp(data_offset, kComponentSize) == data_offset, "RoundUp fail");
+    return MemberOffset(data_offset);
+  }
+
+  static constexpr size_t FirstElementOffset() {
+    return OFFSETOF_MEMBER(Array, first_element_);
+  }
 
   void* GetRawData(size_t component_size, int32_t index)
       REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -86,12 +99,24 @@
         + (index * component_size);
     return reinterpret_cast<void*>(data);
   }
+  template <size_t kComponentSize>
+  void* GetRawData(int32_t index) REQUIRES_SHARED(Locks::mutator_lock_) {
+    intptr_t data = reinterpret_cast<intptr_t>(this) + DataOffset<kComponentSize>().Int32Value() +
+        + (index * kComponentSize);
+    return reinterpret_cast<void*>(data);
+  }
 
   const void* GetRawData(size_t component_size, int32_t index) const {
     intptr_t data = reinterpret_cast<intptr_t>(this) + DataOffset(component_size).Int32Value() +
         + (index * component_size);
     return reinterpret_cast<void*>(data);
   }
+  template <size_t kComponentSize>
+  const void* GetRawData(int32_t index) const {
+    intptr_t data = reinterpret_cast<intptr_t>(this) + DataOffset<kComponentSize>().Int32Value() +
+        + (index * kComponentSize);
+    return reinterpret_cast<void*>(data);
+  }
 
   // Returns true if the index is valid. If not, throws an ArrayIndexOutOfBoundsException and
   // returns false.
@@ -132,11 +157,11 @@
 
 
   const T* GetData() const ALWAYS_INLINE  REQUIRES_SHARED(Locks::mutator_lock_) {
-    return reinterpret_cast<const T*>(GetRawData(sizeof(T), 0));
+    return reinterpret_cast<const T*>(GetRawData<sizeof(T)>(0));
   }
 
   T* GetData() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
-    return reinterpret_cast<T*>(GetRawData(sizeof(T), 0));
+    return reinterpret_cast<T*>(GetRawData<sizeof(T)>(0));
   }
 
   T Get(int32_t i) ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_);
@@ -196,6 +221,15 @@
   template<typename T, VerifyObjectFlags kVerifyFlags = kVerifyNone>
   T GetElementPtrSize(uint32_t idx, PointerSize ptr_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
+  template<typename T, PointerSize kPtrSize, VerifyObjectFlags kVerifyFlags = kVerifyNone>
+  T GetElementPtrSize(uint32_t idx)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+  // Same as GetElementPtrSize, but uses unchecked version of array conversion. It is thus not
+  // checked whether kPtrSize matches the underlying array. Only use after at least one invocation
+  // of GetElementPtrSize!
+  template<typename T, PointerSize kPtrSize, VerifyObjectFlags kVerifyFlags = kVerifyNone>
+  T GetElementPtrSizeUnchecked(uint32_t idx)
+      REQUIRES_SHARED(Locks::mutator_lock_);
 
   template<VerifyObjectFlags kVerifyFlags = kVerifyNone>
   void** ElementAddress(size_t index, PointerSize ptr_size) REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 53c9cc7..872095a 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -35,6 +35,7 @@
 #include "gc/accounting/card_table-inl.h"
 #include "gc/heap-inl.h"
 #include "handle_scope-inl.h"
+#include "hidden_api.h"
 #include "subtype_check.h"
 #include "method.h"
 #include "object-inl.h"
@@ -388,14 +389,14 @@
                     new_reference_offsets);
 }
 
-bool Class::IsInSamePackage(const StringPiece& descriptor1, const StringPiece& descriptor2) {
+bool Class::IsInSamePackage(std::string_view descriptor1, std::string_view descriptor2) {
   size_t i = 0;
   size_t min_length = std::min(descriptor1.size(), descriptor2.size());
   while (i < min_length && descriptor1[i] == descriptor2[i]) {
     ++i;
   }
-  if (descriptor1.find('/', i) != StringPiece::npos ||
-      descriptor2.find('/', i) != StringPiece::npos) {
+  if (descriptor1.find('/', i) != std::string_view::npos ||
+      descriptor2.find('/', i) != std::string_view::npos) {
     return false;
   } else {
     return true;
@@ -434,7 +435,7 @@
 
 template <typename SignatureType>
 static inline ArtMethod* FindInterfaceMethodWithSignature(ObjPtr<Class> klass,
-                                                          const StringPiece& name,
+                                                          std::string_view name,
                                                           const SignatureType& signature,
                                                           PointerSize pointer_size)
     REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -477,13 +478,13 @@
   return nullptr;
 }
 
-ArtMethod* Class::FindInterfaceMethod(const StringPiece& name,
-                                      const StringPiece& signature,
+ArtMethod* Class::FindInterfaceMethod(std::string_view name,
+                                      std::string_view signature,
                                       PointerSize pointer_size) {
   return FindInterfaceMethodWithSignature(this, name, signature, pointer_size);
 }
 
-ArtMethod* Class::FindInterfaceMethod(const StringPiece& name,
+ArtMethod* Class::FindInterfaceMethod(std::string_view name,
                                       const Signature& signature,
                                       PointerSize pointer_size) {
   return FindInterfaceMethodWithSignature(this, name, signature, pointer_size);
@@ -495,7 +496,7 @@
   // We always search by name and signature, ignoring the type index in the MethodId.
   const DexFile& dex_file = *dex_cache->GetDexFile();
   const dex::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
-  StringPiece name = dex_file.StringDataByIdx(method_id.name_idx_);
+  std::string_view name = dex_file.StringDataByIdx(method_id.name_idx_);
   const Signature signature = dex_file.GetMethodSignature(method_id);
   return FindInterfaceMethod(name, signature, pointer_size);
 }
@@ -536,7 +537,7 @@
 
 template <typename SignatureType>
 static inline ArtMethod* FindClassMethodWithSignature(ObjPtr<Class> this_klass,
-                                                      const StringPiece& name,
+                                                      std::string_view name,
                                                       const SignatureType& signature,
                                                       PointerSize pointer_size)
     REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -590,13 +591,13 @@
 }
 
 
-ArtMethod* Class::FindClassMethod(const StringPiece& name,
-                                  const StringPiece& signature,
+ArtMethod* Class::FindClassMethod(std::string_view name,
+                                  std::string_view signature,
                                   PointerSize pointer_size) {
   return FindClassMethodWithSignature(this, name, signature, pointer_size);
 }
 
-ArtMethod* Class::FindClassMethod(const StringPiece& name,
+ArtMethod* Class::FindClassMethod(std::string_view name,
                                   const Signature& signature,
                                   PointerSize pointer_size) {
   return FindClassMethodWithSignature(this, name, signature, pointer_size);
@@ -623,7 +624,7 @@
   const DexFile& dex_file = *dex_cache->GetDexFile();
   const dex::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
   const Signature signature = dex_file.GetMethodSignature(method_id);
-  StringPiece name;  // Delay strlen() until actually needed.
+  std::string_view name;  // Delay strlen() until actually needed.
   // If we do not have a dex_cache match, try to find the declared method in this class now.
   if (this_dex_cache != dex_cache && !GetDeclaredMethodsSlice(pointer_size).empty()) {
     DCHECK(name.empty());
@@ -700,10 +701,10 @@
   return uninherited_method;  // Return the `uninherited_method` if any.
 }
 
-ArtMethod* Class::FindConstructor(const StringPiece& signature, PointerSize pointer_size) {
+ArtMethod* Class::FindConstructor(std::string_view signature, PointerSize pointer_size) {
   // Internal helper, never called on proxy classes. We can skip GetInterfaceMethodIfProxy().
   DCHECK(!IsProxyClass());
-  StringPiece name("<init>");
+  std::string_view name("<init>");
   for (ArtMethod& method : GetDirectMethodsSliceUnchecked(pointer_size)) {
     if (method.GetName() == name && method.GetSignature() == signature) {
       return &method;
@@ -712,8 +713,7 @@
   return nullptr;
 }
 
-ArtMethod* Class::FindDeclaredDirectMethodByName(const StringPiece& name,
-                                                 PointerSize pointer_size) {
+ArtMethod* Class::FindDeclaredDirectMethodByName(std::string_view name, PointerSize pointer_size) {
   for (auto& method : GetDirectMethods(pointer_size)) {
     ArtMethod* const np_method = method.GetInterfaceMethodIfProxy(pointer_size);
     if (name == np_method->GetName()) {
@@ -723,8 +723,7 @@
   return nullptr;
 }
 
-ArtMethod* Class::FindDeclaredVirtualMethodByName(const StringPiece& name,
-                                                  PointerSize pointer_size) {
+ArtMethod* Class::FindDeclaredVirtualMethodByName(std::string_view name, PointerSize pointer_size) {
   for (auto& method : GetVirtualMethods(pointer_size)) {
     ArtMethod* const np_method = method.GetInterfaceMethodIfProxy(pointer_size);
     if (name == np_method->GetName()) {
@@ -812,8 +811,8 @@
 
 // Custom binary search to avoid double comparisons from std::binary_search.
 static ArtField* FindFieldByNameAndType(LengthPrefixedArray<ArtField>* fields,
-                                        const StringPiece& name,
-                                        const StringPiece& type)
+                                        std::string_view name,
+                                        std::string_view type)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   if (fields == nullptr) {
     return nullptr;
@@ -826,9 +825,12 @@
     ArtField& field = fields->At(mid);
     // Fields are sorted by class, then name, then type descriptor. This is verified in dex file
     // verifier. There can be multiple fields with the same in the same class name due to proguard.
-    int result = StringPiece(field.GetName()).Compare(name);
+    // Note: std::string_view::compare() uses lexicographical comparison and treats the `char` as
+    // unsigned; for modified-UTF-8 without embedded nulls this is consistent with the
+    // CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues() ordering.
+    int result = std::string_view(field.GetName()).compare(name);
     if (result == 0) {
-      result = StringPiece(field.GetTypeDescriptor()).Compare(type);
+      result = std::string_view(field.GetTypeDescriptor()).compare(type);
     }
     if (result < 0) {
       low = mid + 1;
@@ -852,7 +854,7 @@
   return ret;
 }
 
-ArtField* Class::FindDeclaredInstanceField(const StringPiece& name, const StringPiece& type) {
+ArtField* Class::FindDeclaredInstanceField(std::string_view name, std::string_view type) {
   // Binary search by name. Interfaces are not relevant because they can't contain instance fields.
   return FindFieldByNameAndType(GetIFieldsPtr(), name, type);
 }
@@ -868,7 +870,7 @@
   return nullptr;
 }
 
-ArtField* Class::FindInstanceField(const StringPiece& name, const StringPiece& type) {
+ArtField* Class::FindInstanceField(std::string_view name, std::string_view type) {
   // Is the field in this class, or any of its superclasses?
   // Interfaces are not relevant because they can't contain instance fields.
   for (ObjPtr<Class> c = this; c != nullptr; c = c->GetSuperClass()) {
@@ -892,8 +894,8 @@
   return nullptr;
 }
 
-ArtField* Class::FindDeclaredStaticField(const StringPiece& name, const StringPiece& type) {
-  DCHECK(type != nullptr);
+ArtField* Class::FindDeclaredStaticField(std::string_view name, std::string_view type) {
+  DCHECK(!type.empty());
   return FindFieldByNameAndType(GetSFieldsPtr(), name, type);
 }
 
@@ -910,8 +912,8 @@
 
 ArtField* Class::FindStaticField(Thread* self,
                                  ObjPtr<Class> klass,
-                                 const StringPiece& name,
-                                 const StringPiece& type) {
+                                 std::string_view name,
+                                 std::string_view type) {
   // Is the field in this class (or its interfaces), or any of its
   // superclasses (or their interfaces)?
   for (ObjPtr<Class> k = klass; k != nullptr; k = k->GetSuperClass()) {
@@ -961,8 +963,8 @@
 
 ArtField* Class::FindField(Thread* self,
                            ObjPtr<Class> klass,
-                           const StringPiece& name,
-                           const StringPiece& type) {
+                           std::string_view name,
+                           std::string_view type) {
   // Find a field using the JLS field resolution order
   for (ObjPtr<Class> k = klass; k != nullptr; k = k->GetSuperClass()) {
     // Is the field in this class?
@@ -1252,18 +1254,50 @@
   return (type_id == nullptr) ? dex::TypeIndex() : dex_file.GetIndexForTypeId(*type_id);
 }
 
+ALWAYS_INLINE
+static bool IsMethodPreferredOver(ArtMethod* orig_method,
+                                  bool orig_method_hidden,
+                                  ArtMethod* new_method,
+                                  bool new_method_hidden) {
+  DCHECK(new_method != nullptr);
+
+  // Is this the first result?
+  if (orig_method == nullptr) {
+    return true;
+  }
+
+  // Original method is hidden, the new one is not?
+  if (orig_method_hidden && !new_method_hidden) {
+    return true;
+  }
+
+  // We iterate over virtual methods first and then over direct ones,
+  // so we can never be in situation where `orig_method` is direct and
+  // `new_method` is virtual.
+  DCHECK(!orig_method->IsDirect() || new_method->IsDirect());
+
+  // Original method is synthetic, the new one is not?
+  if (orig_method->IsSynthetic() && !new_method->IsSynthetic()) {
+    return true;
+  }
+
+  return false;
+}
+
 template <PointerSize kPointerSize, bool kTransactionActive>
 ObjPtr<Method> Class::GetDeclaredMethodInternal(
     Thread* self,
     ObjPtr<Class> klass,
     ObjPtr<String> name,
-    ObjPtr<ObjectArray<Class>> args) {
-  // Covariant return types permit the class to define multiple
-  // methods with the same name and parameter types. Prefer to
-  // return a non-synthetic method in such situations. We may
-  // still return a synthetic method to handle situations like
-  // escalated visibility. We never return miranda methods that
-  // were synthesized by the runtime.
+    ObjPtr<ObjectArray<Class>> args,
+    const std::function<hiddenapi::AccessContext()>& fn_get_access_context) {
+  // Covariant return types (or smali) permit the class to define
+  // multiple methods with the same name and parameter types.
+  // Prefer (in decreasing order of importance):
+  //  1) non-hidden method over hidden
+  //  2) virtual methods over direct
+  //  3) non-synthetic methods over synthetic
+  // We never return miranda methods that were synthesized by the runtime.
   StackHandleScope<3> hs(self);
   auto h_method_name = hs.NewHandle(name);
   if (UNLIKELY(h_method_name == nullptr)) {
@@ -1272,8 +1306,13 @@
   }
   auto h_args = hs.NewHandle(args);
   Handle<Class> h_klass = hs.NewHandle(klass);
+  constexpr hiddenapi::AccessMethod access_method = hiddenapi::AccessMethod::kNone;
   ArtMethod* result = nullptr;
+  bool result_hidden = false;
   for (auto& m : h_klass->GetDeclaredVirtualMethods(kPointerSize)) {
+    if (m.IsMiranda()) {
+      continue;
+    }
     auto* np_method = m.GetInterfaceMethodIfProxy(kPointerSize);
     // May cause thread suspension.
     ObjPtr<String> np_name = np_method->ResolveNameString();
@@ -1283,14 +1322,24 @@
       }
       continue;
     }
-    if (!m.IsMiranda()) {
-      if (!m.IsSynthetic()) {
-        return Method::CreateFromArtMethod<kPointerSize, kTransactionActive>(self, &m);
-      }
-      result = &m;  // Remember as potential result if it's not a miranda method.
+    bool m_hidden = hiddenapi::ShouldDenyAccessToMember(&m, fn_get_access_context, access_method);
+    if (!m_hidden && !m.IsSynthetic()) {
+      // Non-hidden, virtual, non-synthetic. Best possible result, exit early.
+      return Method::CreateFromArtMethod<kPointerSize, kTransactionActive>(self, &m);
+    } else if (IsMethodPreferredOver(result, result_hidden, &m, m_hidden)) {
+      // Remember as potential result.
+      result = &m;
+      result_hidden = m_hidden;
     }
   }
-  if (result == nullptr) {
+
+  if ((result != nullptr) && !result_hidden) {
+    // We have not found a non-hidden, virtual, non-synthetic method, but
+    // if we have found a non-hidden, virtual, synthetic method, we cannot
+    // do better than that later.
+    DCHECK(!result->IsDirect());
+    DCHECK(result->IsSynthetic());
+  } else {
     for (auto& m : h_klass->GetDirectMethods(kPointerSize)) {
       auto modifiers = m.GetAccessFlags();
       if ((modifiers & kAccConstructor) != 0) {
@@ -1310,12 +1359,20 @@
         continue;
       }
       DCHECK(!m.IsMiranda());  // Direct methods cannot be miranda methods.
-      if ((modifiers & kAccSynthetic) == 0) {
+      bool m_hidden = hiddenapi::ShouldDenyAccessToMember(&m, fn_get_access_context, access_method);
+      if (!m_hidden && !m.IsSynthetic()) {
+        // Non-hidden, direct, non-synthetic. Any virtual result could only have been
+        // hidden, therefore this is the best possible match. Exit now.
+        DCHECK((result == nullptr) || result_hidden);
         return Method::CreateFromArtMethod<kPointerSize, kTransactionActive>(self, &m);
+      } else if (IsMethodPreferredOver(result, result_hidden, &m, m_hidden)) {
+        // Remember as potential result.
+        result = &m;
+        result_hidden = m_hidden;
       }
-      result = &m;  // Remember as potential result.
     }
   }
+
   return result != nullptr
       ? Method::CreateFromArtMethod<kPointerSize, kTransactionActive>(self, result)
       : nullptr;
@@ -1326,25 +1383,29 @@
     Thread* self,
     ObjPtr<Class> klass,
     ObjPtr<String> name,
-    ObjPtr<ObjectArray<Class>> args);
+    ObjPtr<ObjectArray<Class>> args,
+    const std::function<hiddenapi::AccessContext()>& fn_get_access_context);
 template
 ObjPtr<Method> Class::GetDeclaredMethodInternal<PointerSize::k32, true>(
     Thread* self,
     ObjPtr<Class> klass,
     ObjPtr<String> name,
-    ObjPtr<ObjectArray<Class>> args);
+    ObjPtr<ObjectArray<Class>> args,
+    const std::function<hiddenapi::AccessContext()>& fn_get_access_context);
 template
 ObjPtr<Method> Class::GetDeclaredMethodInternal<PointerSize::k64, false>(
     Thread* self,
     ObjPtr<Class> klass,
     ObjPtr<String> name,
-    ObjPtr<ObjectArray<Class>> args);
+    ObjPtr<ObjectArray<Class>> args,
+    const std::function<hiddenapi::AccessContext()>& fn_get_access_context);
 template
 ObjPtr<Method> Class::GetDeclaredMethodInternal<PointerSize::k64, true>(
     Thread* self,
     ObjPtr<Class> klass,
     ObjPtr<String> name,
-    ObjPtr<ObjectArray<Class>> args);
+    ObjPtr<ObjectArray<Class>> args,
+    const std::function<hiddenapi::AccessContext()>& fn_get_access_context);
 
 template <PointerSize kPointerSize, bool kTransactionActive>
 ObjPtr<Constructor> Class::GetDeclaredConstructorInternal(
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 981ecf1..bde9b03 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -17,6 +17,8 @@
 #ifndef ART_RUNTIME_MIRROR_CLASS_H_
 #define ART_RUNTIME_MIRROR_CLASS_H_
 
+#include <string_view>
+
 #include "base/bit_utils.h"
 #include "base/casts.h"
 #include "base/stride_iterator.h"
@@ -37,6 +39,10 @@
 class TypeList;
 }  // namespace dex
 
+namespace hiddenapi {
+class AccessContext;
+}  // namespace hiddenapi
+
 template<typename T> class ArraySlice;
 class ArtField;
 class ArtMethod;
@@ -49,7 +55,6 @@
 template<typename T> class LengthPrefixedArray;
 enum class PointerSize : size_t;
 class Signature;
-class StringPiece;
 template<size_t kNumReferences> class PACKED(4) StackHandleScope;
 class Thread;
 
@@ -552,7 +557,7 @@
   // Returns true if this class is in the same packages as that class.
   bool IsInSamePackage(ObjPtr<Class> that) REQUIRES_SHARED(Locks::mutator_lock_);
 
-  static bool IsInSamePackage(const StringPiece& descriptor1, const StringPiece& descriptor2);
+  static bool IsInSamePackage(std::string_view descriptor1, std::string_view descriptor2);
 
   // Returns true if this class can access that class.
   bool CanAccess(ObjPtr<Class> that) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -702,10 +707,12 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   template <PointerSize kPointerSize, bool kTransactionActive>
-  static ObjPtr<Method> GetDeclaredMethodInternal(Thread* self,
-                                                  ObjPtr<Class> klass,
-                                                  ObjPtr<String> name,
-                                                  ObjPtr<ObjectArray<Class>> args)
+  static ObjPtr<Method> GetDeclaredMethodInternal(
+      Thread* self,
+      ObjPtr<Class> klass,
+      ObjPtr<String> name,
+      ObjPtr<ObjectArray<Class>> args,
+      const std::function<hiddenapi::AccessContext()>& fn_get_access_context)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   template <PointerSize kPointerSize, bool kTransactionActive>
@@ -855,12 +862,12 @@
   // in an interface without superinterfaces, see JLS 9.2, can be inherited, see JLS 9.4.1).
   // TODO: Implement search for a unique maximally-specific non-abstract superinterface method.
 
-  ArtMethod* FindInterfaceMethod(const StringPiece& name,
-                                 const StringPiece& signature,
+  ArtMethod* FindInterfaceMethod(std::string_view name,
+                                 std::string_view signature,
                                  PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  ArtMethod* FindInterfaceMethod(const StringPiece& name,
+  ArtMethod* FindInterfaceMethod(std::string_view name,
                                  const Signature& signature,
                                  PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
@@ -894,12 +901,12 @@
   // does not satisfy the request. Special consideration should be given to the case where this
   // function returns a method that's not inherited (found in step 2, returned in step 4).
 
-  ArtMethod* FindClassMethod(const StringPiece& name,
-                             const StringPiece& signature,
+  ArtMethod* FindClassMethod(std::string_view name,
+                             std::string_view signature,
                              PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  ArtMethod* FindClassMethod(const StringPiece& name,
+  ArtMethod* FindClassMethod(std::string_view name,
                              const Signature& signature,
                              PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
@@ -909,13 +916,13 @@
                              PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  ArtMethod* FindConstructor(const StringPiece& signature, PointerSize pointer_size)
+  ArtMethod* FindConstructor(std::string_view signature, PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  ArtMethod* FindDeclaredVirtualMethodByName(const StringPiece& name, PointerSize pointer_size)
+  ArtMethod* FindDeclaredVirtualMethodByName(std::string_view name, PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  ArtMethod* FindDeclaredDirectMethodByName(const StringPiece& name, PointerSize pointer_size)
+  ArtMethod* FindDeclaredDirectMethodByName(std::string_view name, PointerSize pointer_size)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   ArtMethod* FindClassInitializer(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -1030,12 +1037,12 @@
   // Find a static or instance field using the JLS resolution order
   static ArtField* FindField(Thread* self,
                              ObjPtr<Class> klass,
-                             const StringPiece& name,
-                             const StringPiece& type)
+                             std::string_view name,
+                             std::string_view type)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Finds the given instance field in this class or a superclass.
-  ArtField* FindInstanceField(const StringPiece& name, const StringPiece& type)
+  ArtField* FindInstanceField(std::string_view name, std::string_view type)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Finds the given instance field in this class or a superclass, only searches classes that
@@ -1043,7 +1050,7 @@
   ArtField* FindInstanceField(ObjPtr<DexCache> dex_cache, uint32_t dex_field_idx)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  ArtField* FindDeclaredInstanceField(const StringPiece& name, const StringPiece& type)
+  ArtField* FindDeclaredInstanceField(std::string_view name, std::string_view type)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   ArtField* FindDeclaredInstanceField(ObjPtr<DexCache> dex_cache, uint32_t dex_field_idx)
@@ -1052,8 +1059,8 @@
   // Finds the given static field in this class or a superclass.
   static ArtField* FindStaticField(Thread* self,
                                    ObjPtr<Class> klass,
-                                   const StringPiece& name,
-                                   const StringPiece& type)
+                                   std::string_view name,
+                                   std::string_view type)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Finds the given static field in this class or superclass, only searches classes that
@@ -1064,7 +1071,7 @@
                                    uint32_t dex_field_idx)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  ArtField* FindDeclaredStaticField(const StringPiece& name, const StringPiece& type)
+  ArtField* FindDeclaredStaticField(std::string_view name, std::string_view type)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   ArtField* FindDeclaredStaticField(ObjPtr<DexCache> dex_cache, uint32_t dex_field_idx)
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 47b621a..f0ad931 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -110,8 +110,7 @@
   WriteBarrier::ForEveryFieldWrite(this);
 }
 
-inline void DexCache::SetPreResolvedString(dex::StringIndex string_idx,
-                                           ObjPtr<String> resolved) {
+inline void DexCache::SetPreResolvedString(dex::StringIndex string_idx, ObjPtr<String> resolved) {
   DCHECK(resolved != nullptr);
   DCHECK_LT(string_idx.index_, GetDexFile()->NumStringIds());
   GetPreResolvedStrings()[string_idx.index_] = GcRoot<mirror::String>(resolved);
@@ -122,6 +121,17 @@
   WriteBarrier::ForEveryFieldWrite(this);
 }
 
+inline void DexCache::ClearPreResolvedStrings() {
+  SetFieldPtr64</*kTransactionActive=*/false,
+                /*kCheckTransaction=*/false,
+                kVerifyNone,
+                GcRoot<mirror::String>*>(PreResolvedStringsOffset(), nullptr);
+  SetField32</*kTransactionActive=*/false,
+             /*bool kCheckTransaction=*/false,
+             kVerifyNone,
+             /*kIsVolatile=*/false>(NumPreResolvedStringsOffset(), 0);
+}
+
 inline void DexCache::ClearString(dex::StringIndex string_idx) {
   DCHECK(Runtime::Current()->IsAotCompiler());
   uint32_t slot_idx = StringSlotIndex(string_idx);
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index c742928..b5619f8 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -283,6 +283,11 @@
                             ObjPtr<mirror::String> resolved)
       ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Clear the preresolved string cache to prevent further usage. Not thread safe, so should only
+  // be called when the string cache is guaranteed to not be accessed.
+  void ClearPreResolvedStrings()
+      ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_);
+
   // Clear a string for a string_idx, used to undo string intern transactions to make sure
   // the string isn't kept live.
   void ClearString(dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 2c2ad9b..005e272 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -253,9 +253,13 @@
 }
 
 template<VerifyObjectFlags kVerifyFlags>
+inline IntArray* Object::AsIntArrayUnchecked() {
+  return down_cast<IntArray*>(this);
+}
+template<VerifyObjectFlags kVerifyFlags>
 inline IntArray* Object::AsIntArray() {
   DCHECK((IsIntArray<kVerifyFlags>()));
-  return down_cast<IntArray*>(this);
+  return AsIntArrayUnchecked<kVerifyFlags>();
 }
 
 template<VerifyObjectFlags kVerifyFlags>
@@ -264,9 +268,13 @@
 }
 
 template<VerifyObjectFlags kVerifyFlags>
+inline LongArray* Object::AsLongArrayUnchecked() {
+  return down_cast<LongArray*>(this);
+}
+template<VerifyObjectFlags kVerifyFlags>
 inline LongArray* Object::AsLongArray() {
   DCHECK((IsLongArray<kVerifyFlags>()));
-  return down_cast<LongArray*>(this);
+  return AsLongArrayUnchecked<kVerifyFlags>();
 }
 
 template<VerifyObjectFlags kVerifyFlags>
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index ba222f6..ca8867d 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -223,11 +223,15 @@
   bool IsIntArray() REQUIRES_SHARED(Locks::mutator_lock_);
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   IntArray* AsIntArray() REQUIRES_SHARED(Locks::mutator_lock_);
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  IntArray* AsIntArrayUnchecked() REQUIRES_SHARED(Locks::mutator_lock_);
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsLongArray() REQUIRES_SHARED(Locks::mutator_lock_);
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   LongArray* AsLongArray() REQUIRES_SHARED(Locks::mutator_lock_);
+  template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+  LongArray* AsLongArrayUnchecked() REQUIRES_SHARED(Locks::mutator_lock_);
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   bool IsFloatArray() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index f4b8ba5..27987c0 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -70,8 +70,6 @@
     ASSERT_EQ(string->IsValueNull(), false);
     // strlen is necessary because the 1-character string "\x00\x00" is interpreted as ""
     ASSERT_TRUE(string->Equals(utf8_in) || (expected_utf16_length == 1 && strlen(utf8_in) == 0));
-    ASSERT_TRUE(string->Equals(StringPiece(utf8_in)) ||
-                (expected_utf16_length == 1 && strlen(utf8_in) == 0));
     for (int32_t i = 0; i < expected_utf16_length; i++) {
       EXPECT_EQ(utf16_expected[i], string->CharAt(i));
     }
diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc
index 01315e7..bf99c37 100644
--- a/runtime/mirror/string.cc
+++ b/runtime/mirror/string.cc
@@ -234,19 +234,6 @@
   }
 }
 
-bool String::Equals(const uint16_t* that_chars, int32_t that_offset, int32_t that_length) {
-  if (this->GetLength() != that_length) {
-    return false;
-  } else {
-    for (int32_t i = 0; i < that_length; ++i) {
-      if (this->CharAt(i) != that_chars[that_offset + i]) {
-        return false;
-      }
-    }
-    return true;
-  }
-}
-
 bool String::Equals(const char* modified_utf8) {
   const int32_t length = GetLength();
   int32_t i = 0;
@@ -274,30 +261,6 @@
   return *modified_utf8 == '\0';
 }
 
-bool String::Equals(const StringPiece& modified_utf8) {
-  const int32_t length = GetLength();
-  const char* p = modified_utf8.data();
-  for (int32_t i = 0; i < length; ++i) {
-    uint32_t ch = GetUtf16FromUtf8(&p);
-
-    if (GetLeadingUtf16Char(ch) != CharAt(i)) {
-      return false;
-    }
-
-    const uint16_t trailing = GetTrailingUtf16Char(ch);
-    if (trailing != 0) {
-      if (i == (length - 1)) {
-        return false;
-      }
-
-      if (CharAt(++i) != trailing) {
-        return false;
-      }
-    }
-  }
-  return true;
-}
-
 // Create a modified UTF-8 encoded std::string from a java/lang/String object.
 std::string String::ToModifiedUtf8() {
   size_t byte_count = GetUtfLength();
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index c367957..4a7f4ae 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -27,7 +27,6 @@
 
 template<class T> class Handle;
 struct StringOffsets;
-class StringPiece;
 class StubTest_ReadBarrierForRoot_Test;
 
 namespace mirror {
@@ -154,27 +153,10 @@
   static String* AllocFromModifiedUtf8(Thread* self, int32_t utf16_length, const char* utf8_data_in)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
 
-  // TODO: This is only used in the interpreter to compare against
-  // entries from a dex files constant pool (ArtField names). Should
-  // we unify this with Equals(const StringPiece&); ?
   bool Equals(const char* modified_utf8) REQUIRES_SHARED(Locks::mutator_lock_);
 
-  // TODO: This is only used to compare DexCache.location with
-  // a dex_file's location (which is an std::string). Do we really
-  // need this in mirror::String just for that one usage ?
-  bool Equals(const StringPiece& modified_utf8)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
   bool Equals(ObjPtr<String> that) REQUIRES_SHARED(Locks::mutator_lock_);
 
-  // Compare UTF-16 code point values not in a locale-sensitive manner
-  int Compare(int32_t utf16_length, const char* utf8_data_in);
-
-  // TODO: do we need this overload? give it a more intention-revealing name.
-  bool Equals(const uint16_t* that_chars, int32_t that_offset,
-              int32_t that_length)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
   // Create a modified UTF-8 encoded std::string from a java/lang/String object.
   std::string ToModifiedUtf8() REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 7240357..6abc8d7 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -280,7 +280,7 @@
 // This function is inlined and just helps to not have the VLOG and ATRACE check at all the
 // potential tracing points.
 void Monitor::AtraceMonitorLock(Thread* self, mirror::Object* obj, bool is_wait) {
-  if (UNLIKELY(VLOG_IS_ON(systrace_lock_logging) && ATRACE_ENABLED())) {
+  if (UNLIKELY(VLOG_IS_ON(systrace_lock_logging) && ATraceEnabled())) {
     AtraceMonitorLockImpl(self, obj, is_wait);
   }
 }
@@ -338,12 +338,12 @@
       (obj == nullptr ? -1 : static_cast<int32_t>(reinterpret_cast<uintptr_t>(obj))),
       (filename != nullptr ? filename : "null"),
       line_number);
-  ATRACE_BEGIN(tmp.c_str());
+  ATraceBegin(tmp.c_str());
 }
 
 void Monitor::AtraceMonitorUnlock() {
   if (UNLIKELY(VLOG_IS_ON(systrace_lock_logging))) {
-    ATRACE_END();
+    ATraceEnd();
   }
 }
 
@@ -431,7 +431,7 @@
     // If systrace logging is enabled, first look at the lock owner. Acquiring the monitor's
     // lock and then re-acquiring the mutator lock can deadlock.
     bool started_trace = false;
-    if (ATRACE_ENABLED()) {
+    if (ATraceEnabled()) {
       if (owner_ != nullptr) {  // Did the owner_ give the lock up?
         std::ostringstream oss;
         std::string name;
@@ -450,7 +450,7 @@
         oss << " blocking from "
             << ArtMethod::PrettyMethod(m) << "(" << (filename != nullptr ? filename : "null")
             << ":" << line_number << ")";
-        ATRACE_BEGIN(oss.str().c_str());
+        ATraceBegin(oss.str().c_str());
         started_trace = true;
       }
     }
@@ -581,7 +581,7 @@
       }
     }
     if (started_trace) {
-      ATRACE_END();
+      ATraceEnd();
     }
     self->SetMonitorEnterObject(nullptr);
     monitor_lock_.Lock(self);  // Reacquire locks in order.
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 26fc5e9..891ecef 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -149,6 +149,7 @@
   HIDDEN_API_ENFORCEMENT_POLICY_MASK = (1 << 12)
                                      | (1 << 13),
   PROFILE_SYSTEM_SERVER              = 1 << 14,
+  USE_APP_IMAGE_STARTUP_CACHE        = 1 << 16,
 
   // bits to shift (flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) by to get a value
   // corresponding to hiddenapi::EnforcementPolicy
@@ -298,6 +299,10 @@
   bool profile_system_server = (runtime_flags & PROFILE_SYSTEM_SERVER) == PROFILE_SYSTEM_SERVER;
   runtime_flags &= ~PROFILE_SYSTEM_SERVER;
 
+  Runtime::Current()->SetLoadAppImageStartupCacheEnabled(
+      (runtime_flags & USE_APP_IMAGE_STARTUP_CACHE) != 0u);
+  runtime_flags &= ~USE_APP_IMAGE_STARTUP_CACHE;
+
   if (runtime_flags != 0) {
     LOG(ERROR) << StringPrintf("Unknown bits set in runtime_flags: %#x", runtime_flags);
   }
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 4115791..db62475 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -113,15 +113,18 @@
                          : hiddenapi::AccessContext(caller);
 }
 
+static std::function<hiddenapi::AccessContext()> GetHiddenapiAccessContextFunction(Thread* self) {
+  return [=]() REQUIRES_SHARED(Locks::mutator_lock_) { return GetReflectionCaller(self); };
+}
+
 // Returns true if the first non-ClassClass caller up the stack should not be
 // allowed access to `member`.
 template<typename T>
 ALWAYS_INLINE static bool ShouldDenyAccessToMember(T* member, Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  return hiddenapi::ShouldDenyAccessToMember(
-      member,
-      [&]() REQUIRES_SHARED(Locks::mutator_lock_) { return GetReflectionCaller(self); },
-      hiddenapi::AccessMethod::kReflection);
+  return hiddenapi::ShouldDenyAccessToMember(member,
+                                             GetHiddenapiAccessContextFunction(self),
+                                             hiddenapi::AccessMethod::kReflection);
 }
 
 // Returns true if a class member should be discoverable with reflection given
@@ -563,7 +566,8 @@
           soa.Self(),
           DecodeClass(soa, javaThis),
           soa.Decode<mirror::String>(name),
-          soa.Decode<mirror::ObjectArray<mirror::Class>>(args)));
+          soa.Decode<mirror::ObjectArray<mirror::Class>>(args),
+          GetHiddenapiAccessContextFunction(soa.Self())));
   if (result == nullptr || ShouldDenyAccessToMember(result->GetArtMethod(), soa.Self())) {
     return nullptr;
   }
diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc
index 00a0a39..a4425ce 100644
--- a/runtime/native_stack_dump.cc
+++ b/runtime/native_stack_dump.cc
@@ -50,7 +50,9 @@
 #include "base/os.h"
 #include "base/unix_file/fd_file.h"
 #include "base/utils.h"
+#include "class_linker.h"
 #include "oat_quick_method_header.h"
+#include "runtime.h"
 #include "thread-current-inl.h"
 
 #endif
@@ -287,11 +289,17 @@
 }
 
 static bool PcIsWithinQuickCode(ArtMethod* method, uintptr_t pc) NO_THREAD_SAFETY_ANALYSIS {
-  uintptr_t code = reinterpret_cast<uintptr_t>(EntryPointToCodePointer(
-      method->GetEntryPointFromQuickCompiledCode()));
-  if (code == 0) {
+  const void* entry_point = method->GetEntryPointFromQuickCompiledCode();
+  if (entry_point == nullptr) {
     return pc == 0;
   }
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  if (class_linker->IsQuickGenericJniStub(entry_point) ||
+      class_linker->IsQuickResolutionStub(entry_point) ||
+      class_linker->IsQuickToInterpreterBridge(entry_point)) {
+    return false;
+  }
+  uintptr_t code = reinterpret_cast<uintptr_t>(EntryPointToCodePointer(entry_point));
   uintptr_t code_size = reinterpret_cast<const OatQuickMethodHeader*>(code)[-1].GetCodeSize();
   return code <= pc && pc <= (code + code_size);
 }
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index d179b80..67803b6 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -58,13 +58,14 @@
 #include "elf_file.h"
 #include "elf_utils.h"
 #include "gc_root.h"
+#include "gc/heap.h"
 #include "gc/space/image_space.h"
 #include "mirror/class.h"
 #include "mirror/object-inl.h"
 #include "oat.h"
 #include "oat_file-inl.h"
 #include "oat_file_manager.h"
-#include "runtime.h"
+#include "runtime-inl.h"
 #include "vdex_file.h"
 
 namespace art {
@@ -447,34 +448,6 @@
   return true;
 }
 
-static void DCheckIndexToBssMapping(OatFile* oat_file,
-                                    uint32_t number_of_indexes,
-                                    size_t slot_size,
-                                    const IndexBssMapping* index_bss_mapping) {
-  if (kIsDebugBuild && index_bss_mapping != nullptr) {
-    size_t index_bits = IndexBssMappingEntry::IndexBits(number_of_indexes);
-    const IndexBssMappingEntry* prev_entry = nullptr;
-    for (const IndexBssMappingEntry& entry : *index_bss_mapping) {
-      CHECK_ALIGNED_PARAM(entry.bss_offset, slot_size);
-      // When loading a non-executable ElfOatFile, .bss symbols are not even
-      // looked up, so we cannot verify the offset against BssSize().
-      if (oat_file->IsExecutable()) {
-        CHECK_LT(entry.bss_offset, oat_file->BssSize());
-      }
-      uint32_t mask = entry.GetMask(index_bits);
-      CHECK_LE(POPCOUNT(mask) * slot_size, entry.bss_offset);
-      size_t index_mask_span = (mask != 0u) ? 32u - index_bits - CTZ(mask) : 0u;
-      CHECK_LE(index_mask_span, entry.GetIndex(index_bits));
-      if (prev_entry != nullptr) {
-        CHECK_LT(prev_entry->GetIndex(index_bits), entry.GetIndex(index_bits) - index_mask_span);
-      }
-      prev_entry = &entry;
-    }
-    CHECK(prev_entry != nullptr);
-    CHECK_LT(prev_entry->GetIndex(index_bits), number_of_indexes);
-  }
-}
-
 bool OatFileBase::Setup(int zip_fd, const char* abs_dex_location, std::string* error_msg) {
   if (!GetOatHeader().IsValid()) {
     std::string cause = GetOatHeader().GetValidationErrorMessage();
@@ -813,12 +786,6 @@
             this, &oat, i, dex_file_location, "string", &string_bss_mapping, error_msg)) {
       return false;
     }
-    DCheckIndexToBssMapping(
-        this, header->method_ids_size_, static_cast<size_t>(pointer_size), method_bss_mapping);
-    DCheckIndexToBssMapping(
-        this, header->type_ids_size_, sizeof(GcRoot<mirror::Class>), type_bss_mapping);
-    DCheckIndexToBssMapping(
-        this, header->string_ids_size_, sizeof(GcRoot<mirror::String>), string_bss_mapping);
 
     std::string canonical_location =
         DexFileLoader::GetDexCanonicalLocation(dex_file_name.c_str());
@@ -838,10 +805,10 @@
     oat_dex_files_storage_.push_back(oat_dex_file);
 
     // Add the location and canonical location (if different) to the oat_dex_files_ table.
-    StringPiece key(oat_dex_file->GetDexFileLocation());
+    std::string_view key(oat_dex_file->GetDexFileLocation());
     oat_dex_files_.Put(key, oat_dex_file);
     if (canonical_location != dex_file_location) {
-      StringPiece canonical_key(oat_dex_file->GetCanonicalDexFileLocation());
+      std::string_view canonical_key(oat_dex_file->GetCanonicalDexFileLocation());
       oat_dex_files_.Put(canonical_key, oat_dex_file);
     }
   }
@@ -1664,7 +1631,7 @@
   // without any performance loss, for example by not doing the first lock-free lookup.
 
   const OatDexFile* oat_dex_file = nullptr;
-  StringPiece key(dex_location);
+  std::string_view key(dex_location);
   // Try to find the key cheaply in the oat_dex_files_ map which holds dex locations
   // directly mentioned in the oat file and doesn't require locking.
   auto primary_it = oat_dex_files_.find(key);
@@ -1683,7 +1650,7 @@
       // We haven't seen this dex_location before, we must check the canonical location.
       std::string dex_canonical_location = DexFileLoader::GetDexCanonicalLocation(dex_location);
       if (dex_canonical_location != dex_location) {
-        StringPiece canonical_key(dex_canonical_location);
+        std::string_view canonical_key(dex_canonical_location);
         auto canonical_it = oat_dex_files_.find(canonical_key);
         if (canonical_it != oat_dex_files_.end()) {
           oat_dex_file = canonical_it->second;
@@ -1692,7 +1659,7 @@
 
       // Copy the key to the string_cache_ and store the result in secondary map.
       string_cache_.emplace_back(key.data(), key.length());
-      StringPiece key_copy(string_cache_.back());
+      std::string_view key_copy(string_cache_.back());
       secondary_oat_dex_files_.PutBefore(secondary_lb, key_copy, oat_dex_file);
     }
   }
@@ -2019,6 +1986,82 @@
   return oat_dex_file->GetOatClass(class_def_idx);
 }
 
+static void DCheckIndexToBssMapping(const OatFile* oat_file,
+                                    uint32_t number_of_indexes,
+                                    size_t slot_size,
+                                    const IndexBssMapping* index_bss_mapping) {
+  if (kIsDebugBuild && index_bss_mapping != nullptr) {
+    size_t index_bits = IndexBssMappingEntry::IndexBits(number_of_indexes);
+    const IndexBssMappingEntry* prev_entry = nullptr;
+    for (const IndexBssMappingEntry& entry : *index_bss_mapping) {
+      CHECK_ALIGNED_PARAM(entry.bss_offset, slot_size);
+      CHECK_LT(entry.bss_offset, oat_file->BssSize());
+      uint32_t mask = entry.GetMask(index_bits);
+      CHECK_LE(POPCOUNT(mask) * slot_size, entry.bss_offset);
+      size_t index_mask_span = (mask != 0u) ? 32u - index_bits - CTZ(mask) : 0u;
+      CHECK_LE(index_mask_span, entry.GetIndex(index_bits));
+      if (prev_entry != nullptr) {
+        CHECK_LT(prev_entry->GetIndex(index_bits), entry.GetIndex(index_bits) - index_mask_span);
+      }
+      prev_entry = &entry;
+    }
+    CHECK(prev_entry != nullptr);
+    CHECK_LT(prev_entry->GetIndex(index_bits), number_of_indexes);
+  }
+}
+
+void OatFile::InitializeRelocations() const {
+  DCHECK(IsExecutable());
+
+  // Initialize the .data.bimg.rel.ro section.
+  if (!GetBootImageRelocations().empty()) {
+    uint8_t* reloc_begin = const_cast<uint8_t*>(DataBimgRelRoBegin());
+    CheckedCall(mprotect,
+                "un-protect boot image relocations",
+                reloc_begin,
+                DataBimgRelRoSize(),
+                PROT_READ | PROT_WRITE);
+    uint32_t boot_image_begin = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(
+        Runtime::Current()->GetHeap()->GetBootImageSpaces().front()->Begin()));
+    for (const uint32_t& relocation : GetBootImageRelocations()) {
+      const_cast<uint32_t&>(relocation) += boot_image_begin;
+    }
+    CheckedCall(mprotect,
+                "protect boot image relocations",
+                reloc_begin,
+                DataBimgRelRoSize(),
+                PROT_READ);
+  }
+
+  // Before initializing .bss, check the .bss mappings in debug mode.
+  if (kIsDebugBuild) {
+    PointerSize pointer_size = GetInstructionSetPointerSize(GetOatHeader().GetInstructionSet());
+    for (const OatDexFile* odf : GetOatDexFiles()) {
+      const DexFile::Header* header =
+          reinterpret_cast<const DexFile::Header*>(odf->GetDexFilePointer());
+      DCheckIndexToBssMapping(this,
+                              header->method_ids_size_,
+                              static_cast<size_t>(pointer_size),
+                              odf->GetMethodBssMapping());
+      DCheckIndexToBssMapping(this,
+                              header->type_ids_size_,
+                              sizeof(GcRoot<mirror::Class>),
+                              odf->GetTypeBssMapping());
+      DCheckIndexToBssMapping(this,
+                              header->string_ids_size_,
+                              sizeof(GcRoot<mirror::String>),
+                              odf->GetStringBssMapping());
+    }
+  }
+
+  // Initialize the .bss section.
+  // TODO: Pre-initialize from boot/app image?
+  ArtMethod* resolution_method = Runtime::Current()->GetResolutionMethod();
+  for (ArtMethod*& entry : GetBssMethods()) {
+    entry = resolution_method;
+  }
+}
+
 void OatDexFile::AssertAotCompiler() {
   CHECK(Runtime::Current()->IsAotCompiler());
 }
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 1ba6e49..04b666c 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -19,13 +19,13 @@
 
 #include <list>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include "base/array_ref.h"
 #include "base/mutex.h"
 #include "base/os.h"
 #include "base/safe_map.h"
-#include "base/stringpiece.h"
 #include "base/tracking_safe_map.h"
 #include "class_status.h"
 #include "compiler_filter.h"
@@ -325,6 +325,9 @@
   ArrayRef<ArtMethod*> GetBssMethods() const;
   ArrayRef<GcRoot<mirror::Object>> GetBssGcRoots() const;
 
+  // Initialize relocation sections (.data.bimg.rel.ro and .bss).
+  void InitializeRelocations() const;
+
   // Returns the absolute dex location for the encoded relative dex location.
   //
   // If not null, abs_dex_location is used to resolve the absolute dex
@@ -398,12 +401,13 @@
   // Owning storage for the OatDexFile objects.
   std::vector<const OatDexFile*> oat_dex_files_storage_;
 
-  // NOTE: We use a StringPiece as the key type to avoid a memory allocation on every
-  // lookup with a const char* key. The StringPiece doesn't own its backing storage,
+  // NOTE: We use a std::string_view as the key type to avoid a memory allocation on every
+  // lookup with a const char* key. The std::string_view doesn't own its backing storage,
   // therefore we're using the OatDexFile::dex_file_location_ as the backing storage
   // for keys in oat_dex_files_ and the string_cache_ entries for the backing storage
   // of keys in secondary_oat_dex_files_ and oat_dex_files_by_canonical_location_.
-  typedef AllocationTrackingSafeMap<StringPiece, const OatDexFile*, kAllocatorTagOatFile> Table;
+  using Table =
+      AllocationTrackingSafeMap<std::string_view, const OatDexFile*, kAllocatorTagOatFile>;
 
   // Map each location and canonical location (if different) retrieved from the
   // oat file to its OatDexFile. This map doesn't change after it's constructed in Setup()
@@ -422,7 +426,7 @@
 
   // Cache of strings. Contains the backing storage for keys in the secondary_oat_dex_files_
   // and the lazily initialized oat_dex_files_by_canonical_location_.
-  // NOTE: We're keeping references to contained strings in form of StringPiece and adding
+  // NOTE: We're keeping references to contained strings in form of std::string_view and adding
   // new strings to the end. The adding of a new element must not touch any previously stored
   // elements. std::list<> and std::deque<> satisfy this requirement, std::vector<> doesn't.
   mutable std::list<std::string> string_cache_ GUARDED_BY(secondary_lookup_lock_);
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 6fd691f..f516d0d 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -24,7 +24,6 @@
 
 #include "base/file_utils.h"
 #include "base/macros.h"
-#include "base/stringpiece.h"
 #include "base/utils.h"
 #include "debugger.h"
 #include "gc/heap.h"
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index feade83..1465b14 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -199,6 +199,7 @@
 };
 
 namespace {
+
 #ifdef __APPLE__
 inline char** GetEnviron() {
   // When Google Test is built as a framework on MacOS X, the environ variable
@@ -212,6 +213,11 @@
 extern "C" char** environ;
 inline char** GetEnviron() { return environ; }
 #endif
+
+void CheckConstants() {
+  CHECK_EQ(mirror::Array::kFirstElementOffset, mirror::Array::FirstElementOffset());
+}
+
 }  // namespace
 
 Runtime::Runtime()
@@ -284,6 +290,7 @@
       verifier_logging_threshold_ms_(100) {
   static_assert(Runtime::kCalleeSaveSize ==
                     static_cast<uint32_t>(CalleeSaveType::kLastCalleeSaveType), "Unexpected size");
+  CheckConstants();
 
   std::fill(callee_save_methods_, callee_save_methods_ + arraysize(callee_save_methods_), 0u);
   interpreter::CheckInterpreterAsmConstants();
@@ -1173,8 +1180,8 @@
 
   compiler_executable_ = runtime_options.ReleaseOrDefault(Opt::Compiler);
   compiler_options_ = runtime_options.ReleaseOrDefault(Opt::CompilerOptions);
-  for (StringPiece option : Runtime::Current()->GetCompilerOptions()) {
-    if (option.starts_with("--debuggable")) {
+  for (const std::string& option : Runtime::Current()->GetCompilerOptions()) {
+    if (option == "--debuggable") {
       SetJavaDebuggable(true);
       break;
     }
@@ -2475,10 +2482,15 @@
   instruction_set += GetInstructionSetString(kRuntimeISA);
   argv->push_back(instruction_set);
 
-  std::unique_ptr<const InstructionSetFeatures> features(InstructionSetFeatures::FromCppDefines());
-  std::string feature_string("--instruction-set-features=");
-  feature_string += features->GetFeatureString();
-  argv->push_back(feature_string);
+  if (InstructionSetFeatures::IsRuntimeDetectionSupported()) {
+    argv->push_back("--instruction-set-features=runtime");
+  } else {
+    std::unique_ptr<const InstructionSetFeatures> features(
+        InstructionSetFeatures::FromCppDefines());
+    std::string feature_string("--instruction-set-features=");
+    feature_string += features->GetFeatureString();
+    argv->push_back(feature_string);
+  }
 }
 
 void Runtime::CreateJitCodeCache(bool rwx_memory_allowed) {
diff --git a/runtime/runtime.h b/runtime/runtime.h
index ee2c514..81c17a5 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -823,6 +823,14 @@
     ThreadPool* const thread_pool_;
   };
 
+  bool LoadAppImageStartupCache() const {
+    return load_app_image_startup_cache_;
+  }
+
+  void SetLoadAppImageStartupCacheEnabled(bool enabled) {
+    load_app_image_startup_cache_ = enabled;
+  }
+
  private:
   static void InitPlatformSignalHandlers();
 
@@ -1145,6 +1153,8 @@
 
   uint32_t verifier_logging_threshold_ms_;
 
+  bool load_app_image_startup_cache_ = false;
+
   // Note: See comments on GetFaultMessage.
   friend std::string GetFaultMessageForAbortLogging();
   friend class ScopedThreadPoolUsage;
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 309c04e..4828aae 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -16,10 +16,6 @@
 
 #include "thread.h"
 
-#if !defined(__APPLE__)
-#include <sched.h>
-#endif
-
 #include <pthread.h>
 #include <signal.h>
 #include <sys/resource.h>
@@ -95,6 +91,7 @@
 #include "oat_quick_method_header.h"
 #include "obj_ptr-inl.h"
 #include "object_lock.h"
+#include "palette/palette.h"
 #include "quick/quick_method_frame_info.h"
 #include "quick_exception_handler.h"
 #include "read_barrier-inl.h"
@@ -4233,6 +4230,7 @@
   Runtime::Current()->GetThreadList()->RunCheckpoint(&closure);
 }
 
+
 void Thread::ReleaseLongJumpContextInternal() {
   // Each QuickExceptionHandler gets a long jump context and uses
   // it for doing the long jump, after finding catch blocks/doing deoptimization.
@@ -4246,4 +4244,18 @@
   delete tlsPtr_.long_jump_context;
 }
 
+void Thread::SetNativePriority(int new_priority) {
+  // ART tests on JVM can reach this code path, use tid = 0 as shorthand for current thread.
+  PaletteStatus status = PaletteSchedSetPriority(0, new_priority);
+  CHECK(status == PaletteStatus::kOkay || status == PaletteStatus::kCheckErrno);
+}
+
+int Thread::GetNativePriority() {
+  int priority = 0;
+  // ART tests on JVM can reach this code path, use tid = 0 as shorthand for current thread.
+  PaletteStatus status = PaletteSchedGetPriority(0, &priority);
+  CHECK(status == PaletteStatus::kOkay || status == PaletteStatus::kCheckErrno);
+  return priority;
+}
+
 }  // namespace art
diff --git a/runtime/thread_android.cc b/runtime/thread_android.cc
index 24864f9..f333400 100644
--- a/runtime/thread_android.cc
+++ b/runtime/thread_android.cc
@@ -16,84 +16,8 @@
 
 #include "thread.h"
 
-#include <errno.h>
-#include <limits.h>
-#include <sys/resource.h>
-#include <sys/time.h>
-
-#include <processgroup/sched_policy.h>
-#include <utils/threads.h>
-
-#include "base/macros.h"
-
 namespace art {
 
-// Conversion map for "nice" values.
-//
-// We use Android thread priority constants to be consistent with the rest
-// of the system.  In some cases adjacent entries may overlap.
-//
-static const int kNiceValues[10] = {
-  ANDROID_PRIORITY_LOWEST,                // 1 (MIN_PRIORITY)
-  ANDROID_PRIORITY_BACKGROUND + 6,
-  ANDROID_PRIORITY_BACKGROUND + 3,
-  ANDROID_PRIORITY_BACKGROUND,
-  ANDROID_PRIORITY_NORMAL,                // 5 (NORM_PRIORITY)
-  ANDROID_PRIORITY_NORMAL - 2,
-  ANDROID_PRIORITY_NORMAL - 4,
-  ANDROID_PRIORITY_URGENT_DISPLAY + 3,
-  ANDROID_PRIORITY_URGENT_DISPLAY + 2,
-  ANDROID_PRIORITY_URGENT_DISPLAY         // 10 (MAX_PRIORITY)
-};
-
-void Thread::SetNativePriority(int newPriority) {
-  if (newPriority < 1 || newPriority > 10) {
-    LOG(WARNING) << "bad priority " << newPriority;
-    newPriority = 5;
-  }
-
-  int newNice = kNiceValues[newPriority-1];
-  pid_t tid = GetTid();
-
-  // TODO: b/18249098 The code below is broken. It uses getpriority() as a proxy for whether a
-  // thread is already in the SP_FOREGROUND cgroup. This is not necessarily true for background
-  // processes, where all threads are in the SP_BACKGROUND cgroup. This means that callers will
-  // have to call setPriority twice to do what they want :
-  //
-  //     Thread.setPriority(Thread.MIN_PRIORITY);  // no-op wrt to cgroups
-  //     Thread.setPriority(Thread.MAX_PRIORITY);  // will actually change cgroups.
-  if (newNice >= ANDROID_PRIORITY_BACKGROUND) {
-    set_sched_policy(tid, SP_BACKGROUND);
-  } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) {
-    set_sched_policy(tid, SP_FOREGROUND);
-  }
-
-  if (setpriority(PRIO_PROCESS, tid, newNice) != 0) {
-    PLOG(INFO) << *this << " setPriority(PRIO_PROCESS, " << tid << ", " << newNice << ") failed";
-  }
-}
-
-int Thread::GetNativePriority() {
-  errno = 0;
-  int native_priority = getpriority(PRIO_PROCESS, 0);
-  if (native_priority == -1 && errno != 0) {
-    PLOG(WARNING) << "getpriority failed";
-    return kNormThreadPriority;
-  }
-
-  int managed_priority = kMinThreadPriority;
-  for (size_t i = 0; i < arraysize(kNiceValues); i++) {
-    if (native_priority >= kNiceValues[i]) {
-      break;
-    }
-    managed_priority++;
-  }
-  if (managed_priority > kMaxThreadPriority) {
-    managed_priority = kMaxThreadPriority;
-  }
-  return managed_priority;
-}
-
 void Thread::SetUpAlternateSignalStack() {
   // Bionic does this for us.
 }
diff --git a/runtime/thread_linux.cc b/runtime/thread_linux.cc
index d05fecf..3ed4276 100644
--- a/runtime/thread_linux.cc
+++ b/runtime/thread_linux.cc
@@ -23,14 +23,6 @@
 
 namespace art {
 
-void Thread::SetNativePriority(int) {
-  // Do nothing.
-}
-
-int Thread::GetNativePriority() {
-  return kNormThreadPriority;
-}
-
 static void SigAltStack(stack_t* new_stack, stack_t* old_stack) {
   if (sigaltstack(new_stack, old_stack) == -1) {
     PLOG(FATAL) << "sigaltstack failed";
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 1b3b037..a5406ea 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -683,7 +683,7 @@
       AssertThreadsAreSuspended(self, self);
     }
   }
-  ATRACE_BEGIN((std::string("Mutator threads suspended for ") + cause).c_str());
+  ATraceBegin((std::string("Mutator threads suspended for ") + cause).c_str());
 
   if (self != nullptr) {
     VLOG(threads) << *self << " SuspendAll complete";
@@ -811,7 +811,7 @@
     VLOG(threads) << "Thread[null] ResumeAll starting";
   }
 
-  ATRACE_END();
+  ATraceEnd();
 
   ScopedTrace trace("Resuming mutator threads");
 
@@ -855,8 +855,8 @@
 }
 
 bool ThreadList::Resume(Thread* thread, SuspendReason reason) {
-  // This assumes there was an ATRACE_BEGIN when we suspended the thread.
-  ATRACE_END();
+  // This assumes there was an ATraceBegin when we suspended the thread.
+  ATraceEnd();
 
   Thread* self = Thread::Current();
   DCHECK_NE(thread, self);
@@ -987,10 +987,10 @@
         // done.
         if (thread->IsSuspended()) {
           VLOG(threads) << "SuspendThreadByPeer thread suspended: " << *thread;
-          if (ATRACE_ENABLED()) {
+          if (ATraceEnabled()) {
             std::string name;
             thread->GetThreadName(name);
-            ATRACE_BEGIN(StringPrintf("SuspendThreadByPeer suspended %s for peer=%p", name.c_str(),
+            ATraceBegin(StringPrintf("SuspendThreadByPeer suspended %s for peer=%p", name.c_str(),
                                       peer).c_str());
           }
           return thread;
@@ -1097,10 +1097,10 @@
         // count, or else we've waited and it has self suspended) or is the current thread, we're
         // done.
         if (thread->IsSuspended()) {
-          if (ATRACE_ENABLED()) {
+          if (ATraceEnabled()) {
             std::string name;
             thread->GetThreadName(name);
-            ATRACE_BEGIN(StringPrintf("SuspendThreadByThreadId suspended %s id=%d",
+            ATraceBegin(StringPrintf("SuspendThreadByThreadId suspended %s id=%d",
                                       name.c_str(), thread_id).c_str());
           }
           VLOG(threads) << "SuspendThreadByThreadId thread suspended: " << *thread;
diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc
index 36890a6..09171a4 100644
--- a/runtime/verifier/method_verifier_test.cc
+++ b/runtime/verifier/method_verifier_test.cc
@@ -35,7 +35,7 @@
  protected:
   void VerifyClass(const std::string& descriptor)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    ASSERT_TRUE(descriptor != nullptr);
+    ASSERT_FALSE(descriptor.empty());
     Thread* self = Thread::Current();
     ObjPtr<mirror::Class> klass = class_linker_->FindSystemClass(self, descriptor.c_str());
 
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index 91be00d..150d35c 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -55,18 +55,22 @@
 const NullType* NullType::instance_ = nullptr;
 
 PrimitiveType::PrimitiveType(ObjPtr<mirror::Class> klass,
-                             const StringPiece& descriptor,
+                             const std::string_view& descriptor,
                              uint16_t cache_id)
     : RegType(klass, descriptor, cache_id) {
   CHECK(klass != nullptr);
   CHECK(!descriptor.empty());
 }
 
-Cat1Type::Cat1Type(ObjPtr<mirror::Class> klass, const StringPiece& descriptor, uint16_t cache_id)
+Cat1Type::Cat1Type(ObjPtr<mirror::Class> klass,
+                   const std::string_view& descriptor,
+                   uint16_t cache_id)
     : PrimitiveType(klass, descriptor, cache_id) {
 }
 
-Cat2Type::Cat2Type(ObjPtr<mirror::Class> klass, const StringPiece& descriptor, uint16_t cache_id)
+Cat2Type::Cat2Type(ObjPtr<mirror::Class> klass,
+                   const std::string_view& descriptor,
+                   uint16_t cache_id)
     : PrimitiveType(klass, descriptor, cache_id) {
 }
 
@@ -132,7 +136,7 @@
 }
 
 const DoubleHiType* DoubleHiType::CreateInstance(ObjPtr<mirror::Class> klass,
-                                                 const StringPiece& descriptor,
+                                                 const std::string_view& descriptor,
                                                  uint16_t cache_id) {
   CHECK(instance_ == nullptr);
   instance_ = new DoubleHiType(klass, descriptor, cache_id);
@@ -147,7 +151,7 @@
 }
 
 const DoubleLoType* DoubleLoType::CreateInstance(ObjPtr<mirror::Class> klass,
-                                                 const StringPiece& descriptor,
+                                                 const std::string_view& descriptor,
                                                  uint16_t cache_id) {
   CHECK(instance_ == nullptr);
   instance_ = new DoubleLoType(klass, descriptor, cache_id);
@@ -162,7 +166,7 @@
 }
 
 const LongLoType* LongLoType::CreateInstance(ObjPtr<mirror::Class> klass,
-                                             const StringPiece& descriptor,
+                                             const std::string_view& descriptor,
                                              uint16_t cache_id) {
   CHECK(instance_ == nullptr);
   instance_ = new LongLoType(klass, descriptor, cache_id);
@@ -170,7 +174,7 @@
 }
 
 const LongHiType* LongHiType::CreateInstance(ObjPtr<mirror::Class> klass,
-                                             const StringPiece& descriptor,
+                                             const std::string_view& descriptor,
                                              uint16_t cache_id) {
   CHECK(instance_ == nullptr);
   instance_ = new LongHiType(klass, descriptor, cache_id);
@@ -192,7 +196,7 @@
 }
 
 const FloatType* FloatType::CreateInstance(ObjPtr<mirror::Class> klass,
-                                           const StringPiece& descriptor,
+                                           const std::string_view& descriptor,
                                            uint16_t cache_id) {
   CHECK(instance_ == nullptr);
   instance_ = new FloatType(klass, descriptor, cache_id);
@@ -207,7 +211,7 @@
 }
 
 const CharType* CharType::CreateInstance(ObjPtr<mirror::Class> klass,
-                                         const StringPiece& descriptor,
+                                         const std::string_view& descriptor,
                                          uint16_t cache_id) {
   CHECK(instance_ == nullptr);
   instance_ = new CharType(klass, descriptor, cache_id);
@@ -222,7 +226,7 @@
 }
 
 const ShortType* ShortType::CreateInstance(ObjPtr<mirror::Class> klass,
-                                           const StringPiece& descriptor,
+                                           const std::string_view& descriptor,
                                            uint16_t cache_id) {
   CHECK(instance_ == nullptr);
   instance_ = new ShortType(klass, descriptor, cache_id);
@@ -237,7 +241,7 @@
 }
 
 const ByteType* ByteType::CreateInstance(ObjPtr<mirror::Class> klass,
-                                         const StringPiece& descriptor,
+                                         const std::string_view& descriptor,
                                          uint16_t cache_id) {
   CHECK(instance_ == nullptr);
   instance_ = new ByteType(klass, descriptor, cache_id);
@@ -252,7 +256,7 @@
 }
 
 const IntegerType* IntegerType::CreateInstance(ObjPtr<mirror::Class> klass,
-                                               const StringPiece& descriptor,
+                                               const std::string_view& descriptor,
                                                uint16_t cache_id) {
   CHECK(instance_ == nullptr);
   instance_ = new IntegerType(klass, descriptor, cache_id);
@@ -267,7 +271,7 @@
 }
 
 const ConflictType* ConflictType::CreateInstance(ObjPtr<mirror::Class> klass,
-                                                 const StringPiece& descriptor,
+                                                 const std::string_view& descriptor,
                                                  uint16_t cache_id) {
   CHECK(instance_ == nullptr);
   instance_ = new ConflictType(klass, descriptor, cache_id);
@@ -282,7 +286,7 @@
 }
 
 const BooleanType* BooleanType::CreateInstance(ObjPtr<mirror::Class> klass,
-                                               const StringPiece& descriptor,
+                                               const std::string_view& descriptor,
                                                uint16_t cache_id) {
   CHECK(BooleanType::instance_ == nullptr);
   instance_ = new BooleanType(klass, descriptor, cache_id);
@@ -301,7 +305,7 @@
 }
 
 const UndefinedType* UndefinedType::CreateInstance(ObjPtr<mirror::Class> klass,
-                                                   const StringPiece& descriptor,
+                                                   const std::string_view& descriptor,
                                                    uint16_t cache_id) {
   CHECK(instance_ == nullptr);
   instance_ = new UndefinedType(klass, descriptor, cache_id);
@@ -316,7 +320,7 @@
 }
 
 PreciseReferenceType::PreciseReferenceType(ObjPtr<mirror::Class> klass,
-                                           const StringPiece& descriptor,
+                                           const std::string_view& descriptor,
                                            uint16_t cache_id)
     : RegType(klass, descriptor, cache_id) {
   // Note: no check for IsInstantiable() here. We may produce this in case an InstantiationError
@@ -352,47 +356,47 @@
 
 std::string UnresolvedReferenceType::Dump() const {
   std::stringstream result;
-  result << "Unresolved Reference" << ": " << PrettyDescriptor(GetDescriptor().as_string().c_str());
+  result << "Unresolved Reference: " << PrettyDescriptor(std::string(GetDescriptor()).c_str());
   return result.str();
 }
 
 std::string UnresolvedUninitializedRefType::Dump() const {
   std::stringstream result;
-  result << "Unresolved And Uninitialized Reference" << ": "
-      << PrettyDescriptor(GetDescriptor().as_string().c_str())
+  result << "Unresolved And Uninitialized Reference: "
+      << PrettyDescriptor(std::string(GetDescriptor()).c_str())
       << " Allocation PC: " << GetAllocationPc();
   return result.str();
 }
 
 std::string UnresolvedUninitializedThisRefType::Dump() const {
   std::stringstream result;
-  result << "Unresolved And Uninitialized This Reference"
-      << PrettyDescriptor(GetDescriptor().as_string().c_str());
+  result << "Unresolved And Uninitialized This Reference: "
+      << PrettyDescriptor(std::string(GetDescriptor()).c_str());
   return result.str();
 }
 
 std::string ReferenceType::Dump() const {
   std::stringstream result;
-  result << "Reference" << ": " << mirror::Class::PrettyDescriptor(GetClass());
+  result << "Reference: " << mirror::Class::PrettyDescriptor(GetClass());
   return result.str();
 }
 
 std::string PreciseReferenceType::Dump() const {
   std::stringstream result;
-  result << "Precise Reference" << ": "<< mirror::Class::PrettyDescriptor(GetClass());
+  result << "Precise Reference: " << mirror::Class::PrettyDescriptor(GetClass());
   return result.str();
 }
 
 std::string UninitializedReferenceType::Dump() const {
   std::stringstream result;
-  result << "Uninitialized Reference" << ": " << mirror::Class::PrettyDescriptor(GetClass());
+  result << "Uninitialized Reference: " << mirror::Class::PrettyDescriptor(GetClass());
   result << " Allocation PC: " << GetAllocationPc();
   return result.str();
 }
 
 std::string UninitializedThisReferenceType::Dump() const {
   std::stringstream result;
-  result << "Uninitialized This Reference" << ": " << mirror::Class::PrettyDescriptor(GetClass());
+  result << "Uninitialized This Reference: " << mirror::Class::PrettyDescriptor(GetClass());
   result << "Allocation PC: " << GetAllocationPc();
   return result.str();
 }
@@ -990,7 +994,7 @@
 }
 
 const NullType* NullType::CreateInstance(ObjPtr<mirror::Class> klass,
-                                         const StringPiece& descriptor,
+                                         const std::string_view& descriptor,
                                          uint16_t cache_id) {
   CHECK(instance_ == nullptr);
   instance_ = new NullType(klass, descriptor, cache_id);
diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h
index 3369784..56073db 100644
--- a/runtime/verifier/reg_type.h
+++ b/runtime/verifier/reg_type.h
@@ -21,12 +21,12 @@
 #include <limits>
 #include <set>
 #include <string>
+#include <string_view>
 
 #include "base/arena_object.h"
 #include "base/bit_vector.h"
 #include "base/locks.h"
 #include "base/macros.h"
-#include "base/stringpiece.h"
 #include "dex/primitive.h"
 #include "gc_root.h"
 #include "handle_scope.h"
@@ -185,7 +185,7 @@
   bool IsJavaLangObjectArray() const
       REQUIRES_SHARED(Locks::mutator_lock_);
   bool IsInstantiableTypes() const REQUIRES_SHARED(Locks::mutator_lock_);
-  const StringPiece& GetDescriptor() const {
+  const std::string_view& GetDescriptor() const {
     DCHECK(HasClass() ||
            (IsUnresolvedTypes() && !IsUnresolvedMergedReference() &&
             !IsUnresolvedSuperClass()));
@@ -319,7 +319,7 @@
 
  protected:
   RegType(ObjPtr<mirror::Class> klass,
-          const StringPiece& descriptor,
+          const std::string_view& descriptor,
           uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
       : descriptor_(descriptor),
         klass_(klass),
@@ -336,7 +336,7 @@
 
   virtual AssignmentType GetAssignmentTypeImpl() const = 0;
 
-  const StringPiece descriptor_;
+  const std::string_view descriptor_;
   mutable GcRoot<mirror::Class> klass_;  // Non-const only due to moving classes.
   const uint16_t cache_id_;
 
@@ -389,7 +389,7 @@
 
   // Create the singleton instance.
   static const ConflictType* CreateInstance(ObjPtr<mirror::Class> klass,
-                                            const StringPiece& descriptor,
+                                            const std::string_view& descriptor,
                                             uint16_t cache_id)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -402,7 +402,7 @@
 
  private:
   ConflictType(ObjPtr<mirror::Class> klass,
-               const StringPiece& descriptor,
+               const std::string_view& descriptor,
                uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
       : RegType(klass, descriptor, cache_id) {
     CheckConstructorInvariants(this);
@@ -425,7 +425,7 @@
 
   // Create the singleton instance.
   static const UndefinedType* CreateInstance(ObjPtr<mirror::Class> klass,
-                                             const StringPiece& descriptor,
+                                             const std::string_view& descriptor,
                                              uint16_t cache_id)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -438,7 +438,7 @@
 
  private:
   UndefinedType(ObjPtr<mirror::Class> klass,
-                const StringPiece& descriptor,
+                const std::string_view& descriptor,
                 uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
       : RegType(klass, descriptor, cache_id) {
     CheckConstructorInvariants(this);
@@ -450,7 +450,7 @@
 class PrimitiveType : public RegType {
  public:
   PrimitiveType(ObjPtr<mirror::Class> klass,
-                const StringPiece& descriptor,
+                const std::string_view& descriptor,
                 uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_);
 
   bool HasClassVirtual() const override { return true; }
@@ -458,7 +458,8 @@
 
 class Cat1Type : public PrimitiveType {
  public:
-  Cat1Type(ObjPtr<mirror::Class> klass, const StringPiece& descriptor,
+  Cat1Type(ObjPtr<mirror::Class> klass,
+           const std::string_view& descriptor,
            uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_);
 };
 
@@ -467,7 +468,7 @@
   bool IsInteger() const override { return true; }
   std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
   static const IntegerType* CreateInstance(ObjPtr<mirror::Class> klass,
-                                           const StringPiece& descriptor,
+                                           const std::string_view& descriptor,
                                            uint16_t cache_id)
       REQUIRES_SHARED(Locks::mutator_lock_);
   static const IntegerType* GetInstance() PURE;
@@ -479,7 +480,7 @@
 
  private:
   IntegerType(ObjPtr<mirror::Class> klass,
-              const StringPiece& descriptor,
+              const std::string_view& descriptor,
               uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
       : Cat1Type(klass, descriptor, cache_id) {
     CheckConstructorInvariants(this);
@@ -492,7 +493,7 @@
   bool IsBoolean() const override { return true; }
   std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
   static const BooleanType* CreateInstance(ObjPtr<mirror::Class> klass,
-                                           const StringPiece& descriptor,
+                                           const std::string_view& descriptor,
                                            uint16_t cache_id)
       REQUIRES_SHARED(Locks::mutator_lock_);
   static const BooleanType* GetInstance() PURE;
@@ -504,7 +505,7 @@
 
  private:
   BooleanType(ObjPtr<mirror::Class> klass,
-              const StringPiece& descriptor,
+              const std::string_view& descriptor,
               uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
       : Cat1Type(klass, descriptor, cache_id) {
     CheckConstructorInvariants(this);
@@ -518,7 +519,7 @@
   bool IsByte() const override { return true; }
   std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
   static const ByteType* CreateInstance(ObjPtr<mirror::Class> klass,
-                                        const StringPiece& descriptor,
+                                        const std::string_view& descriptor,
                                         uint16_t cache_id)
       REQUIRES_SHARED(Locks::mutator_lock_);
   static const ByteType* GetInstance() PURE;
@@ -530,7 +531,7 @@
 
  private:
   ByteType(ObjPtr<mirror::Class> klass,
-           const StringPiece& descriptor,
+           const std::string_view& descriptor,
            uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
       : Cat1Type(klass, descriptor, cache_id) {
     CheckConstructorInvariants(this);
@@ -543,7 +544,7 @@
   bool IsShort() const override { return true; }
   std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
   static const ShortType* CreateInstance(ObjPtr<mirror::Class> klass,
-                                         const StringPiece& descriptor,
+                                         const std::string_view& descriptor,
                                          uint16_t cache_id)
       REQUIRES_SHARED(Locks::mutator_lock_);
   static const ShortType* GetInstance() PURE;
@@ -554,7 +555,7 @@
   }
 
  private:
-  ShortType(ObjPtr<mirror::Class> klass, const StringPiece& descriptor,
+  ShortType(ObjPtr<mirror::Class> klass, const std::string_view& descriptor,
             uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
       : Cat1Type(klass, descriptor, cache_id) {
     CheckConstructorInvariants(this);
@@ -567,7 +568,7 @@
   bool IsChar() const override { return true; }
   std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
   static const CharType* CreateInstance(ObjPtr<mirror::Class> klass,
-                                        const StringPiece& descriptor,
+                                        const std::string_view& descriptor,
                                         uint16_t cache_id)
       REQUIRES_SHARED(Locks::mutator_lock_);
   static const CharType* GetInstance() PURE;
@@ -579,7 +580,7 @@
 
  private:
   CharType(ObjPtr<mirror::Class> klass,
-           const StringPiece& descriptor,
+           const std::string_view& descriptor,
            uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
       : Cat1Type(klass, descriptor, cache_id) {
     CheckConstructorInvariants(this);
@@ -592,7 +593,7 @@
   bool IsFloat() const override { return true; }
   std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
   static const FloatType* CreateInstance(ObjPtr<mirror::Class> klass,
-                                         const StringPiece& descriptor,
+                                         const std::string_view& descriptor,
                                          uint16_t cache_id)
       REQUIRES_SHARED(Locks::mutator_lock_);
   static const FloatType* GetInstance() PURE;
@@ -604,7 +605,7 @@
 
  private:
   FloatType(ObjPtr<mirror::Class> klass,
-            const StringPiece& descriptor,
+            const std::string_view& descriptor,
             uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
       : Cat1Type(klass, descriptor, cache_id) {
     CheckConstructorInvariants(this);
@@ -615,7 +616,7 @@
 class Cat2Type : public PrimitiveType {
  public:
   Cat2Type(ObjPtr<mirror::Class> klass,
-           const StringPiece& descriptor,
+           const std::string_view& descriptor,
            uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_);
 };
 
@@ -625,7 +626,7 @@
   bool IsLongLo() const override { return true; }
   bool IsLong() const override { return true; }
   static const LongLoType* CreateInstance(ObjPtr<mirror::Class> klass,
-                                          const StringPiece& descriptor,
+                                          const std::string_view& descriptor,
                                           uint16_t cache_id)
       REQUIRES_SHARED(Locks::mutator_lock_);
   static const LongLoType* GetInstance() PURE;
@@ -637,7 +638,7 @@
 
  private:
   LongLoType(ObjPtr<mirror::Class> klass,
-             const StringPiece& descriptor,
+             const std::string_view& descriptor,
              uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
       : Cat2Type(klass, descriptor, cache_id) {
     CheckConstructorInvariants(this);
@@ -650,7 +651,7 @@
   std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
   bool IsLongHi() const override { return true; }
   static const LongHiType* CreateInstance(ObjPtr<mirror::Class> klass,
-                                          const StringPiece& descriptor,
+                                          const std::string_view& descriptor,
                                           uint16_t cache_id)
       REQUIRES_SHARED(Locks::mutator_lock_);
   static const LongHiType* GetInstance() PURE;
@@ -662,7 +663,7 @@
 
  private:
   LongHiType(ObjPtr<mirror::Class> klass,
-             const StringPiece& descriptor,
+             const std::string_view& descriptor,
              uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
       : Cat2Type(klass, descriptor, cache_id) {
     CheckConstructorInvariants(this);
@@ -676,7 +677,7 @@
   bool IsDoubleLo() const override { return true; }
   bool IsDouble() const override { return true; }
   static const DoubleLoType* CreateInstance(ObjPtr<mirror::Class> klass,
-                                            const StringPiece& descriptor,
+                                            const std::string_view& descriptor,
                                             uint16_t cache_id)
       REQUIRES_SHARED(Locks::mutator_lock_);
   static const DoubleLoType* GetInstance() PURE;
@@ -688,7 +689,7 @@
 
  private:
   DoubleLoType(ObjPtr<mirror::Class> klass,
-               const StringPiece& descriptor,
+               const std::string_view& descriptor,
                uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
       : Cat2Type(klass, descriptor, cache_id) {
     CheckConstructorInvariants(this);
@@ -701,7 +702,7 @@
   std::string Dump() const override REQUIRES_SHARED(Locks::mutator_lock_);
   bool IsDoubleHi() const override { return true; }
   static const DoubleHiType* CreateInstance(ObjPtr<mirror::Class> klass,
-                                            const StringPiece& descriptor,
+                                            const std::string_view& descriptor,
                                             uint16_t cache_id)
       REQUIRES_SHARED(Locks::mutator_lock_);
   static const DoubleHiType* GetInstance() PURE;
@@ -713,7 +714,7 @@
 
  private:
   DoubleHiType(ObjPtr<mirror::Class> klass,
-               const StringPiece& descriptor,
+               const std::string_view& descriptor,
                uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
       : Cat2Type(klass, descriptor, cache_id) {
     CheckConstructorInvariants(this);
@@ -886,7 +887,7 @@
 
   // Create the singleton instance.
   static const NullType* CreateInstance(ObjPtr<mirror::Class> klass,
-                                        const StringPiece& descriptor,
+                                        const std::string_view& descriptor,
                                         uint16_t cache_id)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -905,7 +906,7 @@
   }
 
  private:
-  NullType(ObjPtr<mirror::Class> klass, const StringPiece& descriptor, uint16_t cache_id)
+  NullType(ObjPtr<mirror::Class> klass, const std::string_view& descriptor, uint16_t cache_id)
       REQUIRES_SHARED(Locks::mutator_lock_)
       : RegType(klass, descriptor, cache_id) {
     CheckConstructorInvariants(this);
@@ -920,7 +921,7 @@
 class UninitializedType : public RegType {
  public:
   UninitializedType(ObjPtr<mirror::Class> klass,
-                    const StringPiece& descriptor,
+                    const std::string_view& descriptor,
                     uint32_t allocation_pc,
                     uint16_t cache_id)
       : RegType(klass, descriptor, cache_id), allocation_pc_(allocation_pc) {}
@@ -945,7 +946,7 @@
 class UninitializedReferenceType final : public UninitializedType {
  public:
   UninitializedReferenceType(ObjPtr<mirror::Class> klass,
-                             const StringPiece& descriptor,
+                             const std::string_view& descriptor,
                              uint32_t allocation_pc,
                              uint16_t cache_id)
       REQUIRES_SHARED(Locks::mutator_lock_)
@@ -964,8 +965,9 @@
 // constructor.
 class UnresolvedUninitializedRefType final : public UninitializedType {
  public:
-  UnresolvedUninitializedRefType(const StringPiece& descriptor,
-                                 uint32_t allocation_pc, uint16_t cache_id)
+  UnresolvedUninitializedRefType(const std::string_view& descriptor,
+                                 uint32_t allocation_pc,
+                                 uint16_t cache_id)
       REQUIRES_SHARED(Locks::mutator_lock_)
       : UninitializedType(nullptr, descriptor, allocation_pc, cache_id) {
     CheckConstructorInvariants(this);
@@ -986,7 +988,7 @@
 class UninitializedThisReferenceType final : public UninitializedType {
  public:
   UninitializedThisReferenceType(ObjPtr<mirror::Class> klass,
-                                 const StringPiece& descriptor,
+                                 const std::string_view& descriptor,
                                  uint16_t cache_id)
       REQUIRES_SHARED(Locks::mutator_lock_)
       : UninitializedType(klass, descriptor, 0, cache_id) {
@@ -1005,8 +1007,7 @@
 
 class UnresolvedUninitializedThisRefType final : public UninitializedType {
  public:
-  UnresolvedUninitializedThisRefType(const StringPiece& descriptor,
-                                     uint16_t cache_id)
+  UnresolvedUninitializedThisRefType(const std::string_view& descriptor, uint16_t cache_id)
       REQUIRES_SHARED(Locks::mutator_lock_)
       : UninitializedType(nullptr, descriptor, 0, cache_id) {
     CheckConstructorInvariants(this);
@@ -1027,7 +1028,7 @@
 class ReferenceType final : public RegType {
  public:
   ReferenceType(ObjPtr<mirror::Class> klass,
-                const StringPiece& descriptor,
+                const std::string_view& descriptor,
                 uint16_t cache_id) REQUIRES_SHARED(Locks::mutator_lock_)
       : RegType(klass, descriptor, cache_id) {
     CheckConstructorInvariants(this);
@@ -1052,7 +1053,7 @@
 class PreciseReferenceType final : public RegType {
  public:
   PreciseReferenceType(ObjPtr<mirror::Class> klass,
-                       const StringPiece& descriptor,
+                       const std::string_view& descriptor,
                        uint16_t cache_id)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -1072,7 +1073,7 @@
 // Common parent of unresolved types.
 class UnresolvedType : public RegType {
  public:
-  UnresolvedType(const StringPiece& descriptor, uint16_t cache_id)
+  UnresolvedType(const std::string_view& descriptor, uint16_t cache_id)
       REQUIRES_SHARED(Locks::mutator_lock_)
       : RegType(nullptr, descriptor, cache_id) {}
 
@@ -1088,7 +1089,7 @@
 // of this type must be conservative.
 class UnresolvedReferenceType final : public UnresolvedType {
  public:
-  UnresolvedReferenceType(const StringPiece& descriptor, uint16_t cache_id)
+  UnresolvedReferenceType(const std::string_view& descriptor, uint16_t cache_id)
       REQUIRES_SHARED(Locks::mutator_lock_)
       : UnresolvedType(descriptor, cache_id) {
     CheckConstructorInvariants(this);
diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc
index ceba748..7bff255 100644
--- a/runtime/verifier/reg_type_cache.cc
+++ b/runtime/verifier/reg_type_cache.cc
@@ -135,7 +135,7 @@
   }
 }
 
-bool RegTypeCache::MatchDescriptor(size_t idx, const StringPiece& descriptor, bool precise) {
+bool RegTypeCache::MatchDescriptor(size_t idx, const std::string_view& descriptor, bool precise) {
   const RegType* entry = entries_[idx];
   if (descriptor != entry->descriptor_) {
     return false;
@@ -170,20 +170,20 @@
   return klass;
 }
 
-StringPiece RegTypeCache::AddString(const StringPiece& string_piece) {
-  char* ptr = allocator_.AllocArray<char>(string_piece.length());
-  memcpy(ptr, string_piece.data(), string_piece.length());
-  return StringPiece(ptr, string_piece.length());
+std::string_view RegTypeCache::AddString(const std::string_view& str) {
+  char* ptr = allocator_.AllocArray<char>(str.length());
+  memcpy(ptr, str.data(), str.length());
+  return std::string_view(ptr, str.length());
 }
 
 const RegType& RegTypeCache::From(ObjPtr<mirror::ClassLoader> loader,
                                   const char* descriptor,
                                   bool precise) {
-  StringPiece sp_descriptor(descriptor);
-  // Try looking up the class in the cache first. We use a StringPiece to avoid continual strlen
-  // operations on the descriptor.
+  std::string_view sv_descriptor(descriptor);
+  // Try looking up the class in the cache first. We use a std::string_view to avoid
+  // repeated strlen operations on the descriptor.
   for (size_t i = primitive_count_; i < entries_.size(); i++) {
-    if (MatchDescriptor(i, sp_descriptor, precise)) {
+    if (MatchDescriptor(i, sv_descriptor, precise)) {
       return *(entries_[i]);
     }
   }
@@ -205,9 +205,9 @@
       DCHECK(!(klass->IsAbstract()) || klass->IsArrayClass());
       DCHECK(!klass->IsInterface());
       entry =
-          new (&allocator_) PreciseReferenceType(klass, AddString(sp_descriptor), entries_.size());
+          new (&allocator_) PreciseReferenceType(klass, AddString(sv_descriptor), entries_.size());
     } else {
-      entry = new (&allocator_) ReferenceType(klass, AddString(sp_descriptor), entries_.size());
+      entry = new (&allocator_) ReferenceType(klass, AddString(sv_descriptor), entries_.size());
     }
     return AddEntry(entry);
   } else {  // Class not resolved.
@@ -221,7 +221,7 @@
     }
     if (IsValidDescriptor(descriptor)) {
       return AddEntry(
-          new (&allocator_) UnresolvedReferenceType(AddString(sp_descriptor), entries_.size()));
+          new (&allocator_) UnresolvedReferenceType(AddString(sv_descriptor), entries_.size()));
     } else {
       // The descriptor is broken return the unknown type as there's nothing sensible that
       // could be done at runtime
@@ -254,7 +254,7 @@
   return nullptr;
 }
 
-const RegType* RegTypeCache::InsertClass(const StringPiece& descriptor,
+const RegType* RegTypeCache::InsertClass(const std::string_view& descriptor,
                                          ObjPtr<mirror::Class> klass,
                                          bool precise) {
   // No reference to the class was found, create new reference.
@@ -272,7 +272,7 @@
   DCHECK(klass != nullptr);
   const RegType* reg_type = FindClass(klass, precise);
   if (reg_type == nullptr) {
-    reg_type = InsertClass(AddString(StringPiece(descriptor)), klass, precise);
+    reg_type = InsertClass(AddString(std::string_view(descriptor)), klass, precise);
   }
   return *reg_type;
 }
@@ -488,7 +488,7 @@
 
 const UninitializedType& RegTypeCache::Uninitialized(const RegType& type, uint32_t allocation_pc) {
   UninitializedType* entry = nullptr;
-  const StringPiece& descriptor(type.GetDescriptor());
+  const std::string_view& descriptor(type.GetDescriptor());
   if (type.IsUnresolvedTypes()) {
     for (size_t i = primitive_count_; i < entries_.size(); i++) {
       const RegType* cur_entry = entries_[i];
@@ -525,7 +525,7 @@
   RegType* entry;
 
   if (uninit_type.IsUnresolvedTypes()) {
-    const StringPiece& descriptor(uninit_type.GetDescriptor());
+    const std::string_view& descriptor(uninit_type.GetDescriptor());
     for (size_t i = primitive_count_; i < entries_.size(); i++) {
       const RegType* cur_entry = entries_[i];
       if (cur_entry->IsUnresolvedReference() &&
@@ -575,7 +575,7 @@
 
 const UninitializedType& RegTypeCache::UninitializedThisArgument(const RegType& type) {
   UninitializedType* entry;
-  const StringPiece& descriptor(type.GetDescriptor());
+  const std::string_view& descriptor(type.GetDescriptor());
   if (type.IsUnresolvedTypes()) {
     for (size_t i = primitive_count_; i < entries_.size(); i++) {
       const RegType* cur_entry = entries_[i];
@@ -656,7 +656,7 @@
     return Conflict();
   } else if (array.IsUnresolvedTypes()) {
     DCHECK(!array.IsUnresolvedMergedReference());  // Caller must make sure not to ask for this.
-    const std::string descriptor(array.GetDescriptor().as_string());
+    const std::string descriptor(array.GetDescriptor());
     return FromDescriptor(loader, descriptor.c_str() + 1, false);
   } else {
     ObjPtr<mirror::Class> klass = array.GetClass()->GetComponentType();
diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h
index d668222..a9a8116 100644
--- a/runtime/verifier/reg_type_cache.h
+++ b/runtime/verifier/reg_type_cache.h
@@ -18,6 +18,7 @@
 #define ART_RUNTIME_VERIFIER_REG_TYPE_CACHE_H_
 
 #include <stdint.h>
+#include <string_view>
 #include <vector>
 
 #include "base/casts.h"
@@ -32,7 +33,6 @@
 class ClassLoader;
 }  // namespace mirror
 class ScopedArenaAllocator;
-class StringPiece;
 
 namespace verifier {
 
@@ -80,7 +80,7 @@
   const RegType* FindClass(ObjPtr<mirror::Class> klass, bool precise) const
       REQUIRES_SHARED(Locks::mutator_lock_);
   // Insert a new class with a specified descriptor, must not already be in the cache.
-  const RegType* InsertClass(const StringPiece& descriptor,
+  const RegType* InsertClass(const std::string_view& descriptor,
                              ObjPtr<mirror::Class> klass,
                              bool precise)
       REQUIRES_SHARED(Locks::mutator_lock_);
@@ -164,7 +164,7 @@
   void FillPrimitiveAndSmallConstantTypes() REQUIRES_SHARED(Locks::mutator_lock_);
   ObjPtr<mirror::Class> ResolveClass(const char* descriptor, ObjPtr<mirror::ClassLoader> loader)
       REQUIRES_SHARED(Locks::mutator_lock_);
-  bool MatchDescriptor(size_t idx, const StringPiece& descriptor, bool precise)
+  bool MatchDescriptor(size_t idx, const std::string_view& descriptor, bool precise)
       REQUIRES_SHARED(Locks::mutator_lock_);
   const ConstantType& FromCat1NonSmallConstant(int32_t value, bool precise)
       REQUIRES_SHARED(Locks::mutator_lock_);
@@ -173,9 +173,9 @@
   template <class RegTypeType>
   RegTypeType& AddEntry(RegTypeType* new_entry) REQUIRES_SHARED(Locks::mutator_lock_);
 
-  // Add a string piece to the arena allocator so that it stays live for the lifetime of the
-  // verifier.
-  StringPiece AddString(const StringPiece& string_piece);
+  // Add a string to the arena allocator so that it stays live for the lifetime of the
+  // verifier and return a string view.
+  std::string_view AddString(const std::string_view& str);
 
   static void CreatePrimitiveAndSmallConstantTypes() REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index bdcadd9..bd98997 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -17,6 +17,7 @@
 #include "verifier_deps.h"
 
 #include <cstring>
+#include <sstream>
 
 #include "art_field-inl.h"
 #include "art_method-inl.h"
@@ -25,6 +26,7 @@
 #include "base/mutex-inl.h"
 #include "base/stl_util.h"
 #include "compiler_callbacks.h"
+#include "dex/class_accessor-inl.h"
 #include "dex/dex_file-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
@@ -690,6 +692,12 @@
   }
 }
 
+static inline std::string ToHex(uint32_t value) {
+  std::stringstream ss;
+  ss << std::hex << value << std::dec;
+  return ss.str();
+}
+
 }  // namespace
 
 void VerifierDeps::Encode(const std::vector<const DexFile*>& dex_files,
@@ -849,9 +857,10 @@
 }
 
 bool VerifierDeps::ValidateDependencies(Handle<mirror::ClassLoader> class_loader,
-                                        Thread* self) const {
+                                        Thread* self,
+                                        /* out */ std::string* error_msg) const {
   for (const auto& entry : dex_deps_) {
-    if (!VerifyDexFile(class_loader, *entry.first, *entry.second, self)) {
+    if (!VerifyDexFile(class_loader, *entry.first, *entry.second, self, error_msg)) {
       return false;
     }
   }
@@ -862,10 +871,10 @@
 // the same lookup pattern.
 static ObjPtr<mirror::Class> FindClassAndClearException(ClassLinker* class_linker,
                                                         Thread* self,
-                                                        const char* name,
+                                                        const std::string& name,
                                                         Handle<mirror::ClassLoader> class_loader)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  ObjPtr<mirror::Class> result = class_linker->FindClass(self, name, class_loader);
+  ObjPtr<mirror::Class> result = class_linker->FindClass(self, name.c_str(), class_loader);
   if (result == nullptr) {
     DCHECK(self->IsExceptionPending());
     self->ClearException();
@@ -877,7 +886,8 @@
                                        const DexFile& dex_file,
                                        const std::set<TypeAssignability>& assignables,
                                        bool expected_assignability,
-                                       Thread* self) const {
+                                       Thread* self,
+                                       /* out */ std::string* error_msg) const {
   StackHandleScope<2> hs(self);
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   MutableHandle<mirror::Class> source(hs.NewHandle<mirror::Class>(nullptr));
@@ -892,22 +902,19 @@
         FindClassAndClearException(class_linker, self, source_desc.c_str(), class_loader));
 
     if (destination == nullptr) {
-      LOG(INFO) << "VerifiersDeps: Could not resolve class " << destination_desc;
+      *error_msg = "Could not resolve class " + destination_desc;
       return false;
     }
 
     if (source == nullptr) {
-      LOG(INFO) << "VerifierDeps: Could not resolve class " << source_desc;
+      *error_msg = "Could not resolve class " + source_desc;
       return false;
     }
 
     DCHECK(destination->IsResolved() && source->IsResolved());
     if (destination->IsAssignableFrom(source.Get()) != expected_assignability) {
-      LOG(INFO) << "VerifierDeps: Class "
-                << destination_desc
-                << (expected_assignability ? " not " : " ")
-                << "assignable from "
-                << source_desc;
+      *error_msg = "Class " + destination_desc + (expected_assignability ? " not " : " ") +
+          "assignable from " + source_desc;
       return false;
     }
   }
@@ -917,31 +924,27 @@
 bool VerifierDeps::VerifyClasses(Handle<mirror::ClassLoader> class_loader,
                                  const DexFile& dex_file,
                                  const std::set<ClassResolution>& classes,
-                                 Thread* self) const {
+                                 Thread* self,
+                                 /* out */ std::string* error_msg) const {
   StackHandleScope<1> hs(self);
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   MutableHandle<mirror::Class> cls(hs.NewHandle<mirror::Class>(nullptr));
   for (const auto& entry : classes) {
-    const char* descriptor = dex_file.StringByTypeIdx(entry.GetDexTypeIndex());
+    std::string descriptor = dex_file.StringByTypeIdx(entry.GetDexTypeIndex());
     cls.Assign(FindClassAndClearException(class_linker, self, descriptor, class_loader));
 
     if (entry.IsResolved()) {
       if (cls == nullptr) {
-        LOG(INFO) << "VerifierDeps: Could not resolve class " << descriptor;
+        *error_msg = "Could not resolve class " + descriptor;
         return false;
       } else if (entry.GetAccessFlags() != GetAccessFlags(cls.Get())) {
-        LOG(INFO) << "VerifierDeps: Unexpected access flags on class "
-                  << descriptor
-                  << std::hex
-                  << " (expected="
-                  << entry.GetAccessFlags()
-                  << ", actual="
-                  << GetAccessFlags(cls.Get()) << ")"
-                  << std::dec;
+        *error_msg = "Unexpected access flags on class " + descriptor
+            + " (expected=" + ToHex(entry.GetAccessFlags())
+            + ", actual=" + ToHex(GetAccessFlags(cls.Get())) + ")";
         return false;
       }
     } else if (cls != nullptr) {
-      LOG(INFO) << "VerifierDeps: Unexpected successful resolution of class " << descriptor;
+      *error_msg = "Unexpected successful resolution of class " + descriptor;
       return false;
     }
   }
@@ -960,14 +963,16 @@
 bool VerifierDeps::VerifyFields(Handle<mirror::ClassLoader> class_loader,
                                 const DexFile& dex_file,
                                 const std::set<FieldResolution>& fields,
-                                Thread* self) const {
+                                Thread* self,
+                                /* out */ std::string* error_msg) const {
   // Check recorded fields are resolved the same way, have the same recorded class,
   // and have the same recorded flags.
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   for (const auto& entry : fields) {
     const dex::FieldId& field_id = dex_file.GetFieldId(entry.GetDexFieldIndex());
-    StringPiece name(dex_file.StringDataByIdx(field_id.name_idx_));
-    StringPiece type(dex_file.StringDataByIdx(dex_file.GetTypeId(field_id.type_idx_).descriptor_idx_));
+    std::string_view name(dex_file.StringDataByIdx(field_id.name_idx_));
+    std::string_view type(
+        dex_file.StringDataByIdx(dex_file.GetTypeId(field_id.type_idx_).descriptor_idx_));
     // Only use field_id.class_idx_ when the entry is unresolved, which is rare.
     // Otherwise, we might end up resolving an application class, which is expensive.
     std::string expected_decl_klass = entry.IsResolved()
@@ -976,7 +981,7 @@
     ObjPtr<mirror::Class> cls = FindClassAndClearException(
         class_linker, self, expected_decl_klass.c_str(), class_loader);
     if (cls == nullptr) {
-      LOG(INFO) << "VerifierDeps: Could not resolve class " << expected_decl_klass;
+      *error_msg = "Could not resolve class " + expected_decl_klass;
       return false;
     }
     DCHECK(cls->IsResolved());
@@ -985,25 +990,25 @@
     if (entry.IsResolved()) {
       std::string temp;
       if (field == nullptr) {
-        LOG(INFO) << "VerifierDeps: Could not resolve field "
-                  << GetFieldDescription(dex_file, entry.GetDexFieldIndex());
+        *error_msg = "Could not resolve field " +
+            GetFieldDescription(dex_file, entry.GetDexFieldIndex());
         return false;
       } else if (expected_decl_klass != field->GetDeclaringClass()->GetDescriptor(&temp)) {
-        LOG(INFO) << "VerifierDeps: Unexpected declaring class for field resolution "
-                  << GetFieldDescription(dex_file, entry.GetDexFieldIndex())
-                  << " (expected=" << expected_decl_klass
-                  << ", actual=" << field->GetDeclaringClass()->GetDescriptor(&temp) << ")";
+        *error_msg = "Unexpected declaring class for field resolution "
+            + GetFieldDescription(dex_file, entry.GetDexFieldIndex())
+            + " (expected=" + expected_decl_klass
+            + ", actual=" + field->GetDeclaringClass()->GetDescriptor(&temp) + ")";
         return false;
       } else if (entry.GetAccessFlags() != GetAccessFlags(field)) {
-        LOG(INFO) << "VerifierDeps: Unexpected access flags for resolved field "
-                  << GetFieldDescription(dex_file, entry.GetDexFieldIndex())
-                  << std::hex << " (expected=" << entry.GetAccessFlags()
-                  << ", actual=" << GetAccessFlags(field) << ")" << std::dec;
+        *error_msg = "Unexpected access flags for resolved field "
+            + GetFieldDescription(dex_file, entry.GetDexFieldIndex())
+            + " (expected=" + ToHex(entry.GetAccessFlags())
+            + ", actual=" + ToHex(GetAccessFlags(field)) + ")";
         return false;
       }
     } else if (field != nullptr) {
-      LOG(INFO) << "VerifierDeps: Unexpected successful resolution of field "
-                << GetFieldDescription(dex_file, entry.GetDexFieldIndex());
+      *error_msg = "Unexpected successful resolution of field "
+          + GetFieldDescription(dex_file, entry.GetDexFieldIndex());
       return false;
     }
   }
@@ -1021,7 +1026,8 @@
 bool VerifierDeps::VerifyMethods(Handle<mirror::ClassLoader> class_loader,
                                  const DexFile& dex_file,
                                  const std::set<MethodResolution>& methods,
-                                 Thread* self) const {
+                                 Thread* self,
+                                 /* out */ std::string* error_msg) const {
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   PointerSize pointer_size = class_linker->GetImagePointerSize();
 
@@ -1039,7 +1045,7 @@
     ObjPtr<mirror::Class> cls = FindClassAndClearException(
         class_linker, self, expected_decl_klass.c_str(), class_loader);
     if (cls == nullptr) {
-      LOG(INFO) << "VerifierDeps: Could not resolve class " << expected_decl_klass;
+      *error_msg = "Could not resolve class " + expected_decl_klass;
       return false;
     }
     DCHECK(cls->IsResolved());
@@ -1053,53 +1059,94 @@
     if (entry.IsResolved()) {
       std::string temp;
       if (method == nullptr) {
-        LOG(INFO) << "VerifierDeps: Could not resolve method "
-                  << GetMethodDescription(dex_file, entry.GetDexMethodIndex());
+        *error_msg = "Could not resolve method "
+            + GetMethodDescription(dex_file, entry.GetDexMethodIndex());
         return false;
       } else if (expected_decl_klass != method->GetDeclaringClass()->GetDescriptor(&temp)) {
-        LOG(INFO) << "VerifierDeps: Unexpected declaring class for method resolution "
-                  << GetMethodDescription(dex_file, entry.GetDexMethodIndex())
-                  << " (expected="
-                  << expected_decl_klass
-                  << ", actual="
-                  << method->GetDeclaringClass()->GetDescriptor(&temp)
-                  << ")";
+        *error_msg = "Unexpected declaring class for method resolution "
+            + GetMethodDescription(dex_file, entry.GetDexMethodIndex())
+            + " (expected=" + expected_decl_klass
+            + ", actual=" + method->GetDeclaringClass()->GetDescriptor(&temp) + ")";
         return false;
       } else if (entry.GetAccessFlags() != GetAccessFlags(method)) {
-        LOG(INFO) << "VerifierDeps: Unexpected access flags for resolved method resolution "
-                  << GetMethodDescription(dex_file, entry.GetDexMethodIndex())
-                  << std::hex
-                  << " (expected="
-                  << entry.GetAccessFlags()
-                  << ", actual="
-                  << GetAccessFlags(method) << ")"
-                  << std::dec;
+        *error_msg = "Unexpected access flags for resolved method resolution "
+            + GetMethodDescription(dex_file, entry.GetDexMethodIndex())
+            + " (expected=" + ToHex(entry.GetAccessFlags())
+            + ", actual=" + ToHex(GetAccessFlags(method)) + ")";
         return false;
       }
     } else if (method != nullptr) {
-      LOG(INFO) << "VerifierDeps: Unexpected successful resolution of method "
-                << GetMethodDescription(dex_file, entry.GetDexMethodIndex());
+      *error_msg = "Unexpected successful resolution of method "
+          + GetMethodDescription(dex_file, entry.GetDexMethodIndex());
       return false;
     }
   }
   return true;
 }
 
+bool VerifierDeps::VerifyInternalClasses(Handle<mirror::ClassLoader> class_loader,
+                                         const DexFile& dex_file,
+                                         const std::set<dex::TypeIndex>& unverified_classes,
+                                         Thread* self,
+                                         /* out */ std::string* error_msg) const {
+  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+
+  for (ClassAccessor accessor : dex_file.GetClasses()) {
+    std::string descriptor = accessor.GetDescriptor();
+    ObjPtr<mirror::Class> cls = FindClassAndClearException(class_linker,
+                                                           self,
+                                                           descriptor,
+                                                           class_loader);
+    if (UNLIKELY(cls == nullptr)) {
+      // Could not resolve class from the currently verified dex file.
+      // This can happen when the class fails to link. Check if this
+      // expected by looking in the `unverified_classes` set.
+      if (unverified_classes.find(accessor.GetClassDef().class_idx_) == unverified_classes.end()) {
+        *error_msg = "Failed to resolve internal class " + descriptor;
+        return false;
+      }
+      continue;
+    }
+
+    // Check that the class resolved into the same dex file. Otherwise there is
+    // a different class with the same descriptor somewhere in one of the parent
+    // class loaders.
+    if (&cls->GetDexFile() != &dex_file) {
+      *error_msg = "Class " + descriptor + " redefines a class in a parent class loader "
+          + "(dexFile expected=" + accessor.GetDexFile().GetLocation()
+          + ", actual=" + cls->GetDexFile().GetLocation() + ")";
+      return false;
+    }
+  }
+
+  return true;
+}
+
 bool VerifierDeps::VerifyDexFile(Handle<mirror::ClassLoader> class_loader,
                                  const DexFile& dex_file,
                                  const DexFileDeps& deps,
-                                 Thread* self) const {
-  bool result = VerifyAssignability(
-      class_loader, dex_file, deps.assignable_types_, /* expected_assignability= */ true, self);
-  result = result && VerifyAssignability(
-      class_loader, dex_file, deps.unassignable_types_, /* expected_assignability= */ false, self);
-
-  result = result && VerifyClasses(class_loader, dex_file, deps.classes_, self);
-  result = result && VerifyFields(class_loader, dex_file, deps.fields_, self);
-
-  result = result && VerifyMethods(class_loader, dex_file, deps.methods_, self);
-
-  return result;
+                                 Thread* self,
+                                 /* out */ std::string* error_msg) const {
+  return VerifyInternalClasses(class_loader,
+                               dex_file,
+                               deps.unverified_classes_,
+                               self,
+                               error_msg) &&
+         VerifyAssignability(class_loader,
+                             dex_file,
+                             deps.assignable_types_,
+                             /* expected_assignability= */ true,
+                             self,
+                             error_msg) &&
+         VerifyAssignability(class_loader,
+                             dex_file,
+                             deps.unassignable_types_,
+                             /* expected_assignability= */ false,
+                             self,
+                             error_msg) &&
+         VerifyClasses(class_loader, dex_file, deps.classes_, self, error_msg) &&
+         VerifyFields(class_loader, dex_file, deps.fields_, self, error_msg) &&
+         VerifyMethods(class_loader, dex_file, deps.methods_, self, error_msg);
 }
 
 }  // namespace verifier
diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h
index dfd4a5c..7c43a3b 100644
--- a/runtime/verifier/verifier_deps.h
+++ b/runtime/verifier/verifier_deps.h
@@ -114,7 +114,9 @@
   void Dump(VariableIndentationOutputStream* vios) const;
 
   // Verify the encoded dependencies of this `VerifierDeps` are still valid.
-  bool ValidateDependencies(Handle<mirror::ClassLoader> class_loader, Thread* self) const
+  bool ValidateDependencies(Handle<mirror::ClassLoader> class_loader,
+                            Thread* self,
+                            /* out */ std::string* error_msg) const
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   const std::set<dex::TypeIndex>& GetUnverifiedClasses(const DexFile& dex_file) const {
@@ -287,14 +289,31 @@
   bool VerifyDexFile(Handle<mirror::ClassLoader> class_loader,
                      const DexFile& dex_file,
                      const DexFileDeps& deps,
-                     Thread* self) const
+                     Thread* self,
+                     /* out */ std::string* error_msg) const
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
+  // Check that classes which are to be verified using these dependencies
+  // are not eclipsed by classes in parent class loaders, e.g. when vdex was
+  // created against SDK stubs and the app redefines a non-public class on
+  // boot classpath, or simply if a class is added during an OTA. In such cases,
+  // dependencies do not include the dependencies on the presumed-internal class
+  // and verification must fail.
+  // TODO(dbrazdil): Encode a set of redefined classes during full verification.
+  // If such class is found to be redefined at runtime, dependencies remain valid.
+  bool VerifyInternalClasses(Handle<mirror::ClassLoader> class_loader,
+                             const DexFile& dex_file,
+                             const std::set<dex::TypeIndex>& unverified_classes,
+                             Thread* self,
+                             /* out */ std::string* error_msg) const
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   bool VerifyAssignability(Handle<mirror::ClassLoader> class_loader,
                            const DexFile& dex_file,
                            const std::set<TypeAssignability>& assignables,
                            bool expected_assignability,
-                           Thread* self) const
+                           Thread* self,
+                           /* out */ std::string* error_msg) const
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Verify that the set of resolved classes at the point of creation
@@ -302,7 +321,8 @@
   bool VerifyClasses(Handle<mirror::ClassLoader> class_loader,
                      const DexFile& dex_file,
                      const std::set<ClassResolution>& classes,
-                     Thread* self) const
+                     Thread* self,
+                     /* out */ std::string* error_msg) const
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Verify that the set of resolved fields at the point of creation
@@ -311,7 +331,8 @@
   bool VerifyFields(Handle<mirror::ClassLoader> class_loader,
                     const DexFile& dex_file,
                     const std::set<FieldResolution>& classes,
-                    Thread* self) const
+                    Thread* self,
+                    /* out */ std::string* error_msg) const
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::verifier_deps_lock_);
 
@@ -321,7 +342,8 @@
   bool VerifyMethods(Handle<mirror::ClassLoader> class_loader,
                      const DexFile& dex_file,
                      const std::set<MethodResolution>& methods,
-                     Thread* self) const
+                     Thread* self,
+                     /* out */ std::string* error_msg) const
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Map from DexFiles into dependencies collected from verification of their methods.
diff --git a/test/004-StackWalk/stack_walk_jni.cc b/test/004-StackWalk/stack_walk_jni.cc
index 81c27ec..6e53f30 100644
--- a/test/004-StackWalk/stack_walk_jni.cc
+++ b/test/004-StackWalk/stack_walk_jni.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <string_view>
+
 #include "art_method-inl.h"
 #include "check_reference_map_visitor.h"
 #include "jni.h"
@@ -38,7 +40,7 @@
       return true;
     }
     ArtMethod* m = GetMethod();
-    StringPiece m_name(m->GetName());
+    std::string_view m_name(m->GetName());
 
     // Given the method name and the number of times the method has been called,
     // we know the Dex registers with live reference values. Assert that what we
diff --git a/test/690-hiddenapi-same-name-methods/build b/test/690-hiddenapi-same-name-methods/build
new file mode 100644
index 0000000..c364b3b
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2019 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.
+
+USE_HIDDENAPI=true ./default-build "$@"
diff --git a/test/690-hiddenapi-same-name-methods/expected.txt b/test/690-hiddenapi-same-name-methods/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/690-hiddenapi-same-name-methods/hiddenapi-flags.csv b/test/690-hiddenapi-same-name-methods/hiddenapi-flags.csv
new file mode 100644
index 0000000..001ab80
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/hiddenapi-flags.csv
@@ -0,0 +1,9 @@
+LSpecificClass;->foo()Ljava/lang/Double;,blacklist
+LDirectMethods;->foo()Ljava/lang/Integer;,blacklist
+LDirectMethods;->foo()Ljava/lang/Boolean;,blacklist
+LVirtualMethods;->foo()Ljava/lang/Integer;,blacklist
+LVirtualMethods;->foo()Ljava/lang/Boolean;,blacklist
+LSyntheticMethods;->foo()Ljava/lang/Integer;,blacklist
+LSyntheticMethods;->foo()Ljava/lang/Boolean;,blacklist
+LNonSyntheticMethods;->foo()Ljava/lang/Integer;,blacklist
+LNonSyntheticMethods;->foo()Ljava/lang/Boolean;,blacklist
\ No newline at end of file
diff --git a/test/690-hiddenapi-same-name-methods/info.txt b/test/690-hiddenapi-same-name-methods/info.txt
new file mode 100644
index 0000000..be5b195
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/info.txt
@@ -0,0 +1 @@
+Test that Class::GetDeclaredMethodInternal() takes hidden API into account.
\ No newline at end of file
diff --git a/test/690-hiddenapi-same-name-methods/smali-ex/DirectMethods.smali b/test/690-hiddenapi-same-name-methods/smali-ex/DirectMethods.smali
new file mode 100644
index 0000000..8564976
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/smali-ex/DirectMethods.smali
@@ -0,0 +1,46 @@
+#
+# Copyright (C) 2019 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.
+#
+
+.class LDirectMethods;
+.super Ljava/lang/Object;
+
+# Expect to choose the non-hidden, non-synthetic method.
+
+# Non-hidden methods
+.method private foo()Ljava/lang/Number;
+    .registers 1
+    const/4 v0, 0x0
+    return-object v0
+.end method
+
+.method private synthetic foo()Ljava/lang/Double;
+    .registers 1
+    const/4 v0, 0x0
+    return-object v0
+.end method
+
+# Hidden methods
+.method private foo()Ljava/lang/Integer;
+    .registers 1
+    const/4 v0, 0x0
+    return-object v0
+.end method
+
+.method private synthetic foo()Ljava/lang/Boolean;
+    .registers 1
+    const/4 v0, 0x0
+    return-object v0
+.end method
diff --git a/test/690-hiddenapi-same-name-methods/smali-ex/NonSyntheticMethods.smali b/test/690-hiddenapi-same-name-methods/smali-ex/NonSyntheticMethods.smali
new file mode 100644
index 0000000..f47219f
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/smali-ex/NonSyntheticMethods.smali
@@ -0,0 +1,46 @@
+#
+# Copyright (C) 2019 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.
+#
+
+.class LNonSyntheticMethods;
+.super Ljava/lang/Object;
+
+# Expect to choose the non-hidden, virtual method.
+
+# Non-hidden methods
+.method public foo()Ljava/lang/Number;
+    .registers 1
+    const/4 v0, 0x0
+    return-object v0
+.end method
+
+.method private foo()Ljava/lang/Double;
+    .registers 1
+    const/4 v0, 0x0
+    return-object v0
+.end method
+
+# Hidden methods
+.method public foo()Ljava/lang/Integer;
+    .registers 1
+    const/4 v0, 0x0
+    return-object v0
+.end method
+
+.method private foo()Ljava/lang/Boolean;
+    .registers 1
+    const/4 v0, 0x0
+    return-object v0
+.end method
diff --git a/test/690-hiddenapi-same-name-methods/smali-ex/SyntheticMethods.smali b/test/690-hiddenapi-same-name-methods/smali-ex/SyntheticMethods.smali
new file mode 100644
index 0000000..afb4d33
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/smali-ex/SyntheticMethods.smali
@@ -0,0 +1,46 @@
+#
+# Copyright (C) 2019 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.
+#
+
+.class LSyntheticMethods;
+.super Ljava/lang/Object;
+
+# Expect to choose the non-hidden, virtual method.
+
+# Non-hidden methods
+.method public synthetic foo()Ljava/lang/Number;
+    .registers 1
+    const/4 v0, 0x0
+    return-object v0
+.end method
+
+.method private synthetic foo()Ljava/lang/Double;
+    .registers 1
+    const/4 v0, 0x0
+    return-object v0
+.end method
+
+# Hidden methods
+.method public synthetic foo()Ljava/lang/Integer;
+    .registers 1
+    const/4 v0, 0x0
+    return-object v0
+.end method
+
+.method private synthetic foo()Ljava/lang/Boolean;
+    .registers 1
+    const/4 v0, 0x0
+    return-object v0
+.end method
diff --git a/test/690-hiddenapi-same-name-methods/smali-ex/VirtualMethods.smali b/test/690-hiddenapi-same-name-methods/smali-ex/VirtualMethods.smali
new file mode 100644
index 0000000..fb26c74
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/smali-ex/VirtualMethods.smali
@@ -0,0 +1,46 @@
+#
+# Copyright (C) 2019 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.
+#
+
+.class LVirtualMethods;
+.super Ljava/lang/Object;
+
+# Expect to choose the non-hidden, non-synthetic method.
+
+# Non-hidden methods
+.method public foo()Ljava/lang/Number;
+    .registers 1
+    const/4 v0, 0x0
+    return-object v0
+.end method
+
+.method public synthetic foo()Ljava/lang/Double;
+    .registers 1
+    const/4 v0, 0x0
+    return-object v0
+.end method
+
+# Hidden methods
+.method public foo()Ljava/lang/Integer;
+    .registers 1
+    const/4 v0, 0x0
+    return-object v0
+.end method
+
+.method public synthetic foo()Ljava/lang/Boolean;
+    .registers 1
+    const/4 v0, 0x0
+    return-object v0
+.end method
diff --git a/test/690-hiddenapi-same-name-methods/src-ex/GenericInterface.java b/test/690-hiddenapi-same-name-methods/src-ex/GenericInterface.java
new file mode 100644
index 0000000..c404402
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/src-ex/GenericInterface.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 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 interface GenericInterface<T extends Number> {
+  public T foo();
+}
diff --git a/test/690-hiddenapi-same-name-methods/src-ex/SpecificClass.java b/test/690-hiddenapi-same-name-methods/src-ex/SpecificClass.java
new file mode 100644
index 0000000..dd3a835
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/src-ex/SpecificClass.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2019 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 SpecificClass implements GenericInterface<Double> {
+  public Double foo() {
+    return 42.0;
+  }
+}
diff --git a/test/690-hiddenapi-same-name-methods/src/Main.java b/test/690-hiddenapi-same-name-methods/src/Main.java
new file mode 100644
index 0000000..12cfdd7
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/src/Main.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 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.io.File;
+import java.lang.reflect.Method;
+import java.util.Base64;
+
+public class Main {
+  public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
+    System.loadLibrary(args[0]);
+
+    // Run the initialization routine. This will enable hidden API checks in
+    // the runtime, in case they are not enabled by default.
+    init();
+
+    // Load the '-ex' APK and attach it to the boot class path.
+    appendToBootClassLoader(DEX_EXTRA, /* isCorePlatform */ false);
+
+    // All test classes contain just methods named "foo" with different return types
+    // and access flags. Check that:
+    // (a) only the non-hidden ones are returned from getDeclaredMethods
+    //     (they have return types Number and Double), and
+    // (b) getDeclaredMethod picks virtual/non-synthetic methods over direct/synthetic
+    //     (the right one always has return type Number).
+    Class<?> covariantClass = Class.forName(JAVA_CLASS_NAME, true, BOOT_CLASS_LOADER);
+    checkMethodList(covariantClass, /* expectedLength= */ 1);
+    checkMethod(covariantClass);
+
+    String[] classes = new String[] {
+      "VirtualMethods",
+      "DirectMethods",
+      "SyntheticMethods",
+      "NonSyntheticMethods"
+    };
+    for (String className : classes) {
+      Class<?> klass = Class.forName(className, true, BOOT_CLASS_LOADER);
+      checkMethodList(klass, /* expectedLength= */ 2);
+      checkMethod(klass);
+    }
+  }
+
+  private static void checkMethodList(Class<?> klass, int expectedLength) {
+    String className = klass.getName();
+    Method[] methods = klass.getDeclaredMethods();
+    if (methods.length != expectedLength) {
+      throw new RuntimeException(className + ": expected " + expectedLength +
+          " declared method(s), got " + methods.length);
+    }
+    boolean hasNumberReturnType = false;
+    boolean hasDoubleReturnType = false;
+    for (Method method : methods) {
+      if (!METHOD_NAME.equals(method.getName())) {
+        throw new RuntimeException(className + ": expected declared method name: \"" + METHOD_NAME +
+            "\", got: \"" + method.getName() + "\"");
+      }
+      if (Number.class == method.getReturnType()) {
+        hasNumberReturnType = true;
+      } else if (Double.class == method.getReturnType()) {
+        hasDoubleReturnType = true;
+      }
+    }
+    if (methods.length >= 1 && !hasNumberReturnType) {
+      throw new RuntimeException(className + ": expected a method with return type \"Number\"");
+    }
+    if (methods.length >= 2 && !hasDoubleReturnType) {
+      throw new RuntimeException(className + ": expected a method with return type \"Double\"");
+    }
+  }
+
+  private static void checkMethod(Class<?> klass) throws NoSuchMethodException {
+    String className = klass.getName();
+    Method method = klass.getDeclaredMethod(METHOD_NAME);
+    if (!METHOD_NAME.equals(method.getName())) {
+      throw new RuntimeException(className + ": expected declared method name: \"" + METHOD_NAME +
+          "\", got: \"" + method.getName() + "\"");
+    } else if (Number.class != method.getReturnType()) {
+      throw new RuntimeException(className + ": expected method return type: \"Number\", got \"" +
+          method.getReturnType().toString() + "\"");
+    }
+  }
+
+  private static final String DEX_EXTRA = new File(System.getenv("DEX_LOCATION"),
+      "690-hiddenapi-same-name-methods-ex.jar").getAbsolutePath();
+
+  private static ClassLoader BOOT_CLASS_LOADER = Object.class.getClassLoader();
+
+  private static final String JAVA_CLASS_NAME = "SpecificClass";
+  private static final String METHOD_NAME = "foo";
+
+  // Native functions. Note that these are implemented in 674-hiddenapi/hiddenapi.cc.
+  private static native void appendToBootClassLoader(String dexPath, boolean isCorePlatform);
+  private static native void init();
+}
diff --git a/test/719-dm-verify-redefinition/check b/test/719-dm-verify-redefinition/check
new file mode 100644
index 0000000..9845eee
--- /dev/null
+++ b/test/719-dm-verify-redefinition/check
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 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.
+
+# Search for the redefinition line and remove unnecessary tags.
+sed -e 's/^dex2oat[d]\?\(\|32\|64\)\ W.*\] \(Fast verification failed: Class L[^;]*; redefines a class in a parent class loader\).*/\2/g' "$2" > "$2.tmp1"
+# Remove all other dex2oat/dalvikvm log lines.
+grep -v dex2oat "$2.tmp1" | grep -v dalvikvm >> "$2.tmp2"
+
+./default-check "$1" "$2.tmp2"
diff --git a/test/719-dm-verify-redefinition/expected.txt b/test/719-dm-verify-redefinition/expected.txt
new file mode 100644
index 0000000..e1ab7d1
--- /dev/null
+++ b/test/719-dm-verify-redefinition/expected.txt
@@ -0,0 +1,3 @@
+Fast verification failed: Class Ljava/util/BitSet; redefines a class in a parent class loader
+Hello, world!
+Correct resolution of boot class.
diff --git a/test/719-dm-verify-redefinition/info.txt b/test/719-dm-verify-redefinition/info.txt
new file mode 100644
index 0000000..1229bdb
--- /dev/null
+++ b/test/719-dm-verify-redefinition/info.txt
@@ -0,0 +1,2 @@
+Verifies that the vdex file from a DexMetadata archive is discarded
+if the app redefines boot classes.
diff --git a/test/719-dm-verify-redefinition/run b/test/719-dm-verify-redefinition/run
new file mode 100644
index 0000000..8e568b5
--- /dev/null
+++ b/test/719-dm-verify-redefinition/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 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.
+
+export ANDROID_LOG_TAGS='*:w'
+exec ${RUN} --external-log-tags --dm "${@}"
diff --git a/test/719-dm-verify-redefinition/src/Main.java b/test/719-dm-verify-redefinition/src/Main.java
new file mode 100644
index 0000000..37575b6
--- /dev/null
+++ b/test/719-dm-verify-redefinition/src/Main.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 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.util.BitSet;
+
+public class Main {
+  public static void main(String[] args) {
+    System.out.println("Hello, world!");
+    if (BitSet.class.getClassLoader().equals(String.class.getClassLoader())) {
+      System.out.println("Correct resolution of boot class.");
+    } else {
+      System.out.println("Bogus resolution of boot class.");
+    }
+  }
+}
diff --git a/test/719-dm-verify-redefinition/src/java/util/BitSet.java b/test/719-dm-verify-redefinition/src/java/util/BitSet.java
new file mode 100644
index 0000000..5d91fd8
--- /dev/null
+++ b/test/719-dm-verify-redefinition/src/java/util/BitSet.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2019 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 java.util;
+
+public class BitSet {
+}
diff --git a/test/HiddenApi/AbstractPackageClass.java b/test/HiddenApi/AbstractPackageClass.java
new file mode 100644
index 0000000..8f955ca
--- /dev/null
+++ b/test/HiddenApi/AbstractPackageClass.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+abstract class AbstractPackageClass {
+  public void publicMethod2() {}
+}
diff --git a/test/HiddenApi/PackageClass.java b/test/HiddenApi/PackageClass.java
new file mode 100644
index 0000000..eece100
--- /dev/null
+++ b/test/HiddenApi/PackageClass.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+class PackageClass extends AbstractPackageClass implements PublicInterface {
+  public void publicMethod1() {}
+}
diff --git a/test/HiddenApi/PublicInterface.java b/test/HiddenApi/PublicInterface.java
new file mode 100644
index 0000000..77a3709
--- /dev/null
+++ b/test/HiddenApi/PublicInterface.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2019 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 interface PublicInterface {
+  void publicMethod1();
+  void publicMethod2();
+}
diff --git a/test/HiddenApiStubs/HiddenApi b/test/HiddenApiStubs/HiddenApi
new file mode 100644
index 0000000..6841ab5
--- /dev/null
+++ b/test/HiddenApiStubs/HiddenApi
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 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 interface PublicInterface {
+  void publicMethod();
+}
diff --git a/test/HiddenApiStubs/PublicInterface.java b/test/HiddenApiStubs/PublicInterface.java
new file mode 100644
index 0000000..77a3709
--- /dev/null
+++ b/test/HiddenApiStubs/PublicInterface.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2019 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 interface PublicInterface {
+  void publicMethod1();
+  void publicMethod2();
+}
diff --git a/test/VerifierDeps/Iface.smali b/test/VerifierDeps/Iface.smali
index 8607307..1ee2358 100644
--- a/test/VerifierDeps/Iface.smali
+++ b/test/VerifierDeps/Iface.smali
@@ -1,18 +1,16 @@
-# /*
-#  * Copyright (C) 2017 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.
-#  */
+# Copyright (C) 2017 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.
 
 .class public abstract interface LIface;
 .super Ljava/lang/Object;
diff --git a/test/etc/default-build b/test/etc/default-build
index 5eba804..d203698 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -80,6 +80,12 @@
   HAS_JASMIN_MULTIDEX=false
 fi
 
+if [ -d smali-ex ]; then
+  HAS_SMALI_EX=true
+else
+  HAS_SMALI_EX=false
+fi
+
 if [ -d src-ex ]; then
   HAS_SRC_EX=true
 else
@@ -454,13 +460,25 @@
     javac_with_bootclasspath -d classes-tmp-for-ex `find src-art -name '*.java'`
     src_tmp_for_ex="-cp classes-tmp-for-ex"
   fi
-  mkdir classes-ex
+  mkdir -p classes-ex
   javac_with_bootclasspath -d classes-ex $src_tmp_for_ex `find src-ex -name '*.java'`
 fi
 
 if [[ -d classes-ex ]] && [ ${NEED_DEX} = "true" ]; then
   make_dex classes-ex
+fi
 
+if [ "${HAS_SMALI_EX}" = "true" -a ${NEED_DEX} = "true" ]; then
+  # Compile Smali classes
+  ${SMALI} -JXmx512m assemble ${SMALI_ARGS} --output smali_classes-ex.dex `find smali-ex -name '*.smali'`
+  if [[ ! -s smali_classes-ex.dex ]] ; then
+    fail "${SMALI} produced no output."
+  fi
+  # Merge smali files into classes-ex.dex.
+  make_dexmerge classes-ex.dex smali_classes-ex.dex
+fi
+
+if [[ -f classes-ex.dex ]]; then
   # Apply hiddenapi on the dex files if the test has API list file(s).
   if [ ${USE_HIDDENAPI} = "true" -a ${HAS_HIDDENAPI_SPEC} = "true" ]; then
     make_hiddenapi classes-ex.dex
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 7453823..2910920 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -761,8 +761,8 @@
   if [ "$DEV_MODE" = "y" ]; then
     zip_options=""
   fi
-  setupapex_cmdline="unzip -o -f -u ${zip_options} ${ZIPAPEX_LOC} apex_payload.zip -d ${DEX_LOCATION}"
-  installapex_cmdline="unzip -o -f -u ${zip_options} ${DEX_LOCATION}/apex_payload.zip -d ${DEX_LOCATION}/zipapex"
+  setupapex_cmdline="unzip -o -u ${zip_options} ${ZIPAPEX_LOC} apex_payload.zip -d ${DEX_LOCATION}"
+  installapex_cmdline="unzip -o -u ${zip_options} ${DEX_LOCATION}/apex_payload.zip -d ${DEX_LOCATION}/zipapex"
   BIN_DIR=$DEX_LOCATION/zipapex/bin
 fi
 
@@ -1005,12 +1005,14 @@
     export ANDROID_DATA="$DEX_LOCATION"
     export ANDROID_ROOT="${ANDROID_ROOT}"
     export ANDROID_RUNTIME_ROOT="${ANDROID_RUNTIME_ROOT}"
-    export LD_LIBRARY_PATH="${ANDROID_ROOT}/${LIBRARY_DIRECTORY}:${ANDROID_ROOT}/${TEST_DIRECTORY}"
     if [ "$USE_ZIPAPEX" = "y" ]; then
       # Put the zipapex files in front of the ld-library-path
-      export LD_LIBRARY_PATH="${ANDROID_DATA}/zipapex/${LIBRARY_DIRECTORY}:${LD_LIBRARY_PATH}"
+      export LD_LIBRARY_PATH="${ANDROID_DATA}/zipapex/${LIBRARY_DIRECTORY}:${ANDROID_ROOT}/${TEST_DIRECTORY}"
+      export DYLD_LIBRARY_PATH="${ANDROID_DATA}/zipapex/${LIBRARY_DIRECTORY}:${ANDROID_ROOT}/${TEST_DIRECTORY}"
+    else
+      export LD_LIBRARY_PATH="${ANDROID_ROOT}/${LIBRARY_DIRECTORY}:${ANDROID_ROOT}/${TEST_DIRECTORY}"
+      export DYLD_LIBRARY_PATH="${ANDROID_ROOT}/${LIBRARY_DIRECTORY}:${ANDROID_ROOT}/${TEST_DIRECTORY}"
     fi
-    export DYLD_LIBRARY_PATH="${ANDROID_ROOT}/${LIBRARY_DIRECTORY}:${ANDROID_ROOT}/${TEST_DIRECTORY}"
     export PATH="$PATH:$BIN_DIR"
 
     # Temporarily disable address space layout randomization (ASLR).
@@ -1066,7 +1068,7 @@
 
     mkdir -p ${mkdir_locations} || exit 1
     $setupapex_cmdline || { echo "zipapex extraction failed." >&2 ; exit 2; }
-    $installapex_cmdline || { echo "zipapex install failed." >&2 ; exit 2; }
+    $installapex_cmdline || { echo "zipapex install failed. cmd was: ${installapex_cmdline}." >&2; find ${mkdir_locations} -type f >&2; exit 2; }
     $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; }
diff --git a/test/knownfailures.json b/test/knownfailures.json
index e32d23d..cf6e69c 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -534,8 +534,9 @@
             "097-duplicate-method",
             "138-duplicate-classes-check2",
             "159-app-image-fields",
-            "674-hiddenapi",
             "649-vdex-duplicate-method",
+            "674-hiddenapi",
+            "690-hiddenapi-same-name-methods",
             "804-class-extends-itself",
             "921-hello-failure",
             "999-redefine-hiddenapi"
@@ -555,6 +556,7 @@
             "629-vdex-speed",
             "647-jni-get-field-id",
             "674-hiddenapi",
+            "690-hiddenapi-same-name-methods",
             "944-transform-classloaders",
             "999-redefine-hiddenapi"
         ],
@@ -1085,6 +1087,7 @@
                   "678-quickening",
                   "679-locks",
                   "688-shared-library",
+                  "690-hiddenapi-same-name-methods",
                   "999-redefine-hiddenapi",
                   "1000-non-moving-space-stress",
                   "1001-app-image-regions",
@@ -1182,5 +1185,13 @@
         "variant": "jit-on-first-use",
         "bug": "b/120112467",
         "description": [ "Fails on Android Build hosts with uncaught std::bad_alloc." ]
+    },
+    {
+        "tests": ["719-dm-verify-redefinition"],
+        "variant": "jvm | speed-profile | interp-ac | target | no-prebuild",
+        "description": ["Doesn't run on RI because of boot class redefintion.",
+                        "Doesn't work with profiles because the run-test is not setup to",
+                        "support both. It also needs full verification, so no interp-ac.",
+                        "Requires zip, which isn't available on device"]
     }
 ]
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index 755104b..6be243a 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -77,6 +77,7 @@
   make_command+=" debuggerd su"
   make_command+=" libstdc++ "
   make_command+=" ${ANDROID_PRODUCT_OUT#"${ANDROID_BUILD_TOP}/"}/system/etc/public.libraries.txt"
+  make_command+=" art-bionic-files"
   if [[ -n "$ART_TEST_CHROOT" ]]; then
     # These targets are needed for the chroot environment.
     make_command+=" crash_dump event-log-tags"
diff --git a/tools/dexanalyze/Android.bp b/tools/dexanalyze/Android.bp
index a85bf56..a232a1b 100644
--- a/tools/dexanalyze/Android.bp
+++ b/tools/dexanalyze/Android.bp
@@ -24,11 +24,6 @@
         "dexanalyze_experiments.cc",
         "dexanalyze_strings.cc",
     ],
-    target: {
-        android: {
-            shared_libs: ["libcutils"],
-        },
-    },
     header_libs: [
         "art_cmdlineparser_headers",
     ],
diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc
index f426d02..141dd22 100644
--- a/tools/hiddenapi/hiddenapi.cc
+++ b/tools/hiddenapi/hiddenapi.cc
@@ -18,6 +18,8 @@
 #include <iostream>
 #include <map>
 #include <set>
+#include <string>
+#include <string_view>
 
 #include "android-base/stringprintf.h"
 #include "android-base/strings.h"
@@ -27,6 +29,7 @@
 #include "base/mem_map.h"
 #include "base/os.h"
 #include "base/stl_util.h"
+#include "base/string_view_cpp20.h"
 #include "base/unix_file/fd_file.h"
 #include "dex/art_dex_file_loader.h"
 #include "dex/class_accessor-inl.h"
@@ -124,6 +127,7 @@
   }
 
   inline bool IsPublic() const { return HasAccessFlags(kAccPublic); }
+  inline bool IsInterface() const { return HasAccessFlags(kAccInterface); }
 
   inline bool Equals(const DexClass& other) const {
     bool equals = strcmp(GetDescriptor(), other.GetDescriptor()) == 0;
@@ -344,13 +348,13 @@
   // See comment on Hierarchy::ForEachResolvableMember.
   template<typename Fn>
   bool ForEachResolvableMember(const DexMember& other, Fn fn) {
-    return ForEachResolvableMember_Impl(other, fn) != ResolutionResult::kNotFound;
+    std::vector<HierarchyClass*> visited;
+    return ForEachResolvableMember_Impl(other, fn, true, true, visited);
   }
 
   // Returns true if this class contains at least one member matching `other`.
   bool HasMatchingMember(const DexMember& other) {
-    return ForEachMatchingMember(
-        other, [](const DexMember&) { return true; }) != ResolutionResult::kNotFound;
+    return ForEachMatchingMember(other, [](const DexMember&) { return true; });
   }
 
   // Recursively iterates over all subclasses of this class and invokes `fn`
@@ -366,62 +370,60 @@
   }
 
  private:
-  // Result of resolution which takes into account whether the member was found
-  // for the first time or not. This is just a performance optimization to prevent
-  // re-visiting previously visited members.
-  // Note that order matters. When accumulating results, we always pick the maximum.
-  enum class ResolutionResult {
-    kNotFound,
-    kFoundOld,
-    kFoundNew,
-  };
-
-  inline ResolutionResult Accumulate(ResolutionResult a, ResolutionResult b) {
-    return static_cast<ResolutionResult>(
-        std::max(static_cast<unsigned>(a), static_cast<unsigned>(b)));
-  }
-
   template<typename Fn>
-  ResolutionResult ForEachResolvableMember_Impl(const DexMember& other, Fn fn) {
-    // First try to find a member matching `other` in this class.
-    ResolutionResult foundInClass = ForEachMatchingMember(other, fn);
-
-    switch (foundInClass) {
-      case ResolutionResult::kFoundOld:
-        // A matching member was found and previously explored. All subclasses
-        // must have been explored too.
-        break;
-
-      case ResolutionResult::kFoundNew:
-        // A matching member was found and this was the first time it was visited.
-        // If it is a virtual method, visit all methods overriding/implementing it too.
-        if (other.IsVirtualMethod()) {
-          for (HierarchyClass* subclass : extended_by_) {
-            subclass->ForEachOverridingMember(other, fn);
-          }
-        }
-        break;
-
-      case ResolutionResult::kNotFound:
-        // A matching member was not found in this class. Explore the superclasses
-        // and implemented interfaces.
-        for (HierarchyClass* superclass : extends_) {
-          foundInClass = Accumulate(
-              foundInClass, superclass->ForEachResolvableMember_Impl(other, fn));
-        }
-        break;
+  bool ForEachResolvableMember_Impl(const DexMember& other,
+                                    Fn fn,
+                                    bool allow_explore_up,
+                                    bool allow_explore_down,
+                                    std::vector<HierarchyClass*> visited) {
+    if (std::find(visited.begin(), visited.end(), this) == visited.end()) {
+      visited.push_back(this);
+    } else {
+      return false;
     }
 
-    return foundInClass;
+    // First try to find a member matching `other` in this class.
+    bool found = ForEachMatchingMember(other, fn);
+
+    // If not found, see if it is inherited from parents. Note that this will not
+    // revisit parents already in `visited`.
+    if (!found && allow_explore_up) {
+      for (HierarchyClass* superclass : extends_) {
+        found |= superclass->ForEachResolvableMember_Impl(
+            other,
+            fn,
+            /* allow_explore_up */ true,
+            /* allow_explore_down */ false,
+            visited);
+      }
+    }
+
+    // If this is a virtual method, continue exploring into subclasses so as to visit
+    // all overriding methods. Allow subclasses to explore their superclasses if this
+    // is an interface. This is needed to find implementations of this interface's
+    // methods inherited from superclasses (b/122551864).
+    if (allow_explore_down && other.IsVirtualMethod()) {
+      for (HierarchyClass* subclass : extended_by_) {
+        subclass->ForEachResolvableMember_Impl(
+            other,
+            fn,
+            /* allow_explore_up */ GetOneDexClass().IsInterface(),
+            /* allow_explore_down */ true,
+            visited);
+      }
+    }
+
+    return found;
   }
 
   template<typename Fn>
-  ResolutionResult ForEachMatchingMember(const DexMember& other, Fn fn) {
-    ResolutionResult found = ResolutionResult::kNotFound;
+  bool ForEachMatchingMember(const DexMember& other, Fn fn) {
+    bool found = false;
     auto compare_member = [&](const DexMember& member) {
+      // TODO(dbrazdil): Check whether class of `other` can access `member`.
       if (member == other) {
-        found = Accumulate(found, fn(member) ? ResolutionResult::kFoundNew
-                                             : ResolutionResult::kFoundOld);
+        found = true;
+        fn(member);
       }
     };
     for (const DexClass& dex_class : dex_classes_) {
@@ -435,20 +437,6 @@
     return found;
   }
 
-  template<typename Fn>
-  void ForEachOverridingMember(const DexMember& other, Fn fn) {
-    CHECK(other.IsVirtualMethod());
-    ResolutionResult found = ForEachMatchingMember(other, fn);
-    if (found == ResolutionResult::kFoundOld) {
-      // No need to explore further.
-      return;
-    } else {
-      for (HierarchyClass* subclass : extended_by_) {
-        subclass->ForEachOverridingMember(other, fn);
-      }
-    }
-  }
-
   // DexClass entries of this class found across all the provided dex files.
   std::vector<DexClass> dex_classes_;
 
@@ -902,45 +890,48 @@
     argc--;
 
     if (argc > 0) {
-      const StringPiece command(argv[0]);
+      const char* raw_command = argv[0];
+      const std::string_view command(raw_command);
       if (command == "encode") {
         for (int i = 1; i < argc; ++i) {
-          const StringPiece option(argv[i]);
-          if (option.starts_with("--input-dex=")) {
-            boot_dex_paths_.push_back(option.substr(strlen("--input-dex=")).ToString());
-          } else if (option.starts_with("--output-dex=")) {
-            output_dex_paths_.push_back(option.substr(strlen("--output-dex=")).ToString());
-          } else if (option.starts_with("--api-flags=")) {
-            api_flags_path_ = option.substr(strlen("--api-flags=")).ToString();
+          const char* raw_option = argv[i];
+          const std::string_view option(raw_option);
+          if (StartsWith(option, "--input-dex=")) {
+            boot_dex_paths_.push_back(std::string(option.substr(strlen("--input-dex="))));
+          } else if (StartsWith(option, "--output-dex=")) {
+            output_dex_paths_.push_back(std::string(option.substr(strlen("--output-dex="))));
+          } else if (StartsWith(option, "--api-flags=")) {
+            api_flags_path_ = std::string(option.substr(strlen("--api-flags=")));
           } else if (option == "--no-force-assign-all") {
             force_assign_all_ = false;
           } else {
-            Usage("Unknown argument '%s'", option.data());
+            Usage("Unknown argument '%s'", raw_option);
           }
         }
         return Command::kEncode;
       } else if (command == "list") {
         for (int i = 1; i < argc; ++i) {
-          const StringPiece option(argv[i]);
-          if (option.starts_with("--boot-dex=")) {
-            boot_dex_paths_.push_back(option.substr(strlen("--boot-dex=")).ToString());
-          } else if (option.starts_with("--public-stub-classpath=")) {
+          const char* raw_option = argv[i];
+          const std::string_view option(raw_option);
+          if (StartsWith(option, "--boot-dex=")) {
+            boot_dex_paths_.push_back(std::string(option.substr(strlen("--boot-dex="))));
+          } else if (StartsWith(option, "--public-stub-classpath=")) {
             stub_classpaths_.push_back(std::make_pair(
-                option.substr(strlen("--public-stub-classpath=")).ToString(),
+                std::string(option.substr(strlen("--public-stub-classpath="))),
                 ApiList::Whitelist()));
-          } else if (option.starts_with("--core-platform-stub-classpath=")) {
+          } else if (StartsWith(option, "--core-platform-stub-classpath=")) {
             stub_classpaths_.push_back(std::make_pair(
-                option.substr(strlen("--core-platform-stub-classpath=")).ToString(),
+                std::string(option.substr(strlen("--core-platform-stub-classpath="))),
                 ApiList::CorePlatformApi()));
-          } else if (option.starts_with("--out-api-flags=")) {
-            api_flags_path_ = option.substr(strlen("--out-api-flags=")).ToString();
+          } else if (StartsWith(option, "--out-api-flags=")) {
+            api_flags_path_ = std::string(option.substr(strlen("--out-api-flags=")));
           } else {
-            Usage("Unknown argument '%s'", option.data());
+            Usage("Unknown argument '%s'", raw_option);
           }
         }
         return Command::kList;
       } else {
-        Usage("Unknown command '%s'", command.data());
+        Usage("Unknown command '%s'", raw_command);
       }
     } else {
       Usage("No command specified");
@@ -1070,12 +1061,7 @@
                   std::string entry = boot_member.GetApiEntry();
                   auto it = boot_members.find(entry);
                   CHECK(it != boot_members.end());
-                  if (it->second.Contains(stub_api_list)) {
-                    return false;  // has been marked before
-                  } else {
-                    it->second |= stub_api_list;
-                    return true;  // marked for the first time
-                  }
+                  it->second |= stub_api_list;
                 });
             if (!resolved) {
               unresolved.insert(stub_member.GetApiEntry());
diff --git a/tools/hiddenapi/hiddenapi_test.cc b/tools/hiddenapi/hiddenapi_test.cc
index 7ef5b3d..74feb8a 100644
--- a/tools/hiddenapi/hiddenapi_test.cc
+++ b/tools/hiddenapi/hiddenapi_test.cc
@@ -16,6 +16,8 @@
 
 #include <fstream>
 
+#include "android-base/strings.h"
+
 #include "base/unix_file/fd_file.h"
 #include "base/zip_archive.h"
 #include "common_runtime_test.h"
@@ -41,9 +43,9 @@
     return file_path;
   }
 
-  std::unique_ptr<const DexFile> RunHiddenApi(const ScratchFile& flags_csv,
-                                              const std::vector<std::string>& extra_args,
-                                              ScratchFile* out_dex) {
+  std::unique_ptr<const DexFile> RunHiddenapiEncode(const ScratchFile& flags_csv,
+                                                    const std::vector<std::string>& extra_args,
+                                                    const ScratchFile& out_dex) {
     std::string error;
     ScratchFile in_dex;
     std::unique_ptr<ZipArchive> jar(
@@ -68,18 +70,42 @@
     argv_str.insert(argv_str.end(), extra_args.begin(), extra_args.end());
     argv_str.push_back("encode");
     argv_str.push_back("--input-dex=" + in_dex.GetFilename());
-    argv_str.push_back("--output-dex=" + out_dex->GetFilename());
+    argv_str.push_back("--output-dex=" + out_dex.GetFilename());
     argv_str.push_back("--api-flags=" + flags_csv.GetFilename());
     argv_str.push_back("--no-force-assign-all");
     int return_code = ExecAndReturnCode(argv_str, &error);
     if (return_code == 0) {
-      return OpenDex(*out_dex);
+      return OpenDex(out_dex);
     } else {
       LOG(ERROR) << "HiddenApi binary exited with unexpected return code " << return_code;
       return nullptr;
     }
   }
 
+  bool RunHiddenapiList(const ScratchFile& out_flags_csv) {
+    std::string error;
+    std::string boot_jar = GetTestDexFileName("HiddenApi");
+    std::string stub_jar = GetTestDexFileName("HiddenApiStubs");
+    std::string boot_cp = android::base::Join(GetLibCoreDexFileNames(), ":");
+
+    std::vector<std::string> argv_str;
+    argv_str.push_back(GetHiddenApiCmd());
+    argv_str.push_back("list");
+    for (const std::string& core_jar : GetLibCoreDexFileNames()) {
+      argv_str.push_back("--boot-dex=" + core_jar);
+    }
+    argv_str.push_back("--boot-dex=" + boot_jar);
+    argv_str.push_back("--public-stub-classpath=" + boot_cp + ":" + stub_jar);
+    argv_str.push_back("--out-api-flags=" + out_flags_csv.GetFilename());
+    int return_code = ExecAndReturnCode(argv_str, &error);
+    if (return_code == 0) {
+      return true;
+    } else {
+      LOG(ERROR) << "HiddenApi binary exited with unexpected return code " << return_code;
+      return false;
+    }
+  }
+
   std::unique_ptr<const DexFile> OpenDex(const ScratchFile& file) {
     ArtDexFileLoader dex_loader;
     std::string error_msg;
@@ -113,6 +139,31 @@
     return ofs;
   }
 
+  std::map<std::string, std::string> ReadFlagsCsvFile(const ScratchFile& file) {
+    std::ifstream ifs(file.GetFilename());
+    std::map<std::string, std::string> flags;
+
+    for (std::string line; std::getline(ifs, line);) {
+      std::size_t comma = line.find(",");
+      if (comma == std::string::npos) {
+        flags.emplace(line, "");
+      } else {
+        flags.emplace(line.substr(0, comma), line.substr(comma + 1));
+      }
+    }
+
+    return flags;
+  }
+
+  std::string SafeMapGet(const std::string& key, const std::map<std::string, std::string>& map) {
+    auto it = map.find(key);
+    if (it == map.end()) {
+      LOG(FATAL) << "Key not found: " << key;
+      UNREACHABLE();
+    }
+    return it->second;
+  }
+
   const dex::ClassDef& FindClass(const char* desc, const DexFile& dex_file) {
     const dex::TypeId* type_id = dex_file.FindTypeId(desc);
     CHECK(type_id != nullptr) << "Could not find class " << desc;
@@ -220,7 +271,7 @@
       << "LMain;->ifield:LBadType1;,greylist" << std::endl
       << "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl
       << "LMain;->ifield:LBadType3;,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_NE(dex_file.get(), nullptr);
   ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetIFieldHiddenFlags(*dex_file));
 }
@@ -231,7 +282,7 @@
       << "LMain;->ifield:I,greylist" << std::endl
       << "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl
       << "LMain;->ifield:LBadType3;,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_NE(dex_file.get(), nullptr);
   ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetIFieldHiddenFlags(*dex_file));
 }
@@ -242,7 +293,7 @@
       << "LMain;->ifield:LBadType1;,greylist" << std::endl
       << "LMain;->ifield:I,greylist-max-o" << std::endl
       << "LMain;->ifield:LBadType3;,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_NE(dex_file.get(), nullptr);
   ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetIFieldHiddenFlags(*dex_file));
 }
@@ -253,7 +304,7 @@
       << "LMain;->ifield:LBadType1;,greylist" << std::endl
       << "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl
       << "LMain;->ifield:I,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_NE(dex_file.get(), nullptr);
   ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetIFieldHiddenFlags(*dex_file));
 }
@@ -263,7 +314,7 @@
   OpenStream(flags_csv)
       << "LMain;->ifield:LBadType1;,greylist" << std::endl
       << "LMain;->ifield:I,blacklist,greylist-max-o" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_EQ(dex_file.get(), nullptr);
 }
 
@@ -272,7 +323,7 @@
   OpenStream(flags_csv)
       << "LMain;->ifield:LBadType2;,greylist-max-o" << std::endl
       << "LMain;->ifield:I,blacklist,greylist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_EQ(dex_file.get(), nullptr);
 }
 
@@ -281,7 +332,7 @@
   OpenStream(flags_csv)
       << "LMain;->ifield:I,greylist,greylist-max-o" << std::endl
       << "LMain;->ifield:LBadType3;,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_EQ(dex_file.get(), nullptr);
 }
 
@@ -291,7 +342,7 @@
       << "LMain;->sfield:LBadType1;,greylist" << std::endl
       << "LMain;->sfield:LBadType2;,greylist-max-o" << std::endl
       << "LMain;->sfield:LBadType3;,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_NE(dex_file.get(), nullptr);
   ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetSFieldHiddenFlags(*dex_file));
 }
@@ -302,7 +353,7 @@
       << "LMain;->sfield:Ljava/lang/Object;,greylist" << std::endl
       << "LMain;->sfield:LBadType2;,greylist-max-o" << std::endl
       << "LMain;->sfield:LBadType3;,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_NE(dex_file.get(), nullptr);
   ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetSFieldHiddenFlags(*dex_file));
 }
@@ -313,7 +364,7 @@
       << "LMain;->sfield:LBadType1;,greylist" << std::endl
       << "LMain;->sfield:Ljava/lang/Object;,greylist-max-o" << std::endl
       << "LMain;->sfield:LBadType3;,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_NE(dex_file.get(), nullptr);
   ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetSFieldHiddenFlags(*dex_file));
 }
@@ -324,7 +375,7 @@
       << "LMain;->sfield:LBadType1;,greylist" << std::endl
       << "LMain;->sfield:LBadType2;,greylist-max-o" << std::endl
       << "LMain;->sfield:Ljava/lang/Object;,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_NE(dex_file.get(), nullptr);
   ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetSFieldHiddenFlags(*dex_file));
 }
@@ -334,7 +385,7 @@
   OpenStream(flags_csv)
       << "LMain;->sfield:LBadType1;,greylist" << std::endl
       << "LMain;->sfield:Ljava/lang/Object;,blacklist,greylist-max-o" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_EQ(dex_file.get(), nullptr);
 }
 
@@ -343,7 +394,7 @@
   OpenStream(flags_csv)
       << "LMain;->sfield:LBadType2;,greylist-max-o" << std::endl
       << "LMain;->sfield:Ljava/lang/Object;,blacklist,greylist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_EQ(dex_file.get(), nullptr);
 }
 
@@ -352,7 +403,7 @@
   OpenStream(flags_csv)
       << "LMain;->sfield:Ljava/lang/Object;,greylist,greylist-max-o" << std::endl
       << "LMain;->sfield:LBadType3;,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_EQ(dex_file.get(), nullptr);
 }
 
@@ -362,7 +413,7 @@
       << "LMain;->imethod(LBadType1;)V,greylist" << std::endl
       << "LMain;->imethod(LBadType2;)V,greylist-max-o" << std::endl
       << "LMain;->imethod(LBadType3;)V,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_NE(dex_file.get(), nullptr);
   ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetIMethodHiddenFlags(*dex_file));
 }
@@ -373,7 +424,7 @@
       << "LMain;->imethod(J)V,greylist" << std::endl
       << "LMain;->imethod(LBadType2;)V,greylist-max-o" << std::endl
       << "LMain;->imethod(LBadType3;)V,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_NE(dex_file.get(), nullptr);
   ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetIMethodHiddenFlags(*dex_file));
 }
@@ -384,7 +435,7 @@
       << "LMain;->imethod(LBadType1;)V,greylist" << std::endl
       << "LMain;->imethod(J)V,greylist-max-o" << std::endl
       << "LMain;->imethod(LBadType3;)V,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_NE(dex_file.get(), nullptr);
   ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetIMethodHiddenFlags(*dex_file));
 }
@@ -395,7 +446,7 @@
       << "LMain;->imethod(LBadType1;)V,greylist" << std::endl
       << "LMain;->imethod(LBadType2;)V,greylist-max-o" << std::endl
       << "LMain;->imethod(J)V,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_NE(dex_file.get(), nullptr);
   ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetIMethodHiddenFlags(*dex_file));
 }
@@ -405,7 +456,7 @@
   OpenStream(flags_csv)
       << "LMain;->imethod(LBadType1;)V,greylist" << std::endl
       << "LMain;->imethod(J)V,blacklist,greylist-max-o" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_EQ(dex_file.get(), nullptr);
 }
 
@@ -414,7 +465,7 @@
   OpenStream(flags_csv)
       << "LMain;->imethod(LBadType2;)V,greylist-max-o" << std::endl
       << "LMain;->imethod(J)V,blacklist,greylist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_EQ(dex_file.get(), nullptr);
 }
 
@@ -423,7 +474,7 @@
   OpenStream(flags_csv)
       << "LMain;->imethod(J)V,greylist,greylist-max-o" << std::endl
       << "LMain;->imethod(LBadType3;)V,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_EQ(dex_file.get(), nullptr);
 }
 
@@ -433,7 +484,7 @@
       << "LMain;->smethod(LBadType1;)V,greylist" << std::endl
       << "LMain;->smethod(LBadType2;)V,greylist-max-o" << std::endl
       << "LMain;->smethod(LBadType3;)V,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_NE(dex_file.get(), nullptr);
   ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetSMethodHiddenFlags(*dex_file));
 }
@@ -444,7 +495,7 @@
       << "LMain;->smethod(Ljava/lang/Object;)V,greylist" << std::endl
       << "LMain;->smethod(LBadType2;)V,greylist-max-o" << std::endl
       << "LMain;->smethod(LBadType3;)V,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_NE(dex_file.get(), nullptr);
   ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetSMethodHiddenFlags(*dex_file));
 }
@@ -455,7 +506,7 @@
       << "LMain;->smethod(LBadType1;)V,greylist" << std::endl
       << "LMain;->smethod(Ljava/lang/Object;)V,greylist-max-o" << std::endl
       << "LMain;->smethod(LBadType3;)V,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_NE(dex_file.get(), nullptr);
   ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetSMethodHiddenFlags(*dex_file));
 }
@@ -466,7 +517,7 @@
       << "LMain;->smethod(LBadType1;)V,greylist" << std::endl
       << "LMain;->smethod(LBadType2;)V,greylist-max-o" << std::endl
       << "LMain;->smethod(Ljava/lang/Object;)V,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_NE(dex_file.get(), nullptr);
   ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetSMethodHiddenFlags(*dex_file));
 }
@@ -476,7 +527,7 @@
   OpenStream(flags_csv)
       << "LMain;->smethod(LBadType1;)V,greylist" << std::endl
       << "LMain;->smethod(Ljava/lang/Object;)V,blacklist,greylist-max-o" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_EQ(dex_file.get(), nullptr);
 }
 
@@ -485,7 +536,7 @@
   OpenStream(flags_csv)
       << "LMain;->smethod(LBadType2;)V,greylist-max-o" << std::endl
       << "LMain;->smethod(Ljava/lang/Object;)V,blacklist,greylist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_EQ(dex_file.get(), nullptr);
 }
 
@@ -494,7 +545,7 @@
   OpenStream(flags_csv)
       << "LMain;->smethod(Ljava/lang/Object;)V,greylist,greylist-max-o" << std::endl
       << "LMain;->smethod(LBadType3;)V,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_EQ(dex_file.get(), nullptr);
 }
 
@@ -504,7 +555,7 @@
       << "LMain;->inmethod(LBadType1;)V,greylist" << std::endl
       << "LMain;->inmethod(LBadType2;)V,greylist-max-o" << std::endl
       << "LMain;->inmethod(LBadType3;)V,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_NE(dex_file.get(), nullptr);
   ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetINMethodHiddenFlags(*dex_file));
 }
@@ -515,7 +566,7 @@
       << "LMain;->inmethod(C)V,greylist" << std::endl
       << "LMain;->inmethod(LBadType2;)V,greylist-max-o" << std::endl
       << "LMain;->inmethod(LBadType3;)V,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_NE(dex_file.get(), nullptr);
   ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetINMethodHiddenFlags(*dex_file));
 }
@@ -526,7 +577,7 @@
       << "LMain;->inmethod(LBadType1;)V,greylist" << std::endl
       << "LMain;->inmethod(C)V,greylist-max-o" << std::endl
       << "LMain;->inmethod(LBadType3;)V,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_NE(dex_file.get(), nullptr);
   ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetINMethodHiddenFlags(*dex_file));
 }
@@ -537,7 +588,7 @@
       << "LMain;->inmethod(LBadType1;)V,greylist" << std::endl
       << "LMain;->inmethod(LBadType2;)V,greylist-max-o" << std::endl
       << "LMain;->inmethod(C)V,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_NE(dex_file.get(), nullptr);
   ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetINMethodHiddenFlags(*dex_file));
 }
@@ -547,7 +598,7 @@
   OpenStream(flags_csv)
       << "LMain;->inmethod(LBadType1;)V,greylist" << std::endl
       << "LMain;->inmethod(C)V,blacklist,greylist-max-o" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_EQ(dex_file.get(), nullptr);
 }
 
@@ -556,7 +607,7 @@
   OpenStream(flags_csv)
       << "LMain;->inmethod(C)V,blacklist,greylist" << std::endl
       << "LMain;->inmethod(LBadType2;)V,greylist-max-o" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_EQ(dex_file.get(), nullptr);
 }
 
@@ -565,7 +616,7 @@
   OpenStream(flags_csv)
       << "LMain;->inmethod(C)V,greylist,greylist-max-o" << std::endl
       << "LMain;->inmethod(LBadType3;)V,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_EQ(dex_file.get(), nullptr);
 }
 
@@ -575,7 +626,7 @@
       << "LMain;->snmethod(LBadType1;)V,greylist" << std::endl
       << "LMain;->snmethod(LBadType2;)V,greylist-max-o" << std::endl
       << "LMain;->snmethod(LBadType3;)V,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_NE(dex_file.get(), nullptr);
   ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetSNMethodHiddenFlags(*dex_file));
 }
@@ -586,7 +637,7 @@
       << "LMain;->snmethod(Ljava/lang/Integer;)V,greylist" << std::endl
       << "LMain;->snmethod(LBadType2;)V,greylist-max-o" << std::endl
       << "LMain;->snmethod(LBadType3;)V,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_NE(dex_file.get(), nullptr);
   ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetSNMethodHiddenFlags(*dex_file));
 }
@@ -597,7 +648,7 @@
       << "LMain;->snmethod(LBadType1;)V,greylist" << std::endl
       << "LMain;->snmethod(Ljava/lang/Integer;)V,greylist-max-o" << std::endl
       << "LMain;->snmethod(LBadType3;)V,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_NE(dex_file.get(), nullptr);
   ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetSNMethodHiddenFlags(*dex_file));
 }
@@ -608,7 +659,7 @@
       << "LMain;->snmethod(LBadType1;)V,greylist" << std::endl
       << "LMain;->snmethod(LBadType2;)V,greylist-max-o" << std::endl
       << "LMain;->snmethod(Ljava/lang/Integer;)V,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_NE(dex_file.get(), nullptr);
   ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetSNMethodHiddenFlags(*dex_file));
 }
@@ -618,7 +669,7 @@
   OpenStream(flags_csv)
       << "LMain;->snmethod(LBadType1;)V,greylist" << std::endl
       << "LMain;->snmethod(Ljava/lang/Integer;)V,blacklist,greylist-max-o" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_EQ(dex_file.get(), nullptr);
 }
 
@@ -627,7 +678,7 @@
   OpenStream(flags_csv)
       << "LMain;->snmethod(Ljava/lang/Integer;)V,blacklist,greylist" << std::endl
       << "LMain;->snmethod(LBadType2;)V,greylist-max-o" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_EQ(dex_file.get(), nullptr);
 }
 
@@ -636,8 +687,35 @@
   OpenStream(flags_csv)
       << "LMain;->snmethod(Ljava/lang/Integer;)V,greylist,greylist-max-o" << std::endl
       << "LMain;->snmethod(LBadType3;)V,blacklist" << std::endl;
-  auto dex_file = RunHiddenApi(flags_csv, {}, &dex);
+  auto dex_file = RunHiddenapiEncode(flags_csv, {}, dex);
   ASSERT_EQ(dex_file.get(), nullptr);
 }
 
+// The following tests use this class hierarchy:
+//
+//    AbstractPackageClass  PublicInterface
+//           |                     |
+//           |    ┌----------------┘
+//           |    |
+//        PackageClass
+//
+// Only PublicInterface is in stubs.
+
+// Test a method declared in PublicInterface and defined in PackageClass.
+TEST_F(HiddenApiTest, InterfaceMethodImplemented) {
+  ScratchFile flags_csv;
+  ASSERT_TRUE(RunHiddenapiList(flags_csv));
+  auto flags = ReadFlagsCsvFile(flags_csv);
+  ASSERT_EQ(SafeMapGet("LPackageClass;->publicMethod1()V", flags), "whitelist");
+}
+
+// Test a method declared in PublicInterface, defined in AbstractPackageClass and
+// inherited by PackageClass.
+TEST_F(HiddenApiTest, InterfaceMethodImplementedInParent) {
+  ScratchFile flags_csv;
+  ASSERT_TRUE(RunHiddenapiList(flags_csv));
+  auto flags = ReadFlagsCsvFile(flags_csv);
+  ASSERT_EQ(SafeMapGet("LAbstractPackageClass;->publicMethod2()V", flags), "whitelist");
+}
+
 }  // namespace art
diff --git a/tools/libcore_gcstress_debug_failures.txt b/tools/libcore_gcstress_debug_failures.txt
index 25a4c82..9009a4e 100644
--- a/tools/libcore_gcstress_debug_failures.txt
+++ b/tools/libcore_gcstress_debug_failures.txt
@@ -32,6 +32,12 @@
         ]
 },
 {
+  description: "Timeouts on host with gcstress and debug.",
+  result: EXEC_FAILED,
+  modes: [host],
+  names: ["jsr166.StampedLockTest#testWriteAfterReadLock"]
+},
+{
   description: "Sometimes times out with gcstress and debug.",
   result: EXEC_FAILED,
   bug: 78228743,
diff --git a/tools/ti-fast/README.md b/tools/ti-fast/README.md
index a0a7dd7..c8cf180 100644
--- a/tools/ti-fast/README.md
+++ b/tools/ti-fast/README.md
@@ -84,10 +84,15 @@
 
 * `GarbageCollectionFinish`
 
+* `VMStart`
+
+* `VMInit`
+
+* `VMDeath`
+
 All other events cannot be listened for by this agent. Most of these missing
 events either require the use of other functions in order to be called
-(`FramePop`, `ObjectFree`, etc) or are only called once (`VMInit`, `VMDeath`,
-etc).
+(`FramePop`, `ObjectFree`, etc).
 
 #### ART
 >    `art -Xplugin:$ANDROID_HOST_OUT/lib64/libopenjdkjvmti.so '-agentpath:libtifast.so=MethodEntry' -cp tmp/java/helloworld.dex -Xint helloworld`
diff --git a/tools/ti-fast/tifast.cc b/tools/ti-fast/tifast.cc
index d02e549..8a6ac36 100644
--- a/tools/ti-fast/tifast.cc
+++ b/tools/ti-fast/tifast.cc
@@ -88,6 +88,9 @@
     fun(MonitorWaited, EVENT(MONITOR_WAITED), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jobject obj, jboolean b1), (jvmti, jni, jthreadContainer{.thread = thread}, obj, b1)) \
     fun(ResourceExhausted, EVENT(RESOURCE_EXHAUSTED), (jvmtiEnv* jvmti, JNIEnv* jni, jint i1, const void* cv, const char* cc), (jvmti, jni, i1, cv, cc)) \
     fun(VMObjectAlloc, EVENT(VM_OBJECT_ALLOC), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jobject obj, jclass klass, jlong l1), (jvmti, jni, jthreadContainer{.thread = thread}, obj, klass, jlongContainer{.val = l1})) \
+    fun(VMInit, EVENT(VM_INIT), (jvmtiEnv* jvmti, JNIEnv* jni, jthread thread), (jvmti, jni, jthreadContainer{.thread = thread})) \
+    fun(VMStart, EVENT(VM_START), (jvmtiEnv* jvmti, JNIEnv* jni), (jvmti, jni)) \
+    fun(VMDeath, EVENT(VM_DEATH), (jvmtiEnv* jvmti, JNIEnv* jni), (jvmti, jni)) \
 
 #define FOR_ALL_SUPPORTED_NO_JNI_EVENTS(fun) \
     fun(CompiledMethodLoad, EVENT(COMPILED_METHOD_LOAD), (jvmtiEnv* jvmti, jmethodID meth, jint i1, const void* cv1, jint i2, const jvmtiAddrLocationMap* alm, const void* cv2), (jvmti, meth, i1, cv1, i2, alm, cv2)) \
diff --git a/tools/tracefast-plugin/Android.bp b/tools/tracefast-plugin/Android.bp
index 1d7dd30..b7ae6c6 100644
--- a/tools/tracefast-plugin/Android.bp
+++ b/tools/tracefast-plugin/Android.bp
@@ -30,11 +30,6 @@
         "libbase",
     ],
     target: {
-        android: {
-            shared_libs: [
-                "libcutils",
-            ],
-        },
         darwin: {
             enabled: false,
         },