Package dexpreopt artifacts for libcore jars in the ART apex.

This patchset removes the obsolete preinstall hook that was used to
run dex2oat to dexpreopt bootclasspath jars when installing the apex
on device.

Instead, this patchset adds precompiled dexpreopt files for the
libcore part of bootclasspath in the apex. See the related patch in
build/soong.

The dexpreopt files are packaged in dexpreopt/$ARCH/ subdirectory
and have names prefixed with 'boot-art'.

Test: m
Test: m com.android.art deapexer \
    && find $ANDROID_BUILD_TOP -type f -name 'com.android.art.*.apex \
        | xargs deapexer | grep boot \
    Expect to find dexpreopt/$ARCH/boot-art*.{art,oat,vdex} files.
Test: art/build/apex/runtests.sh

Change-Id: I52a9a4d726d0da5a1cc52644351275cd3bb0f0d3
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index ceb3093..29c86b7 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -231,7 +231,6 @@
     binaries: [
         "art_postinstall_hook",
         "art_preinstall_hook",
-        "art_preinstall_hook_boot",
         "art_preinstall_hook_system_server",
         "art_prepostinstall_utils",
     ],
@@ -269,7 +268,7 @@
 // Release version of the ART APEX module (not containing debug
 // variants nor tools), included in user builds. Also used for
 // storage-constrained devices in userdebug and eng builds.
-apex {
+art_apex {
     name: "com.android.art.release",
     defaults: ["com.android.art-defaults"],
     certificate: ":com.android.art.certificate",
@@ -278,7 +277,7 @@
 // "Debug" version of the ART APEX module (containing both release and
 // debug variants, as well as additional tools), included in userdebug and
 // eng build.
-apex {
+art_apex {
     name: "com.android.art.debug",
     defaults: ["com.android.art-dev-defaults"],
     certificate: ":com.android.art.certificate",
@@ -311,7 +310,7 @@
 // "Testing" version of the ART APEX module (containing both release
 // and debug variants, additional tools, and ART gtests), for testing
 // purposes only.
-apex_test {
+art_apex_test {
     name: "com.android.art.testing",
     defaults: ["com.android.art-dev-defaults"],
     file_contexts: "com.android.art.debug",
@@ -471,11 +470,6 @@
 }
 
 sh_binary {
-    name: "art_preinstall_hook_boot",
-    src: "art_preinstall_hook_boot.sh",
-}
-
-sh_binary {
     name: "art_preinstall_hook_system_server",
     src: "art_preinstall_hook_system_server.sh",
 }
diff --git a/build/apex/art_apex_test.py b/build/apex/art_apex_test.py
index d9e5c08..017bb46 100755
--- a/build/apex/art_apex_test.py
+++ b/build/apex/art_apex_test.py
@@ -267,6 +267,14 @@
       return False, '%s is a directory'
     return True, ''
 
+  def is_dir(self, path):
+    fs_object = self._provider.get(path)
+    if fs_object is None:
+      return False, 'Could not find %s'
+    if not fs_object.is_dir:
+      return False, '%s is not a directory'
+    return True, ''
+
   def check_file(self, path):
     ok, msg = self.is_file(path)
     if not ok:
@@ -294,27 +302,30 @@
       self.fail('%s is not a symlink', path)
     self._expected_file_globs.add(path)
 
-  def check_art_test_executable(self, filename):
-    # This is a simplistic implementation, as we declare victory as soon as the
-    # test binary is found for one of the supported (not built) architectures.
-    # Ideally we would propagate the built architectures from the build system
-    # to this script and require test binaries for all of them to be present.
-    # Note that this behavior is not specific to this method: there are other
-    # places in this script where we rely on this simplified strategy.
+  def arch_dirs_for_path(self, path):
+    # Look for target-specific subdirectories for the given directory path.
+    # This is needed because the list of build targets is not propagated
+    # to this script.
     #
-    # TODO: Implement the suggestion above (here and in other places in this
-    # script).
-    test_found = False
+    # TODO: Pass build target information to this script and fix all places
+    # where this function in used (or similar workarounds).
+    dirs = []
     for arch in ARCHS:
-      test_path = '%s/%s/%s' % (ART_TEST_DIR, arch, filename)
-      test_is_file, _ = self.is_file(test_path)
-      if test_is_file:
-        test_found = True
-        self._expected_file_globs.add(test_path)
-        if not self._provider.get(test_path).is_exec:
-          self.fail('%s is not executable', test_path)
-    if not test_found:
+      dir = '%s/%s' % (path, arch)
+      found, _ = self.is_dir(dir)
+      if found:
+        dirs.append(dir)
+    return dirs
+
+  def check_art_test_executable(self, filename):
+    dirs = self.arch_dirs_for_path(ART_TEST_DIR)
+    if not dirs:
       self.fail('ART test binary missing: %s', filename)
+    for dir in dirs:
+      test_path = '%s/%s' % (dir, filename)
+      self._expected_file_globs.add(test_path)
+      if not self._provider.get(test_path).is_exec:
+        self.fail('%s is not executable', test_path)
 
   def check_single_library(self, filename):
     lib_path = 'lib/%s' % filename
@@ -356,6 +367,14 @@
     for unexpected_path in set(paths) - expected_paths:
       self.fail('Unexpected file \'%s\'', unexpected_path)
 
+  def check_dexpreopt(self, basename):
+    dirs = self.arch_dirs_for_path('dexpreopt')
+    if not dirs:
+      self.fail('Could not find dexpreopt directory for any arch.')
+    for dir in dirs:
+      for ext in ['art', 'oat', 'vdex']:
+        self.check_file('%s/%s.%s' % (dir, basename, ext))
+
   # Just here for docs purposes, even if it isn't good Python style.
 
   def check_symlinked_multilib_executable(self, filename):
@@ -522,6 +541,13 @@
     self._checker.check_optional_native_library('libclang_rt.hwasan*')
     self._checker.check_optional_native_library('libclang_rt.ubsan*')
 
+    # Check dexpreopt files for libcore bootclasspath jars
+    self._checker.check_dexpreopt('boot-art')
+    self._checker.check_dexpreopt('boot-art-apache-xml')
+    self._checker.check_dexpreopt('boot-art-bouncycastle')
+    self._checker.check_dexpreopt('boot-art-core-icu4j')
+    self._checker.check_dexpreopt('boot-art-core-libart')
+    self._checker.check_dexpreopt('boot-art-okhttp')
 
 class ReleaseTargetChecker:
   def __init__(self, checker):
@@ -534,7 +560,6 @@
     # Check the APEX package scripts.
     self._checker.check_executable('art_postinstall_hook')
     self._checker.check_executable('art_preinstall_hook')
-    self._checker.check_executable('art_preinstall_hook_boot')
     self._checker.check_executable('art_preinstall_hook_system_server')
     self._checker.check_executable('art_prepostinstall_utils')
 
diff --git a/build/apex/art_preinstall_hook.sh b/build/apex/art_preinstall_hook.sh
index 94a1b21..82db5d2 100644
--- a/build/apex/art_preinstall_hook.sh
+++ b/build/apex/art_preinstall_hook.sh
@@ -36,8 +36,6 @@
   # Create OTA folders.
   mkdir -p /data/ota/dalvik-cache/$ARCH || exit 104
   rm -rf /data/ota/dalvik-cache/$ARCH/* || exit 105
-
-  `dirname $0`/art_preinstall_hook_boot $ARCH || exit 200
 done
 
 PRIMARY_ARCH=`echo $ARCHES | sed -e 's/ .*//'`
diff --git a/build/apex/art_preinstall_hook_boot.sh b/build/apex/art_preinstall_hook_boot.sh
deleted file mode 100644
index 0985bef..0000000
--- a/build/apex/art_preinstall_hook_boot.sh
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/system/bin/sh
-
-# 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.
-#
-
-. `dirname $0`/art_prepostinstall_utils || exit 100
-
-log_info "Preparing boot image compilation parameters"
-
-# Prefer DEX2OATBOOTCLASSPATH, then BOOTCLASSPATH.
-USED_CLASSPATH=$DEX2OATBOOTCLASSPATH
-if [ -z "$USED_CLASSPATH" ] ; then
-  USED_CLASSPATH=$BOOTCLASSPATH
-  if [ -z "$USED_CLASSPATH" ] ; then
-    log_error "Could not find boot class-path to compile"
-    exit 101
-  fi
-fi
-BOOTCP=`echo $USED_CLASSPATH | tr ":" "\n"`
-
-DEX_FILES=
-DEX_LOCATIONS=
-for component in $BOOTCP ; do
-  DEX_FILES="$DEX_FILES --dex-file=$component"
-  DEX_LOCATIONS="$DEX_LOCATIONS --dex-location=$component"
-done
-
-PROFILING=
-if [ -f "/system/etc/boot-image.prof" ] ; then
-  PROFILING="--compiler-filter=speed-profile --profile-file=/system/etc/boot-image.prof"
-fi
-if [ -f "/system/etc/dirty-image-objects" ] ; then
-  PROFILING="$PROFILING --dirty-image-objects=/system/etc/dirty-image-objects"
-fi
-
-DEX2OAT_IMAGE_XMX=`getprop dalvik.vm.image-dex2oat-Xmx`
-
-DEX2OAT_TARGET_ARCH=$1
-DEX2OAT_TARGET_CPU_VARIANT=`getprop dalvik.vm.isa.${DEX2OAT_TARGET_ARCH}.variant`
-DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES=`getprop dalvik.vm.isa.${DEX2OAT_TARGET_ARCH}.features`
-
-log_info "Compiling boot image for $DEX2OAT_TARGET_ARCH"
-
-dex2oat \
-  --avoid-storing-invocation \
-  --runtime-arg -Xmx$DEX2OAT_IMAGE_XMX \
-  $PROFILING \
-  $DEX_FILES \
-  $DEX_LOCATIONS \
-  --generate-mini-debug-info \
-  --strip \
-  --oat-file=/data/dalvik-cache/$DEX2OAT_TARGET_ARCH/system@framework@boot.oat \
-  --oat-location=/data/dalvik-cache/$DEX2OAT_TARGET_ARCH/system@framework@boot.oat \
-  --image=/data/dalvik-cache/$DEX2OAT_TARGET_ARCH/system@framework@boot.art --base=0x70000000 \
-  --instruction-set=$DEX2OAT_TARGET_ARCH \
-  --instruction-set-variant=$DEX2OAT_TARGET_CPU_VARIANT \
-  --instruction-set-features=$DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES \
-  --android-root=/system \
-  --no-inline-from=core-oj.jar \
-  --abort-on-hard-verifier-error \
-  --force-determinism || { log_error "Dex2oat failed" ; exit 102 ; }
diff --git a/build/art.go b/build/art.go
index 56eec54..4c1099b 100644
--- a/build/art.go
+++ b/build/art.go
@@ -312,6 +312,10 @@
 	android.RegisterModuleType("art_global_defaults", artGlobalDefaultsFactory)
 	android.RegisterModuleType("art_debug_defaults", artDebugDefaultsFactory)
 
+	// ART apex is special because it must include dexpreopt files for bootclasspath jars.
+	android.RegisterModuleType("art_apex", artApexBundleFactory)
+	android.RegisterModuleType("art_apex_test", artTestApexBundleFactory)
+
 	// TODO: This makes the module disable itself for host if HOST_PREFER_32_BIT is
 	// set. We need this because the multilib types of binaries listed in the apex
 	// rule must match the declared type. This is normally not difficult but HOST_PREFER_32_BIT
@@ -321,8 +325,16 @@
 	android.RegisterModuleType("art_apex_test_host", artHostTestApexBundleFactory)
 }
 
+func artApexBundleFactory() android.Module {
+	return apex.ApexBundleFactory(false /*testApex*/, true /*artApex*/)
+}
+
+func artTestApexBundleFactory() android.Module {
+	return apex.ApexBundleFactory(true /*testApex*/, true /*artApex*/)
+}
+
 func artHostTestApexBundleFactory() android.Module {
-	module := apex.ApexBundleFactory( /*testApex*/ true)
+	module := apex.ApexBundleFactory(true /*testApex*/, true /*artApex*/)
 	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
 		if envTrue(ctx, "HOST_PREFER_32_BIT") {
 			type props struct {