Merge "ART: Fix off-by-one error in BCE."
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index 9b5c638..a87e14c 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -195,6 +195,7 @@
     host_supported: true,
     device_supported: false,
     manifest: "manifest.json",
+    java_libs: libcore_target_java_libs,
     native_shared_libs: art_runtime_base_native_shared_libs
         + art_runtime_debug_native_shared_libs
         + libcore_native_shared_libs
diff --git a/build/apex/art_apex_test.py b/build/apex/art_apex_test.py
new file mode 100755
index 0000000..e636a72
--- /dev/null
+++ b/build/apex/art_apex_test.py
@@ -0,0 +1,463 @@
+#!/usr/bin/env python3
+
+# 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 argparse
+import logging
+import os
+import subprocess
+import sys
+import zipfile
+
+logging.basicConfig(format='%(message)s')
+
+class FSObject:
+  def __init__(self, name, is_dir, is_exec, is_symlink):
+    self.name = name
+    self.is_dir = is_dir
+    self.is_exec = is_exec
+    self.is_symlink = is_symlink
+  def __str__(self):
+    return '%s(dir=%r,exec=%r,symlink=%r)' % (self.name, self.is_dir, self.is_exec, self.is_symlink)
+
+class TargetApexProvider:
+  def __init__(self, apex, tmpdir, debugfs):
+    self._tmpdir = tmpdir
+    self._debugfs = debugfs
+    self._folder_cache = {}
+    self._payload = os.path.join(self._tmpdir, 'apex_payload.img')
+    # Extract payload to tmpdir.
+    zip = zipfile.ZipFile(apex)
+    zip.extract('apex_payload.img', 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]
+    # Cannot use check_output as it will annoy with stderr.
+    process = subprocess.Popen([self._debugfs, '-R', 'ls -l -p %s' % (dir), self._payload],
+                               stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+                               universal_newlines=True)
+    stdout, stderr = process.communicate()
+    res = str(stdout)
+    map = {}
+    # Debugfs output looks like this:
+    #   debugfs 1.44.4 (18-Aug-2018)
+    #   /12/040755/0/2000/.//
+    #   /2/040755/1000/1000/..//
+    #   /13/100755/0/2000/dalvikvm32/28456/
+    #   /14/100755/0/2000/dexoptanalyzer/20396/
+    #   /15/100755/0/2000/linker/1152724/
+    #   /16/100755/0/2000/dex2oat/563508/
+    #   /17/100755/0/2000/linker64/1605424/
+    #   /18/100755/0/2000/profman/85304/
+    #   /19/100755/0/2000/dalvikvm64/28576/
+    #    |     |   |   |       |        |
+    #    |     |   |   #- gid  #- name  #- size
+    #    |     |   #- uid
+    #    |     #- type and permission bits
+    #    #- inode nr (?)
+    #
+    # Note: could break just on '/' to avoid names with newlines.
+    for line in res.split("\n"):
+      if not line:
+        continue
+      comps = line.split('/')
+      if len(comps) != 8:
+        logging.warn('Could not break and parse line \'%s\'', line)
+        continue
+      bits = comps[2]
+      name = comps[5]
+      if len(bits) != 6:
+        logging.warn('Dont understand bits \'%s\'', bits)
+        continue
+      is_dir = True if bits[1] == '4' else False
+      def is_exec_bit(ch):
+        return True if int(ch) & 1 == 1 else False
+      is_exec = is_exec_bit(bits[3]) and is_exec_bit(bits[4]) and is_exec_bit(bits[5])
+      is_symlink = True if bits[1] == '2' else False
+      map[name] = FSObject(name, is_dir, is_exec, is_symlink)
+    self._folder_cache[dir] = map
+    return map
+
+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
+    logging.error(msg, args)
+
+  def error_count(self):
+    return self._errors
+
+  def is_file(self, file):
+    fs_object = self._provider.get(file)
+    if fs_object is None:
+      return (False, 'Could not find %s')
+    if fs_object.is_dir:
+      return (False, '%s is a directory')
+    return (True, '')
+
+  def check_file(self, file):
+    chk = self.is_file(file)
+    if not chk[0]:
+      self.fail(chk[1], file)
+    return chk[0]
+
+  def check_binary(self, file):
+    path = 'bin/%s' % (file)
+    if not self.check_file(path):
+      return False
+    if not self._provider.get(path).is_exec:
+      self.fail('%s is not executable', path)
+      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)
+    if fs_object is None:
+      self.fail('Could not find %s', path)
+      return False
+    if fs_object.is_dir:
+      self.fail('%s is a directory', path)
+      return False
+    if not fs_object.is_symlink:
+      self.fail('%s is not a symlink', path)
+      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))
+    if not res1[0] and not res2[0]:
+      self.fail('Library missing: %s', file)
+      return False
+    return True
+
+  def check_java_library(self, file):
+    return self.check_file('javalib/%s' % (file))
+
+class ReleaseChecker(Checker):
+  def __init__(self, provider):
+    super().__init__(provider)
+  def __str__(self):
+    return 'Release Checker'
+
+  def run(self):
+    # Check that the mounted image contains an APEX manifest.
+    self.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')
+
+    # 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')
+
+    # 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')
+    # 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')
+    # Check that the mounted image contains additional required libraries.
+    self.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
+    #   ...
+    #
+    # ?
+
+    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')
+
+class DebugChecker(Checker):
+  def __init__(self, provider):
+    super().__init__(provider)
+  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')
+
+    # 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')
+
+    # 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')
+    # Check that the mounted image contains Android Core debug libraries.
+    self.check_library('libopenjdkd.so')
+    # Check that the mounted image contains additional required debug libraries.
+    self.check_library('libadbconnectiond.so')
+
+class DebugTargetChecker(Checker):
+  def __init__(self, provider):
+    super().__init__(provider)
+  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')
+
+def print_list(provider):
+    def print_list_impl(provider, path):
+      map = provider.read_dir(path)
+      if map is None:
+        return
+      map = dict(map)
+      if '.' in map:
+        del map['.']
+      if '..' in map:
+        del map['..']
+      for (_, val) in sorted(map.items()):
+        new_path = os.path.join(path, val.name)
+        print(new_path)
+        if val.is_dir:
+          print_list_impl(provider, new_path)
+    print_list_impl(provider, '.')
+
+def print_tree(provider, title):
+    def get_vertical(has_next_list):
+      str = ''
+      for v in has_next_list:
+        str += '%s   ' % ('│' if v else ' ')
+      return str
+    def get_last_vertical(last):
+      return '└── ' if last else '├── ';
+    def print_tree_impl(provider, path, has_next_list):
+      map = provider.read_dir(path)
+      if map is None:
+        return
+      map = dict(map)
+      if '.' in map:
+        del map['.']
+      if '..' in map:
+        del map['..']
+      key_list = list(sorted(map.keys()))
+      for i in range(0, len(key_list)):
+        val = map[key_list[i]]
+        prev = get_vertical(has_next_list)
+        last = get_last_vertical(i == len(key_list) - 1)
+        print('%s%s%s' % (prev, last, val.name))
+        if val.is_dir:
+          has_next_list.append(i < len(key_list) - 1)
+          print_tree_impl(provider, os.path.join(path, val.name), has_next_list)
+          has_next_list.pop()
+    print('%s' % (title))
+    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")
+    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")
+    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:
+    logging.error("Need a tmpdir.")
+    return 1
+  if args.target and not args.debugfs:
+    logging.error("Need debugfs.")
+    return 1
+
+  try:
+    apex_provider = TargetApexProvider(args.apex, args.tmpdir, args.debugfs)
+  except Exception as e:
+    logging.error('Failed to create provider: %s', e)
+    return 1
+
+  if args.tree:
+    print_tree(apex_provider, args.apex)
+    return 0
+  if args.list:
+    print_list(apex_provider)
+    return 0
+
+  checkers = []
+  if args.host:
+    logging.error('host checking not yet supported')
+    return 1
+
+  checkers.append(ReleaseChecker(apex_provider))
+  if args.debug:
+    checkers.append(DebugChecker(apex_provider))
+  if args.debug and args.target:
+    checkers.append(DebugTargetChecker(apex_provider))
+
+  failed = False
+  for checker in checkers:
+    logging.info('%s...', checker)
+    checker.run()
+    if checker.error_count() > 0:
+      logging.error('%s FAILED', checker)
+      failed = True
+    else:
+      logging.info('%s SUCCEEDED', checker)
+
+  return 1 if failed else 0
+
+def artApexTestDefault(parser):
+  if not 'ANDROID_PRODUCT_OUT' in os.environ:
+    logging.error('No-argument use requires ANDROID_PRODUCT_OUT')
+    sys.exit(1)
+  product_out = os.environ['ANDROID_PRODUCT_OUT']
+  if not 'ANDROID_HOST_OUT' in os.environ:
+    logging.error('No-argument use requires ANDROID_HOST_OUT')
+    sys.exit(1)
+  host_out = os.environ['ANDROID_HOST_OUT']
+
+  args = parser.parse_args(['dummy'])  # For consistency.
+  args.debugfs = '%s/bin/debugfs' % (host_out)
+  args.tmpdir = '.'
+  args.tree = False
+  args.list = False
+  failed = False
+
+  if not os.path.exists(args.debugfs):
+    logging.error("Cannot find debugfs (default path %s). Please build it, e.g., m debugfs",
+                  args.debugfs)
+    sys.exit(1)
+
+  # 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},
+  ]
+
+  for config in configs:
+    logging.info(config['name'])
+    # TODO: Host will need different path.
+    args.apex = '%s/system/apex/%s.apex' % (product_out, config['name'])
+    if not os.path.exists(args.apex):
+      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)
+    if exit_code != 0:
+      failed = True
+
+  if failed:
+    sys.exit(1)
+
+if __name__ == "__main__":
+  parser = argparse.ArgumentParser(description='Check integrity of a Runtime APEX.')
+
+  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')
+  parser.add_argument('--tree', help='Print directory tree', action='store_true')
+
+  parser.add_argument('--tmpdir', help='Directory for temp files')
+  parser.add_argument('--debugfs', help='Path to debugfs')
+
+  if len(sys.argv) == 1:
+    artApexTestDefault(parser)
+  else:
+    args = parser.parse_args()
+
+    if args is None:
+      sys.exit(1)
+
+    exit_code = artApexTestMain(args)
+    sys.exit(exit_code)
diff --git a/build/apex/ld.config.txt b/build/apex/ld.config.txt
index d0145e4..1f0bf4d 100644
--- a/build/apex/ld.config.txt
+++ b/build/apex/ld.config.txt
@@ -33,6 +33,17 @@
 namespace.platform.link.default.shared_libs += libnativebridge.so
 namespace.platform.link.default.shared_libs += libnativehelper.so
 namespace.platform.link.default.shared_libs += libnativeloader.so
+# /system/lib/libc.so, etc are symlinks to /bionic/lib/libc.so, etc.
+# Add /bionic/lib to the permitted paths because linker uses realpath(3)
+# to check the accessibility of the lib. We could add this to search.paths
+# instead but that makes the resolution of bionic libs be dependent on
+# the order of /system/lib and /bionic/lib in search.paths. If /bionic/lib
+# is after /system/lib, then /bionic/lib is never tried because libc.so
+# is always found in /system/lib but fails to pass the accessibility test
+# because of its realpath.  It's better to not depend on the ordering if
+# possible.
+namespace.platform.permitted.paths = /bionic/${LIB}
+namespace.platform.asan.permitted.paths = /bionic/${LIB}
 
 # Note that we don't need to link the default namespace with conscrypt:
 # the runtime Java code and binaries do not explicitly load native libraries
diff --git a/build/apex/runtests.sh b/build/apex/runtests.sh
index 155709a..20da825 100755
--- a/build/apex/runtests.sh
+++ b/build/apex/runtests.sh
@@ -17,6 +17,8 @@
 
 # Run Android Runtime APEX tests.
 
+SCRIPT_DIR=$(dirname $0)
+
 # Status of whole test script.
 exit_status=0
 # Status of current test suite.
@@ -31,17 +33,17 @@
   exit 1
 }
 
-which guestmount >/dev/null && which guestunmount >/dev/null && which virt-filesystems >/dev/null \
-  || die "This script requires 'guestmount', 'guestunmount',
-and 'virt-filesystems' from libguestfs. On Debian-based systems, these tools
-can be installed with:
-
-   sudo apt-get install libguestfs-tools
-"
-
 [[ -n "$ANDROID_PRODUCT_OUT" ]] \
   || die "You need to source and lunch before you can use this script."
 
+[[ -n "$ANDROID_HOST_OUT" ]] \
+  || die "You need to source and lunch before you can use this script."
+
+if [ ! -e "$ANDROID_HOST_OUT/bin/debugfs" ] ; then
+  say "Could not find debugfs, building now."
+  make debugfs-host || die "Cannot build debugfs"
+fi
+
 # Fail early.
 set -e
 
@@ -112,6 +114,25 @@
   fi
 }
 
+# maybe_list_apex_contents_apex APEX TMPDIR [other]
+function maybe_list_apex_contents_apex {
+  local apex=$1
+  local tmpdir=$2
+  shift 2
+
+  # List the contents of the apex in list form.
+  if $list_image_files_p; then
+    say "Listing image files"
+    $SCRIPT_DIR/art_apex_test.py --list --tmpdir "$tmpdir" $@ $apex
+  fi
+
+  # List the contents of the apex in tree form.
+  if $print_image_tree_p; then
+    say "Printing image tree"
+    $SCRIPT_DIR/art_apex_test.py --tree --tmpdir "$tmpdir" $@ $apex
+  fi
+}
+
 function fail_check {
   echo "$0: FAILED: $*"
   test_status=1
@@ -148,9 +169,11 @@
 }
 
 function check_java_library {
-  [[ -x "$mount_point/javalib/$1" ]] || fail_check "Cannot find java library '$1' in mounted image"
+  [[ -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.
@@ -180,7 +203,6 @@
   # Check that the mounted image contains Android Core libraries.
   check_library "libexpat${host_suffix}.so"
   check_library libjavacore.so
-  check_library libjavacrypto.so
   check_library libopenjdk.so
   check_library "libz${host_suffix}.so"
   check_library libziparchive.so
@@ -201,14 +223,11 @@
   #
   # ?
 
-  # TODO: Enable for host
-  if [ $1 != "com.android.runtime.host" ]; then
-    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
-  fi
+  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`.
@@ -243,7 +262,6 @@
 
 # Clean-up.
 function cleanup_target {
-  guestunmount "$mount_point"
   rm -rf "$work_dir"
 }
 
@@ -254,34 +272,6 @@
   cleanup_target
 }
 
-# setup_target_apex APEX_MODULE MOUNT_POINT
-# -----------------------------------------
-# Extract image from target APEX_MODULE and mount it in MOUNT_POINT.
-function setup_target_apex {
-  local apex_module=$1
-  local mount_point=$2
-  local system_apexdir="$ANDROID_PRODUCT_OUT/system/apex"
-  local apex_package="$system_apexdir/$apex_module.apex"
-
-  say "Extracting and mounting image"
-
-  # Extract the payload from the Android Runtime APEX.
-  local image_filename="apex_payload.img"
-  unzip -q "$apex_package" "$image_filename" -d "$work_dir"
-  mkdir "$mount_point"
-  local image_file="$work_dir/$image_filename"
-
-  # Check filesystems in the image.
-  local image_filesystems="$work_dir/image_filesystems"
-  virt-filesystems -a "$image_file" >"$image_filesystems"
-  # We expect a single partition (/dev/sda) in the image.
-  local partition="/dev/sda"
-  echo "$partition" | cmp "$image_filesystems" -
-
-  # Mount the image from the Android Runtime APEX.
-  guestmount -a "$image_file" -m "$partition" --ro "$mount_point"
-}
-
 # Testing release APEX package (com.android.runtime.release).
 # -----------------------------------------------------------
 
@@ -291,23 +281,24 @@
 say "Processing APEX package $apex_module"
 
 work_dir=$(mktemp -d)
-mount_point="$work_dir/image"
-host_suffix=""
 
 trap finish_target EXIT
 
 # Build the APEX package (optional).
 build_apex "$apex_module"
-
-# Set up APEX package.
-setup_target_apex "$apex_module" "$mount_point"
+apex_path="$ANDROID_PRODUCT_OUT/system/apex/${apex_module}.apex"
 
 # List the contents of the APEX image (optional).
-maybe_list_apex_contents "$mount_point"
+maybe_list_apex_contents_apex $apex_path $work_dir --target --debugfs $ANDROID_HOST_OUT/bin/debugfs
 
 # Run tests on APEX package.
 say "Checking APEX package $apex_module"
-check_release_contents "$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"
 
 # Clean up.
 trap - EXIT
@@ -325,27 +316,25 @@
 say "Processing APEX package $apex_module"
 
 work_dir=$(mktemp -d)
-mount_point="$work_dir/image"
-host_suffix=""
 
 trap finish_target EXIT
 
 # Build the APEX package (optional).
 build_apex "$apex_module"
-
-# Set up APEX package.
-setup_target_apex "$apex_module" "$mount_point"
+apex_path="$ANDROID_PRODUCT_OUT/system/apex/${apex_module}.apex"
 
 # List the contents of the APEX image (optional).
-maybe_list_apex_contents "$mount_point"
+maybe_list_apex_contents_apex $apex_path $work_dir --target --debugfs $ANDROID_HOST_OUT/bin/debugfs
 
 # Run tests on APEX package.
 say "Checking APEX package $apex_module"
-check_release_contents "$apex_module"
-check_debug_contents
-# Check for files pulled in from debug target-only oatdump.
-check_binary oatdump
-check_library libart-disassembler.so
+$SCRIPT_DIR/art_apex_test.py \
+  --tmpdir $work_dir \
+  --debugfs $ANDROID_HOST_OUT/bin/debugfs \
+  --target \
+  --debug \
+  $apex_path \
+    || fail_check "Debug checks failed"
 
 # Clean up.
 trap - EXIT
diff --git a/build/art.go b/build/art.go
index 22f6410..5236e31 100644
--- a/build/art.go
+++ b/build/art.go
@@ -348,26 +348,7 @@
 func libartDefaultsFactory() android.Module {
 	c := &codegenProperties{}
 	module := cc.DefaultsFactory(c)
-	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
-		codegen(ctx, c, true)
-
-		type props struct {
-			Target struct {
-				Android struct {
-					Shared_libs []string
-				}
-			}
-		}
-
-		p := &props{}
-		// TODO: express this in .bp instead b/79671158
-		if !envTrue(ctx, "ART_TARGET_LINUX") {
-			p.Target.Android.Shared_libs = []string{
-				"libmetricslogger",
-			}
-		}
-		ctx.AppendProperties(p)
-	})
+	android.AddLoadHook(module, func(ctx android.LoadHookContext) { codegen(ctx, c, true) })
 
 	return module
 }
@@ -375,27 +356,7 @@
 func libartStaticDefaultsFactory() android.Module {
 	c := &codegenProperties{}
 	module := cc.DefaultsFactory(c)
-	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
-		codegen(ctx, c, true)
-
-		type props struct {
-			Target struct {
-				Android struct {
-					Static_libs []string
-				}
-			}
-		}
-
-		p := &props{}
-		// TODO: express this in .bp instead b/79671158
-		if !envTrue(ctx, "ART_TARGET_LINUX") {
-			p.Target.Android.Static_libs = []string{
-				"libmetricslogger",
-				"libstatssocket",
-			}
-		}
-		ctx.AppendProperties(p)
-	})
+	android.AddLoadHook(module, func(ctx android.LoadHookContext) {	codegen(ctx, c, true) })
 
 	return module
 }
diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc
index 7b1de01..8505b0c 100644
--- a/oatdump/oatdump_test.cc
+++ b/oatdump/oatdump_test.cc
@@ -92,7 +92,7 @@
   // Test is failing on target, b/77469384.
   TEST_DISABLED_FOR_TARGET();
   std::string error_msg;
-  ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {}));
+  ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {"--runtime-arg", "-Xmx64M"}));
   ASSERT_TRUE(Exec(kDynamic, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly));
   const std::string dex_location =
       tmp_dir_+ "/" + android::base::Basename(GetTestDexFileName(GetAppBaseName().c_str())) +
@@ -109,7 +109,7 @@
   TEST_DISABLED_FOR_ARM_AND_MIPS();
   TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
   std::string error_msg;
-  ASSERT_TRUE(GenerateAppOdexFile(kDynamic, {}));
+  ASSERT_TRUE(GenerateAppOdexFile(kStatic, {"--runtime-arg", "-Xmx64M"}));
   ASSERT_TRUE(Exec(kStatic, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly));
 }
 
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
index af5e67a..1279997 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -29,15 +29,6 @@
 #include "thread-inl.h"
 #include "well_known_classes.h"
 
-#ifdef ART_TARGET_ANDROID
-#include <metricslogger/metrics_logger.h>
-using android::metricslogger::ComplexEventLogger;
-using android::metricslogger::ACTION_HIDDEN_API_ACCESSED;
-using android::metricslogger::FIELD_HIDDEN_API_ACCESS_METHOD;
-using android::metricslogger::FIELD_HIDDEN_API_ACCESS_DENIED;
-using android::metricslogger::FIELD_HIDDEN_API_SIGNATURE;
-#endif
-
 namespace art {
 namespace hiddenapi {
 
@@ -182,28 +173,6 @@
   return member_name_ == other.member_name_ && type_signature_ == other.type_signature_;
 }
 
-#ifdef ART_TARGET_ANDROID
-// Convert an AccessMethod enum to a value for logging from the proto enum.
-// This method may look odd (the enum values are current the same), but it
-// prevents coupling the internal enum to the proto enum (which should never
-// be changed) so that we are free to change the internal one if necessary in
-// future.
-inline static int32_t GetEnumValueForLog(AccessMethod access_method) {
-  switch (access_method) {
-    case AccessMethod::kNone:
-      return android::metricslogger::ACCESS_METHOD_NONE;
-    case AccessMethod::kReflection:
-      return android::metricslogger::ACCESS_METHOD_REFLECTION;
-    case AccessMethod::kJNI:
-      return android::metricslogger::ACCESS_METHOD_JNI;
-    case AccessMethod::kLinking:
-      return android::metricslogger::ACCESS_METHOD_LINKING;
-    default:
-      DCHECK(false);
-  }
-}
-#endif
-
 void MemberSignature::LogAccessToEventLog(AccessMethod access_method, bool access_denied) {
 #ifdef ART_TARGET_ANDROID
   if (access_method == AccessMethod::kLinking || access_method == AccessMethod::kNone) {
@@ -213,19 +182,32 @@
     // None does not correspond to actual access, so should also be ignored.
     return;
   }
-  ComplexEventLogger log_maker(ACTION_HIDDEN_API_ACCESSED);
-  log_maker.AddTaggedData(FIELD_HIDDEN_API_ACCESS_METHOD, GetEnumValueForLog(access_method));
-  if (access_denied) {
-    log_maker.AddTaggedData(FIELD_HIDDEN_API_ACCESS_DENIED, 1);
+  Runtime* runtime = Runtime::Current();
+  if (runtime->IsAotCompiler()) {
+    return;
   }
+  JNIEnvExt* env = Thread::Current()->GetJniEnv();
   const std::string& package_name = Runtime::Current()->GetProcessPackageName();
-  if (!package_name.empty()) {
-    log_maker.SetPackageName(package_name);
+  ScopedLocalRef<jstring> package_str(env, env->NewStringUTF(package_name.c_str()));
+  if (env->ExceptionCheck()) {
+    env->ExceptionClear();
+    LOG(ERROR) << "Unable to allocate string for package name which called hidden api";
   }
   std::ostringstream signature_str;
   Dump(signature_str);
-  log_maker.AddTaggedData(FIELD_HIDDEN_API_SIGNATURE, signature_str.str());
-  log_maker.Record();
+  ScopedLocalRef<jstring> signature_jstr(env,
+      env->NewStringUTF(signature_str.str().c_str()));
+  if (env->ExceptionCheck()) {
+    env->ExceptionClear();
+    LOG(ERROR) << "Unable to allocate string for hidden api method signature";
+  }
+  env->CallStaticVoidMethod(WellKnownClasses::dalvik_system_VMRuntime,
+      WellKnownClasses::dalvik_system_VMRuntime_hiddenApiUsed, package_str.get(),
+      signature_jstr.get(), static_cast<jint>(access_method), access_denied);
+  if (env->ExceptionCheck()) {
+    env->ExceptionClear();
+    LOG(ERROR) << "Unable to report hidden api usage";
+  }
 #else
   UNUSED(access_method);
   UNUSED(access_denied);
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index 8bd59ea..e15e9f3 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -45,11 +45,13 @@
   return static_cast<EnforcementPolicy>(api_policy_int);
 }
 
+// Hidden API access method
+// Thist must be kept in sync with VMRuntime.HiddenApiUsageLogger.ACCESS_METHOD_*
 enum class AccessMethod {
-  kNone,  // internal test that does not correspond to an actual access by app
-  kReflection,
-  kJNI,
-  kLinking,
+  kNone = 0,  // internal test that does not correspond to an actual access by app
+  kReflection = 1,
+  kJNI = 2,
+  kLinking = 3,
 };
 
 // Represents the API domain of a caller/callee.
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index f61faa3..955a455 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -84,6 +84,7 @@
 
 jmethodID WellKnownClasses::dalvik_system_BaseDexClassLoader_getLdLibraryPath;
 jmethodID WellKnownClasses::dalvik_system_VMRuntime_runFinalization;
+jmethodID WellKnownClasses::dalvik_system_VMRuntime_hiddenApiUsed;
 jmethodID WellKnownClasses::java_lang_Boolean_valueOf;
 jmethodID WellKnownClasses::java_lang_Byte_valueOf;
 jmethodID WellKnownClasses::java_lang_Character_valueOf;
@@ -344,6 +345,7 @@
 
   dalvik_system_BaseDexClassLoader_getLdLibraryPath = CacheMethod(env, dalvik_system_BaseDexClassLoader, false, "getLdLibraryPath", "()Ljava/lang/String;");
   dalvik_system_VMRuntime_runFinalization = CacheMethod(env, dalvik_system_VMRuntime, true, "runFinalization", "(J)V");
+  dalvik_system_VMRuntime_hiddenApiUsed = CacheMethod(env, dalvik_system_VMRuntime, true, "hiddenApiUsed", "(Ljava/lang/String;Ljava/lang/String;IZ)V");
   java_lang_ClassNotFoundException_init = CacheMethod(env, java_lang_ClassNotFoundException, false, "<init>", "(Ljava/lang/String;Ljava/lang/Throwable;)V");
   java_lang_ClassLoader_loadClass = CacheMethod(env, java_lang_ClassLoader, false, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
 
@@ -486,6 +488,7 @@
 
   dalvik_system_BaseDexClassLoader_getLdLibraryPath = nullptr;
   dalvik_system_VMRuntime_runFinalization = nullptr;
+  dalvik_system_VMRuntime_hiddenApiUsed = nullptr;
   java_lang_Boolean_valueOf = nullptr;
   java_lang_Byte_valueOf = nullptr;
   java_lang_Character_valueOf = nullptr;
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index f0e98a8..872b562 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -93,6 +93,7 @@
 
   static jmethodID dalvik_system_BaseDexClassLoader_getLdLibraryPath;
   static jmethodID dalvik_system_VMRuntime_runFinalization;
+  static jmethodID dalvik_system_VMRuntime_hiddenApiUsed;
   static jmethodID java_lang_Boolean_valueOf;
   static jmethodID java_lang_Byte_valueOf;
   static jmethodID java_lang_Character_valueOf;
diff --git a/test/testrunner/run_build_test_target.py b/test/testrunner/run_build_test_target.py
index 139d1af..19f03c3 100755
--- a/test/testrunner/run_build_test_target.py
+++ b/test/testrunner/run_build_test_target.py
@@ -28,6 +28,7 @@
 
 import argparse
 import os
+import pathlib
 import subprocess
 import sys
 
@@ -108,7 +109,10 @@
   run_test_command = [os.path.join(env.ANDROID_BUILD_TOP,
                                    'art/test/testrunner/testrunner.py')]
   test_flags = target.get('run-test', [])
-  run_test_command += list(map(lambda a: a.format(SOONG_OUT_DIR=env.SOONG_OUT_DIR), test_flags))
+  out_dir = pathlib.PurePath(env.SOONG_OUT_DIR)
+  if not out_dir.is_absolute():
+    out_dir = pathlib.PurePath(env.ANDROID_BUILD_TOP).joinpath(out_dir)
+  run_test_command += list(map(lambda a: a.format(SOONG_OUT_DIR=str(out_dir)), test_flags))
   # Let testrunner compute concurrency based on #cpus.
   # b/65822340
   # run_test_command += ['-j', str(n_threads)]
diff --git a/tools/build_linux_bionic.sh b/tools/build_linux_bionic.sh
index d3c1912..b401071 100755
--- a/tools/build_linux_bionic.sh
+++ b/tools/build_linux_bionic.sh
@@ -35,14 +35,22 @@
 # Soong needs a bunch of variables set and will not run if they are missing.
 # The default values of these variables is only contained in make, so use
 # nothing to create the variables then remove all the other artifacts.
-build/soong/soong_ui.bash --make-mode nothing
+
+# TODO(b/123645297) Move hiddenapi steps to soong.
+#
+# Currently hiddenapi relies on .mk to build some of it's configuration files.
+# This prevents us from just cleaning using soong and forces us to do this
+# hacky workaround where we build the targets without linux_bionic and delete
+# the build-config files before going around again. If we fix this issue we can
+# change to only building 'nothing' instead.
+build/soong/soong_ui.bash --make-mode "$@"
+
 if [ $? != 0 ]; then
   exit 1
 fi
 
 out_dir=$(get_build_var OUT_DIR)
 host_out=$(get_build_var HOST_OUT)
-mk_product_out=$(get_build_var PRODUCT_OUT)
 
 # TODO(b/31559095) Figure out a better way to do this.
 #
@@ -51,14 +59,17 @@
 tmp_soong_var=$(mktemp --tmpdir soong.variables.bak.XXXXXX)
 
 cat $out_dir/soong/soong.variables > ${tmp_soong_var}
-build/soong/soong_ui.bash --make-mode clean
-mkdir -p $out_dir/soong
-mkdir -p $mk_product_out
 
-# TODO(b/31559095) Soong will panic if this file isn't present. It contains
-# information from MAKE needed to let soong handle the invocation of dex2oat.
-# This would be great to have but for now isn't needed.
-echo "{}" > $mk_product_out/dexpreopt.config
+# See comment above about b/123645297 for why we cannot just do m clean. Clear
+# out all files except for intermediates and installed files.
+find $out_dir/ -maxdepth 1 -mindepth 1 \
+               -not -name soong        \
+               -not -name host         \
+               -not -name target | xargs -I '{}' rm -rf '{}'
+find $out_dir/soong/ -maxdepth 1 -mindepth 1   \
+                     -not -name .intermediates \
+                     -not -name host           \
+                     -not -name target | xargs -I '{}' rm -rf '{}'
 
 python3 <<END - ${tmp_soong_var} ${out_dir}/soong/soong.variables
 import json
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index 398981e..5177919 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -236,9 +236,8 @@
   ]
 },
 {
-  description: "Expected networking failure on host: we expect 97 (EAFNOSUPPORT), but we get 22 (EINVAL)",
+  description: "Expected networking failure on host / old systems: we expect 97 (EAFNOSUPPORT), but we get 22 (EINVAL)",
   result: EXEC_FAILED,
-  modes: [host],
   names: [
     "libcore.libcore.io.OsTest#testCrossFamilyBindConnectSendto"
   ]