Minimal Android Runtime APEX module.

Introduce build rules to generate com.android.runtime, a minimal
Android Runtime APEX module containing just ART and its dependencies.

When module com.android.runtime is built (`make com.android.runtime`),
make produces a `com.android.runtime.apex` package in the `apex`
directory of the system partition
(`$ANDROID_PRODUCT_OUT/system/apex/com.android.runtime.apex`).

This module is not built by default when generating a system image.

Test: make com.android.runtime
Test: art/build/apex/runtests.sh
Bug: 113373927
Change-Id: I2019bd1934558feba6eccef13b887b2faa96caec
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
new file mode 100644
index 0000000..bca2959
--- /dev/null
+++ b/build/apex/Android.bp
@@ -0,0 +1,100 @@
+// Android Runtime APEX module.
+
+// Modules listed in LOCAL_REQUIRED_MODULES for module art-runtime in art/Android.mk.
+// - Base requirements (binaries for which both 32- and 64-bit versions are built, if relevant).
+art_runtime_base_binaries_both = [
+    "dalvikvm",
+]
+// - Base requirements (binaries for which a 32-bit version is preferred).
+art_runtime_base_binaries_prefer32 = [
+    "dex2oat",
+    "dexoptanalyzer",
+    "profman",
+]
+// - Base requirements (libraries).
+art_runtime_base_native_shared_libs = [
+    "libart",
+    "libart-compiler",
+    "libopenjdkjvm",
+    "libopenjdkjvmti",
+    "libadbconnection",
+]
+// - Fake library that avoids namespace issues and gives some warnings for nosy apps.
+art_runtime_fake_native_shared_libs = [
+     // FIXME: Does not work as-is, because `libart_fake` is defined in libart_fake/Android.mk,
+     // and because a module defined in a Blueprint file cannot depend on a module defined in a
+     // Makefile. To support `libart_fake` as a dependency of this APEX module, we can either
+     // (probably in that order of preference):
+     // a. translate that logic into Blueprint; or
+     // b. write the whole Android Runtime APEX generation logic in Android.mk; or
+     // c. introduce an `art_apex` module type extending the `apex` module type and write the
+     //    corresponding Go logic to handle this extra dependency.
+     //"libart_fake",
+]
+// - Debug variants (binaries for which a 32-bit version is preferred).
+//   FIXME: These modules are optional (the built product can decide to include them or not).
+//   Should they be moved to another APEX file?
+art_runtime_debug_binaries_prefer32 = [
+    "dex2oatd",
+    "dexoptanalyzerd",
+    "profmand",
+]
+art_runtime_debug_native_shared_libs = [
+    "libartd",
+    "libartd-compiler",
+    "libopenjdkd",
+    "libopenjdkjvmd",
+    "libopenjdkjvmtid",
+    "libadbconnectiond",
+]
+
+// Modules listed in LOCAL_REQUIRED_MODULES for module art-tools in art/Android.mk.
+art_tools_binaries = [
+    "dexdiag",
+    "dexdump",
+    "dexlist",
+    "oatdump",
+]
+
+// Host-only modules listed in LOCAL_REQUIRED_MODULES for module art-tools in art/Android.mk.
+// TODO: Include these modules in the future "host APEX".
+art_tools_host_binaries = [
+    // FIXME: Does not work as-is, because `ahat` is defined in tools/ahat/Android.mk
+    // (same issue as for `libart_fake` above).
+    //"ahat",
+    "hprof-conv",
+    // ...
+]
+
+apex_key {
+    name: "com.android.runtime.key",
+    public_key: "runtime.avbpubkey",
+    private_key: "runtime.pem",
+}
+
+apex {
+    name: "com.android.runtime",
+    compile_multilib: "both",
+    manifest: "manifest.json",
+    file_contexts: "file_contexts",
+    native_shared_libs: art_runtime_base_native_shared_libs
+        + art_runtime_fake_native_shared_libs
+        + art_runtime_debug_native_shared_libs,
+    multilib: {
+        both: {
+            // TODO: Add logic to create a `dalvikvm` symlink to `dalvikvm32` or `dalvikvm64`
+            // (see `symlink_preferred_arch` in art/dalvikvm/Android.bp).
+            binaries: art_runtime_base_binaries_both,
+        },
+        prefer32: {
+            binaries: art_runtime_base_binaries_prefer32
+                + art_runtime_debug_binaries_prefer32
+        },
+        first: {
+            binaries: art_tools_binaries,
+        }
+    },
+    key: "com.android.runtime.key",
+    // TODO: Also package a `ld.config.txt` config file (to be placed in `etc/`).
+    // ...
+}
diff --git a/build/apex/file_contexts b/build/apex/file_contexts
new file mode 100644
index 0000000..4d0df80
--- /dev/null
+++ b/build/apex/file_contexts
@@ -0,0 +1,13 @@
+#############################
+# APEX module manifest.
+#
+/manifest\.json          u:object_r:system_file:s0
+
+#############################
+# System files
+#
+(/.*)?                   u:object_r:system_file:s0
+/bin/dex2oat(d)?         u:object_r:dex2oat_exec:s0
+/bin/dexoptanalyzer(d)?  u:object_r:dexoptanalyzer_exec:s0
+/bin/profman(d)?         u:object_r:profman_exec:s0
+/lib(64)?(/.*)?          u:object_r:system_lib_file:s0
diff --git a/build/apex/manifest.json b/build/apex/manifest.json
new file mode 100644
index 0000000..20a8314
--- /dev/null
+++ b/build/apex/manifest.json
@@ -0,0 +1,4 @@
+{
+  "name": "com.android.runtime",
+  "version": 1
+}
diff --git a/build/apex/runtests.sh b/build/apex/runtests.sh
new file mode 100755
index 0000000..6af2a8b
--- /dev/null
+++ b/build/apex/runtests.sh
@@ -0,0 +1,201 @@
+#!/bin/bash
+
+# Copyright (C) 2018 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.
+#
+
+# Run Android Runtime APEX tests.
+
+function say {
+  echo "$0: $*"
+}
+
+function die {
+  echo "$0: $*"
+  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."
+
+# Fail early.
+set -e
+
+build_apex_p=true
+list_image_files_p=false
+
+function usage {
+  cat <<EOF
+Usage: $0 [OPTION]
+Build (optional) and run tests on Android Runtime APEX package (on host).
+
+  -s, --skip-build    skip the build step
+  -l, --list-files    list the contents of the ext4 image
+  -h, --help          display this help and exit
+
+EOF
+  exit
+}
+
+while [[ $# -gt 0 ]]; do
+  case "$1" in
+    (-s|--skip-build) build_apex_p=false;;
+    (-l|--list-files) list_image_files_p=true;;
+    (-h|--help) usage;;
+    (*) die "Unknown option: '$1'
+Try '$0 --help' for more information.";;
+  esac
+  shift
+done
+
+work_dir=$(mktemp -d)
+mount_point="$work_dir/image"
+
+# Garbage collection.
+function finish {
+  # Don't fail early during cleanup.
+  set +e
+  guestunmount "$mount_point"
+  rm -rf "$work_dir"
+}
+
+trap finish EXIT
+
+apex_module="com.android.runtime"
+
+# Build the Android Runtime APEX package (optional).
+$build_apex_p && say "Building package" && make "$apex_module"
+
+system_apexdir="$ANDROID_PRODUCT_OUT/system/apex"
+apex_package="$system_apexdir/$apex_module.apex"
+
+say "Extracting and mounting image"
+
+# Extract the image from the Android Runtime APEX.
+image_filename="image.img"
+unzip -q "$apex_package" "$image_filename" -d "$work_dir"
+mkdir "$mount_point"
+image_file="$work_dir/$image_filename"
+
+# Check filesystems in the image.
+image_filesystems="$work_dir/image_filesystems"
+virt-filesystems -a "$image_file" >"$image_filesystems"
+# We expect a single partition (/dev/sda) in the image.
+partition="/dev/sda"
+echo "$partition" | cmp "$image_filesystems" -
+
+# Mount the image from the Android Runtime APEX.
+guestmount -a "$image_file" -m "$partition" "$mount_point"
+
+# List the contents of the mounted image (optional).
+$list_image_files_p && say "Listing image files" && ls -ld "$mount_point" && tree -ap "$mount_point"
+
+say "Running tests"
+
+# Check that the mounted image contains a manifest.
+[[ -f "$mount_point/manifest.json" ]]
+
+function check_binary {
+  [[ -x "$mount_point/bin/$1" ]] || die "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?
+  [[ -x "$mount_point/bin/${1}32" ]] || [[ -x "$mount_point/bin/${1}64" ]] \
+    || die "Cannot find binary '$1' in mounted image"
+}
+
+function check_binary_symlink {
+  [[ -h "$mount_point/bin/$1" ]] || die "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" ]] \
+    || die "Cannot find library '$1' in mounted image"
+}
+
+# Check that the mounted image contains ART base binaries.
+check_multilib_binary dalvikvm
+# TODO: Does not work yet.
+: check_binary_symlink dalvikvm
+check_binary dex2oat
+check_binary dexoptanalyzer
+check_binary profman
+
+# Check that the mounted image contains ART tools binaries.
+check_binary dexdiag
+check_binary dexdump
+check_binary dexlist
+check_binary oatdump
+
+# Check that the mounted image contains ART debug binaries.
+check_binary dex2oatd
+check_binary dexoptanalyzerd
+check_binary profmand
+
+# Check that the mounted image contains ART libraries.
+check_library libart-compiler.so
+check_library libart.so
+check_library libopenjdkjvm.so
+check_library libopenjdkjvmti.so
+check_library libadbconnection.so
+# TODO: Should we check for these libraries too, even if they are not explicitly
+# listed as dependencies in the Android Runtime APEX module rule?
+check_library libartbase.so
+check_library libart-dexlayout.so
+check_library libart-disassembler.so
+check_library libdexfile.so
+check_library libprofile.so
+
+# Check that the mounted image contains ART debug libraries.
+check_library libartd-compiler.so
+check_library libartd.so
+check_library libdexfiled.so
+check_library libopenjdkd.so
+check_library libopenjdkjvmd.so
+check_library libopenjdkjvmtid.so
+check_library libadbconnectiond.so
+# TODO: Should we check for these libraries too, even if they are not explicitly
+# listed as dependencies in the Android Runtime APEX module rule?
+check_library libartbased.so
+check_library libartd-dexlayout.so
+check_library libprofiled.so
+
+# TODO: Should we check for other libraries, such as:
+#
+#   libbacktrace.so
+#   libbase.so
+#   liblog.so
+#   libsigchain.so
+#   libtombstoned_client.so
+#   libunwindstack.so
+#   libvixl-arm64.so
+#   libvixl-arm.so
+#   libvixld-arm64.so
+#   libvixld-arm.so
+#   ...
+#
+# ?
+
+say "Tests passed"
diff --git a/build/apex/runtime.avbpubkey b/build/apex/runtime.avbpubkey
new file mode 100644
index 0000000..b0ffc9b
--- /dev/null
+++ b/build/apex/runtime.avbpubkey
Binary files differ
diff --git a/build/apex/runtime.pem b/build/apex/runtime.pem
new file mode 100644
index 0000000..4c7ce4b
--- /dev/null
+++ b/build/apex/runtime.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index 666db42..fd5f3cd 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -183,7 +183,7 @@
 
     target: {
         android: {
-            // Use the 32-bit version of dex2oat on devices
+            // Use the 32-bit version of dex2oat on devices.
             compile_multilib: "prefer32",
         },
     },
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 50785c5..d901c01 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -186,7 +186,8 @@
 
   // Construct the final output.
   if (command.size() <= 1U) {
-    // It seems only "/system/bin/dex2oat" is left, or not even that. Use a pretty line.
+    // It seems only "/apex/com.android.runtime/bin/dex2oat" is left, or not
+    // even that. Use a pretty line.
     return "Starting dex2oat.";
   }
   return android::base::Join(command, ' ');
diff --git a/dexoptanalyzer/Android.bp b/dexoptanalyzer/Android.bp
index 99a11cd..72896c8 100644
--- a/dexoptanalyzer/Android.bp
+++ b/dexoptanalyzer/Android.bp
@@ -24,6 +24,7 @@
 
     target: {
         android: {
+            // Use the 32-bit version of dexoptanalyzer on devices.
             compile_multilib: "prefer32",
         },
     },
diff --git a/libartbase/base/logging.h b/libartbase/base/logging.h
index d2c0a02..9ded082 100644
--- a/libartbase/base/logging.h
+++ b/libartbase/base/logging.h
@@ -77,12 +77,12 @@
 // performed.
 extern const char* GetCmdLine();
 
-// The command used to start the ART runtime, such as "/system/bin/dalvikvm". If InitLogging hasn't
-// been performed then just returns "art"
+// The command used to start the ART runtime, such as "/apex/com.android.runtime/bin/dalvikvm". If
+// InitLogging hasn't been performed then just returns "art".
 extern const char* ProgramInvocationName();
 
 // A short version of the command used to start the ART runtime, such as "dalvikvm". If InitLogging
-// hasn't been performed then just returns "art"
+// hasn't been performed then just returns "art".
 extern const char* ProgramInvocationShortName();
 
 class LogHelper {
diff --git a/profman/Android.bp b/profman/Android.bp
index 5aaccb0..89e1f7e 100644
--- a/profman/Android.bp
+++ b/profman/Android.bp
@@ -26,6 +26,7 @@
 
     target: {
         android: {
+            // Use the 32-bit version of profman on devices.
             compile_multilib: "prefer32",
         },
     },