Initial commit of "HostStubGen" (Ravenwood)
See tools/hoststubgen/README.md for the directory structure...
This CL contains:
- The HostGenTool.
- Libraries to build / run host side tets. (helper-*/ directories)
- Currently we expose ArrayMap and Log to the host side, but we also need to
expose a lot more classes that the tests usee.
- Some sample tests. (test-framework/ and test-tiny-framework/)
Sample tests contain very small tests for ArrayMap and Log.
- This version doen't loa JNI code yet. It still uses the Java substitution
for Log's native methods.
This is because `libandroid_runtime` seems to have a lot of obscure dependencies,
and using `libandroid_runtime` could cause obscure build errors when someone
make chages to any of direct/indirect dependencies.
- Current version doesn't use any Java annotations to control what are exposed
on the host side. Instead, we use `framework-policy-override.txt`, which is
easier to change. (because changing the file wouln't require rebuilding
framework-minus-apex.jar.)
- Currently we expose ArrayMap and Log to the host side, but we also need to
expose a lot more classes that the tests usee. See the `framework-policy-override.txt`
file.
Test: ./scripts/run-all-tests.sh
Bug: 292141694
Change-Id: If149e26aa919d17a0b82dacc78f31bd79fbb110b
diff --git a/Android.bp b/Android.bp
index 431f0b9..8eebcbb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -163,6 +163,12 @@
//same purpose.
"//external/robolectric:__subpackages__",
"//frameworks/layoutlib:__subpackages__",
+
+ // This is for the same purpose as robolectric -- to build "framework.jar" for host-side
+ // testing.
+ // TODO: Once Ravenwood is stable, move the host side jar targets to this directory,
+ // and remove this line.
+ "//frameworks/base/tools/hoststubgen:__subpackages__",
],
}
@@ -405,6 +411,7 @@
"audiopolicy-aidl-java",
"sounddose-aidl-java",
"modules-utils-expresslog",
+ "hoststubgen-annotations",
],
}
diff --git a/tools/hoststubgen/.gitignore b/tools/hoststubgen/.gitignore
new file mode 100644
index 0000000..6453bde
--- /dev/null
+++ b/tools/hoststubgen/.gitignore
@@ -0,0 +1,3 @@
+out/
+*-out/
+*.log
diff --git a/tools/hoststubgen/README.md b/tools/hoststubgen/README.md
new file mode 100644
index 0000000..b0a1262
--- /dev/null
+++ b/tools/hoststubgen/README.md
@@ -0,0 +1,76 @@
+# HostStubGen
+
+## Overview
+
+This directory contains tools / sample code / investigation for host side test support.
+
+
+## Directories and files
+
+- `hoststubgen/`
+ Contains source code of the "hoststubgen" tool and relevant code
+
+ - `framework-policy-override.txt`
+ This file contains "policy overrides", which allows to control what goes to stub/impl without
+ having to touch the target java files. This allows quicker iteration, because you can skip
+ having to rebuild framework.jar.
+
+ - `src/`
+
+ HostStubGen tool source code.
+
+ - `annotations-src/` See `Android.bp`.
+ - `helper-framework-buildtime-src/` See `Android.bp`.
+ - `helper-framework-runtime-src/` See `Android.bp`.
+ - `helper-runtime-src/` See `Android.bp`.
+
+ - `test-tiny-framework/` See `README.md` in it.
+
+ - `test-framework` See `README.md` in it.
+
+- `scripts`
+ - `run-host-test.sh`
+
+ Run a host side test. Use it instead of `atest` for now. (`atest` works "mostly" but it has
+ problems.)
+
+ - `dump-jar.sh`
+
+ A script to dump the content of `*.class` and `*.jar` files.
+
+ - `run-all-tests.sh`
+
+ Run all tests. Many tests may fail, but at least this should run til the end.
+ (It should print `run-all-tests.sh finished` at the end)
+
+## Build and run
+
+### Building `HostStubGen` binary
+
+```
+m hoststubgen
+```
+
+### Run the tests
+
+- Run all relevant tests and test scripts. Some of thests are still expected to fail,
+ but this should print "finished, with no unexpected failures" at the end.
+
+ However, because some of the script it executes depend on internal file paths to Soong's
+ intermediate directory, some of it might fail when something changes in the build system.
+
+ We need proper build system integration to fix them.
+```
+$ ./scripts/run-all-tests.sh
+```
+
+- See also `README.md` in `test-*` directories.
+
+## TODOs, etc
+
+ - Make sure the parent's visibility is not smaller than the member's.
+
+- @HostSideTestNativeSubstitutionClass should automatically add class-keep to the substitute class.
+ (or at least check it.)
+
+ - The `HostStubGenTest-framework-test-host-test-lib` jar somehow contain all ASM classes? Figure out where the dependency is coming from.
diff --git a/tools/hoststubgen/TEST_MAPPING b/tools/hoststubgen/TEST_MAPPING
new file mode 100644
index 0000000..9703626
--- /dev/null
+++ b/tools/hoststubgen/TEST_MAPPING
@@ -0,0 +1,6 @@
+{
+ // TODO: Change to presubmit.
+ "postsubmit": [
+ { "name": "tiny-framework-dump-test" }
+ ]
+}
diff --git a/tools/hoststubgen/common.sh b/tools/hoststubgen/common.sh
new file mode 100644
index 0000000..b49ee39
--- /dev/null
+++ b/tools/hoststubgen/common.sh
@@ -0,0 +1,116 @@
+# Copyright (C) 2023 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.
+
+set -e # Exit at failure
+shopt -s globstar # Enable double-star wildcards (**)
+
+cd "${0%/*}" # Move to the script dir
+
+fail() {
+ echo "Error: $*" 1>&2
+ exit 1
+}
+
+# Print the arguments and then execute.
+run() {
+ echo "Running: $*" 1>&2
+ "$@"
+}
+
+# Concatenate the second and subsequent args with the first arg as a separator.
+# e.g. `join : a b c` -> prints `a:b:c`
+join() {
+ local IFS="$1"
+ shift
+ echo "$*"
+}
+
+abspath() {
+ for name in "${@}"; do
+ readlink -f $name
+ done
+}
+
+m() {
+ if (( $SKIP_BUILD )) ; then
+ echo "Skipping build: $*" 1>&2
+ return 0
+ fi
+ run ${ANDROID_BUILD_TOP}/build/soong/soong_ui.bash --make-mode "$@"
+}
+
+# Extract given jar files
+extract() {
+ for f in "${@}"; do
+ local out=$f.ext
+ run rm -fr $out
+ run mkdir -p $out
+
+ # It's too noisy, so only show the first few lines.
+ {
+ # Hmm unzipping kotlin jar files may produce a warning? Let's just add `|| true`...
+ run unzip $f -d $out || true
+ } |& sed -e '5,$d'
+ echo ' (omitting remaining output)'
+
+ done
+}
+
+# Find all *.java files in $1, and print them as Java class names.
+# For example, if there's a file `src/com/android/test/Test.java`, and you run
+# `list_all_classes_under_dir src`, then it'll print `com.android.test.Test`.
+list_all_classes_under_dir() {
+ local dir="$1"
+ ( # Use a subshell, so we won't change the current directory on the caller side.
+ cd "$dir"
+
+ # List the java files, but replace the slashes with dots, and remove the `.java` suffix.
+ ls **/*.java | sed -e 's!/!.!g' -e 's!.java$!!'
+ )
+}
+
+checkenv() {
+ # Make sure $ANDROID_BUILD_TOP is set.
+ : ${ANDROID_BUILD_TOP:?}
+
+ # Make sure ANDROID_BUILD_TOP doesn't contain whitespace.
+ set ${ANDROID_BUILD_TOP}
+ if [[ $# != 1 ]] ; then
+ fail "\$ANDROID_BUILD_TOP cannot contain whitespace."
+ fi
+}
+
+checkenv
+
+JAVAC=${JAVAC:-javac}
+JAVA=${JAVA:-java}
+JAR=${JAR:-jar}
+
+JAVAC_OPTS=${JAVAC_OPTS:--Xmaxerrs 99999 -Xlint:none}
+
+SOONG_INT=$ANDROID_BUILD_TOP/out/soong/.intermediates
+
+JUNIT_TEST_MAIN_CLASS=com.android.hoststubgen.hosthelper.HostTestSuite
+
+run_junit_test_jar() {
+ local jar="$1"
+ echo "Starting test: $jar ..."
+ run cd "${jar%/*}"
+
+ run $JAVA $JAVA_OPTS \
+ -cp $jar \
+ org.junit.runner.JUnitCore \
+ $main_class || return 1
+ return 0
+}
diff --git a/tools/hoststubgen/hoststubgen/.gitignore b/tools/hoststubgen/hoststubgen/.gitignore
new file mode 100644
index 0000000..0f38407
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/.gitignore
@@ -0,0 +1 @@
+framework-all-stub-out
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/tools/hoststubgen/hoststubgen/Android.bp
new file mode 100644
index 0000000..a617876
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/Android.bp
@@ -0,0 +1,303 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+// This library contains the standard hoststubgen annotations.
+java_library {
+ name: "hoststubgen-annotations",
+ srcs: [
+ "annotations-src/**/*.java",
+ ],
+ host_supported: true,
+
+ // Seems like we need it to avoid circular deps.
+ // Copied it from "app-compat-annotations".
+ sdk_version: "core_current",
+ visibility: ["//visibility:public"],
+}
+
+// This library contains helper classes used in the host side test environment at runtime.
+// This library is _not_ specific to Android APIs.
+java_library_host {
+ name: "hoststubgen-helper-runtime",
+ srcs: [
+ "helper-runtime-src/**/*.java",
+ ],
+ libs: [
+ "junit",
+ "ow2-asm",
+ "ow2-asm-analysis",
+ "ow2-asm-commons",
+ "ow2-asm-tree",
+ "ow2-asm-util",
+ ],
+ static_libs: [
+ "guava",
+ ],
+ jarjar_rules: "jarjar-rules.txt",
+ visibility: ["//visibility:public"],
+}
+
+// Host-side stub generator tool.
+java_binary_host {
+ name: "hoststubgen",
+ main_class: "com.android.hoststubgen.Main",
+ srcs: ["src/**/*.kt"],
+ static_libs: [
+ "hoststubgen-helper-runtime",
+ "ow2-asm",
+ "ow2-asm-analysis",
+ "ow2-asm-commons",
+ "ow2-asm-tree",
+ "ow2-asm-util",
+ ],
+ visibility: ["//visibility:public"],
+}
+
+// File that contains the standard command line argumetns to hoststubgen.
+filegroup {
+ name: "hoststubgen-standard-options",
+ srcs: [
+ "hoststubgen-standard-options.txt",
+ ],
+ visibility: ["//visibility:public"],
+}
+
+hoststubgen_common_options = "$(location hoststubgen) " +
+ // "--in-jar $(location :framework-all) " +
+ // "--policy-override-file $(location framework-policy-override.txt) " +
+ "@$(location :hoststubgen-standard-options) " +
+
+ "--out-stub-jar $(location host_stub.jar) " +
+ "--out-impl-jar $(location host_impl.jar) " +
+
+ // "--keep-all-classes " + // Used it for an experiment. See KeepAllClassesFilter.
+ "--gen-keep-all-file $(location hoststubgen_keep_all.txt) " +
+ "--gen-input-dump-file $(location hoststubgen_dump.txt) " +
+ ""
+
+// Common defaults for stub generation.
+// This one is not specific to Android APIs.
+genrule_defaults {
+ name: "hoststubgen-command-defaults",
+ tools: ["hoststubgen"],
+ srcs: [
+ ":hoststubgen-standard-options",
+ ],
+ // Create two jar files.
+ out: [
+ "host_stub.jar",
+ "host_impl.jar",
+
+ // Following files are created just as FYI.
+ "hoststubgen_keep_all.txt",
+ "hoststubgen_dump.txt",
+ ],
+ // visibility: ["//visibility:public"],
+}
+
+// Generate the stub/impl from framework-all, with hidden APIs.
+java_genrule_host {
+ name: "framework-all-hidden-api-host",
+ defaults: ["hoststubgen-command-defaults"],
+ cmd: hoststubgen_common_options +
+ "--in-jar $(location :framework-all) " +
+ "--policy-override-file $(location framework-policy-override.txt) ",
+ srcs: [
+ ":framework-all",
+ "framework-policy-override.txt",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+// Extract the stub jar from "framework-all-host" for subsequent build rules.
+java_genrule_host {
+ name: "framework-all-hidden-api-host-stub",
+ cmd: "cp $(in) $(out)",
+ srcs: [
+ ":framework-all-hidden-api-host{host_stub.jar}",
+ ],
+ out: [
+ "host_stub.jar",
+ ],
+ visibility: ["//visibility:public"],
+}
+
+// Extract the impl jar from "framework-all-host" for subsequent build rules.
+java_genrule_host {
+ name: "framework-all-hidden-api-host-impl",
+ cmd: "cp $(in) $(out)",
+ srcs: [
+ ":framework-all-hidden-api-host{host_impl.jar}",
+ ],
+ out: [
+ "host_impl.jar",
+ ],
+ visibility: ["//visibility:public"],
+}
+
+// Generate the stub/impl from framework-all, with only public/system/test APIs, without
+// hidden APIs.
+java_genrule_host {
+ name: "framework-all-test-api-host",
+ defaults: ["hoststubgen-command-defaults"],
+ cmd: hoststubgen_common_options +
+ "--intersect-stub-jar $(location :android_test_stubs_current{.jar}) " +
+ "--in-jar $(location :framework-all) " +
+ "--policy-override-file $(location framework-policy-override.txt) ",
+ srcs: [
+ ":framework-all",
+ ":android_test_stubs_current{.jar}",
+ "framework-policy-override.txt",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+// Extract the stub jar from "framework-all-test-api-host" for subsequent build rules.
+java_genrule_host {
+ name: "framework-all-test-api-host-stub",
+ cmd: "cp $(in) $(out)",
+ srcs: [
+ ":framework-all-test-api-host{host_stub.jar}",
+ ],
+ out: [
+ "host_stub.jar",
+ ],
+ visibility: ["//visibility:public"],
+}
+
+// Extract the impl jar from "framework-all-test-api-host" for subsequent build rules.
+java_genrule_host {
+ name: "framework-all-test-api-host-impl",
+ cmd: "cp $(in) $(out)",
+ srcs: [
+ ":framework-all-test-api-host{host_impl.jar}",
+ ],
+ out: [
+ "host_impl.jar",
+ ],
+ visibility: ["//visibility:public"],
+}
+
+// This library contains helper classes to build hostside tests/targets.
+// This essentially contains dependencies from tests that we can't actually use the real ones.
+// For example, the actual AndroidTestCase and AndroidJUnit4 don't run on the host side (yet),
+// so we pup "fake" implementations here.
+// Ideally this library should be empty.
+java_library_host {
+ name: "hoststubgen-helper-framework-buildtime",
+ srcs: [
+ "helper-framework-buildtime-src/**/*.java",
+ ],
+ libs: [
+ // We need it to pull in some of the framework classes used in this library,
+ // such as Context.java.
+ "framework-all-hidden-api-host-impl",
+ "junit",
+ ],
+ visibility: ["//visibility:public"],
+}
+
+// This module contains "fake" libcore/dalvik classes, framework native substitution, etc,
+// that are needed at runtime.
+java_library_host {
+ name: "hoststubgen-helper-framework-runtime",
+ srcs: [
+ "helper-framework-runtime-src/**/*.java",
+ ],
+ libs: [
+ "hoststubgen-helper-runtime",
+ "framework-all-hidden-api-host-impl",
+ ],
+ visibility: ["//visibility:public"],
+}
+
+// Defaults for host side test modules.
+// We need two rules for each test.
+// 1. A "-test-lib" jar, which compiles the test against the stub jar.
+// This one is only used by the second rule, so it should be "private.
+// 2. A "-test" jar, which includes 1 + the runtime (impl) jars.
+
+// This and next ones are for tests using framework-app, with hidden APIs.
+java_defaults {
+ name: "hosttest-with-framework-all-hidden-api-test-lib-defaults",
+ installable: false,
+ libs: [
+ "framework-all-hidden-api-host-stub",
+ ],
+ static_libs: [
+ "hoststubgen-helper-framework-buildtime",
+ "framework-annotations-lib",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+// Default rules to include `libandroid_runtime`. For now, it's empty, but we'll use it
+// once we start using JNI.
+java_defaults {
+ name: "hosttest-with-libandroid_runtime",
+ jni_libs: [
+ // "libandroid_runtime",
+
+ // TODO: Figure out how to build them automatically.
+ // Following ones are depended by libandroid_runtime.
+ // Without listing them here, not only we won't get them under
+ // $ANDROID_HOST_OUT/testcases/*/lib64, but also not under
+ // $ANDROID_HOST_OUT/lib64, so we'd fail to load them at runtime.
+ // ($ANDROID_HOST_OUT/lib/ gets all of them though.)
+ // "libcutils",
+ // "libharfbuzz_ng",
+ // "libminikin",
+ // "libz",
+ // "libbinder",
+ // "libhidlbase",
+ // "libvintf",
+ // "libicu",
+ // "libutils",
+ // "libtinyxml2",
+ ],
+}
+
+java_defaults {
+ name: "hosttest-with-framework-all-hidden-api-test-defaults",
+ defaults: ["hosttest-with-libandroid_runtime"],
+ installable: false,
+ test_config: "AndroidTest-host.xml",
+ static_libs: [
+ "hoststubgen-helper-runtime",
+ "hoststubgen-helper-framework-runtime",
+ "framework-all-hidden-api-host-impl",
+ ],
+}
+
+// This and next ones are for tests using framework-app, with public/system/test APIs,
+// without hidden APIs.
+java_defaults {
+ name: "hosttest-with-framework-all-test-api-test-lib-defaults",
+ installable: false,
+ libs: [
+ "framework-all-test-api-host-stub",
+ ],
+ static_libs: [
+ "hoststubgen-helper-framework-buildtime",
+ "framework-annotations-lib",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+java_defaults {
+ name: "hosttest-with-framework-all-test-api-test-defaults",
+ defaults: ["hosttest-with-libandroid_runtime"],
+ installable: false,
+ test_config: "AndroidTest-host.xml",
+ static_libs: [
+ "hoststubgen-helper-runtime",
+ "hoststubgen-helper-framework-runtime",
+ "framework-all-test-api-host-impl",
+ ],
+}
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java
new file mode 100644
index 0000000..a774336
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 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 android.hosttest.annotation;
+
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * Add this with a fully-specified method name (e.g. {@code "com.package.Class.methodName"})
+ * of a callback to get a callback at the class load time.
+ *
+ * The method must be {@code public static} with a single argument that takes
+ * {@link java.lang.Class}.
+ */
+@Target({TYPE})
+@Retention(RetentionPolicy.CLASS)
+public @interface HostSideTestClassLoadHook {
+ String value();
+}
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java
new file mode 100644
index 0000000..06ad1c2
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 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 android.hosttest.annotation;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * Mark a class, field or a method as "Stub", meaning tests can _not_ see the APIs, but they
+ * can indirectly be used on the host side.
+ * When applied to a class, it will _not_ affect the visibility of its members. They need to be
+ * individually marked.
+ *
+ * <p>In order to expose a class and all its members, use {@link HostSideTestWholeClassStub}
+ * instead.
+ * @hide
+ */
+@Target({TYPE, FIELD, METHOD, CONSTRUCTOR})
+@Retention(RetentionPolicy.CLASS)
+public @interface HostSideTestKeep {
+}
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestNativeSubstitutionClass.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestNativeSubstitutionClass.java
new file mode 100644
index 0000000..9c81383
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestNativeSubstitutionClass.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 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 android.hosttest.annotation;
+
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * If a class has this annotation, all its native methods will be delegated to another class.
+ * (See {@link android.os.Parcel} as an example.)
+ */
+@Target({TYPE})
+@Retention(RetentionPolicy.CLASS)
+public @interface HostSideTestNativeSubstitutionClass {
+ String value();
+}
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java
new file mode 100644
index 0000000..46e5078
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 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 android.hosttest.annotation;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * Mark an item as "remove", so this cannot be used on the host side even indirectly.
+ * This is the default behavior.
+ *
+ * @hide
+ */
+@Target({TYPE, FIELD, METHOD, CONSTRUCTOR})
+@Retention(RetentionPolicy.CLASS)
+public @interface HostSideTestRemove {
+}
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStub.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStub.java
new file mode 100644
index 0000000..cabdfe0
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStub.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 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 android.hosttest.annotation;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * Mark a class, field or a method as "Stub", meaning tests can see the APIs.
+ * When applied to a class, it will _not_ affect the visibility of its members. They need to be
+ * individually marked.
+ *
+ * <p>In order to expose a class and all its members, use {@link HostSideTestWholeClassStub}
+ * instead.
+ *
+ * @hide
+ */
+@Target({TYPE, FIELD, METHOD, CONSTRUCTOR})
+@Retention(RetentionPolicy.CLASS)
+public @interface HostSideTestStub {
+}
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java
new file mode 100644
index 0000000..510a67e
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 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 android.hosttest.annotation;
+
+import static java.lang.annotation.ElementType.METHOD;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * If a method has this annotation, we'll replace it with another method on the host side.
+ *
+ * See {@link android.util.LruCache#getEldest()} and its substitution.
+ *
+ * @hide
+ */
+@Target({METHOD})
+@Retention(RetentionPolicy.CLASS)
+public @interface HostSideTestSubstitute {
+ // TODO We should add "_host" as default. We're not doing it yet, because extractign the default
+ // value with ASM doesn't seem trivial. (? not sure.)
+ String suffix();
+}
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java
new file mode 100644
index 0000000..cd1bef4
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2023 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 android.hosttest.annotation;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.METHOD;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * If a method has this annotation, it will throw on the host side.
+ *
+ * @hide
+ */
+@Target({METHOD, CONSTRUCTOR})
+@Retention(RetentionPolicy.CLASS)
+public @interface HostSideTestThrow {
+}
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java
new file mode 100644
index 0000000..3d1ddea
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 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 android.hosttest.annotation;
+
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * Same as {@link HostSideTestKeep} but it'll change the visibility of all its members too.
+ * @hide
+ */
+@Target({TYPE})
+@Retention(RetentionPolicy.CLASS)
+public @interface HostSideTestWholeClassKeep {
+}
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassStub.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassStub.java
new file mode 100644
index 0000000..1824f6f
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassStub.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 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 android.hosttest.annotation;
+
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * Same as {@link HostSideTestStub} but it'll change the visibility of all its members too.
+ *
+ * @hide
+ */
+@Target({TYPE})
+@Retention(RetentionPolicy.CLASS)
+public @interface HostSideTestWholeClassStub {
+}
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java
new file mode 100644
index 0000000..b10f0ff
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 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 android.hosttest.annotation.tests;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Target;
+
+/**
+ * Use this annotation to skip certain tests for host side tests.
+ *
+ * TODO: Actually use it in the test runner.
+ */
+@Target({TYPE, FIELD, METHOD})
+public @interface HostSideTestSuppress {
+}
diff --git a/tools/hoststubgen/hoststubgen/framework-policy-override.txt b/tools/hoststubgen/hoststubgen/framework-policy-override.txt
new file mode 100644
index 0000000..295498d
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/framework-policy-override.txt
@@ -0,0 +1,98 @@
+# --------------------------------------------------------------------------------------------------
+# This file contains rules to process `framework-all.jar` to generate the host side test "stub" and
+# "impl" jars, without using Java annotations.
+#
+# Useful when:
+# - The class is auto-generated and annotations can't be added.
+# (We need to figure out what to do on auto-generated classes.)
+# - Want to quickly change filter rules without having to rebuild framework.jar.
+#
+# Using this file, one can control the visibility of APIs on a per-class, per-field and per-method
+# basis, but in most cases, per-class directives would be sufficient. That is:
+#
+# - To put the entire class, including its members and nested classes, in the "stub" jar,
+# so that the test / target code can use the API, use `stubclass`.
+#
+# class package.class stubclass
+#
+# - To put the entire class, including its members and nested classes, in the "impl" jar,
+# but not in the "stub" jar, use `keepclass`. Use this when you don't want to expose an API to
+# tests/target directly, but it's still needed at runtime, because it's used by other "stub" APIs
+# directly or indirectly.
+#
+# class package.class keepclass
+#
+# All other classes will be removed from both the stub jar and impl jar.
+#
+# --------------------------------------------------------------------------------------------------
+
+# --------------------------------------------------------------------------------------------------
+# Directions on auto-generated classes, where we can't use Java annotations (yet).
+# --------------------------------------------------------------------------------------------------
+class android.Manifest stubclass
+class android.R stubclass
+class android.os.PersistableBundleProto keepclass
+
+# This is in module-utils, where using a HostStubGen annotation would be complicated, so we
+# add a direction here rather than using a java annotation.
+# The build file says it's deprecated, anyway...? Figure out what to do with it.
+class com.android.internal.util.Preconditions keepclass
+
+# --------------------------------------------------------------------------------------------------
+# Actual framework classes
+# --------------------------------------------------------------------------------------------------
+
+# Put basic exception classes in the "impl" jar.
+# We don't put them in stub yet (until something actually needs them).
+class android.os.DeadObjectException keepclass
+class android.os.DeadSystemRuntimeException keepclass
+class android.os.NetworkOnMainThreadException keepclass
+class android.os.RemoteException keepclass
+class android.os.ServiceSpecificException keepclass
+class android.util.AndroidException keepclass
+class android.util.AndroidRuntimeException keepclass
+class android.os.DeadSystemException keepclass
+
+
+# For now, we only want to expose ArrayMap and Log, but they and their tests depend on
+# more classes.
+
+class android.util.ArrayMap stubclass
+
+# Used by ArrayMap. No need to put them in the stub, but we need them in impl.
+class android.util.MapCollections keepclass
+class android.util.ContainerHelpers keepclass
+class com.android.internal.util.XmlUtils keepclass
+class com.android.internal.util.FastMath keepclass
+class android.util.MathUtils keepclass
+
+
+class android.util.Log stubclass
+class android.util.Slog stubclass
+# We don't use Log's native code, yet. Instead, the following line enables the Java substitution.
+# Comment it out to disable Java substitution of Log's native methods.
+class android.util.Log !com.android.hoststubgen.nativesubstitution.Log_host
+
+# Used by log
+class com.android.internal.util.FastPrintWriter keepclass
+class com.android.internal.util.LineBreakBufferedWriter keepclass
+
+
+# Expose Context because it's referred to by AndroidTestCase, but don't need to expose any of
+# its members.
+class android.content.Context keep
+
+# Expose Parcel, Parcel and there relevant classes, which are used by ArrayMapTets.
+class android.os.Parcelable StubClass
+class android.os.Parcel StubClass
+class android.os.Parcel !com.android.hoststubgen.nativesubstitution.Parcel_host
+
+class android.os.IBinder stubClass
+class android.os.IInterface stubclass
+
+class android.os.BadParcelableException stubclass
+class android.os.BadTypeParcelableException stubclass
+
+class android.os.BaseBundle stubclass
+class android.os.Bundle stubclass
+class android.os.PersistableBundle stubclass
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/android/test/AndroidTestCase.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/android/test/AndroidTestCase.java
new file mode 100644
index 0000000..e6d3866
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/android/test/AndroidTestCase.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 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 android.test;
+
+import android.content.Context;
+
+import junit.framework.TestCase;
+
+public class AndroidTestCase extends TestCase {
+ protected Context mContext;
+ public Context getContext() {
+ throw new RuntimeException("[ravenwood] Class Context is not supported yet.");
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/annotation/NonNull.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/annotation/NonNull.java
new file mode 100644
index 0000000..51c5d9a
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/annotation/NonNull.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2013 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 androidx.annotation;
+
+// [ravenwood] TODO: Find the actual androidx jar containing it.s
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that a parameter, field or method return value can never be null.
+ * <p>
+ * This is a marker annotation and it has no specific attributes.
+ *
+ * @paramDoc This value cannot be {@code null}.
+ * @returnDoc This value cannot be {@code null}.
+ * @hide
+ */
+@Retention(SOURCE)
+@Target({METHOD, PARAMETER, FIELD})
+public @interface NonNull {
+}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/annotation/Nullable.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/annotation/Nullable.java
new file mode 100644
index 0000000..f1f0e8b
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/annotation/Nullable.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 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 androidx.annotation;
+
+// [ravenwood] TODO: Find the actual androidx jar containing it.s
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that a parameter, field or method return value can be null.
+ * <p>
+ * When decorating a method call parameter, this denotes that the parameter can
+ * legitimately be null and the method will gracefully deal with it. Typically
+ * used on optional parameters.
+ * <p>
+ * When decorating a method, this denotes the method might legitimately return
+ * null.
+ * <p>
+ * This is a marker annotation and it has no specific attributes.
+ *
+ * @paramDoc This value may be {@code null}.
+ * @returnDoc This value may be {@code null}.
+ * @hide
+ */
+@Retention(SOURCE)
+@Target({METHOD, PARAMETER, FIELD})
+public @interface Nullable {
+}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/ext/junit/runners/AndroidJUnit4.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/ext/junit/runners/AndroidJUnit4.java
new file mode 100644
index 0000000..0c82e4e
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/ext/junit/runners/AndroidJUnit4.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 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 androidx.test.ext.junit.runners;
+
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.model.InitializationError;
+
+// TODO: We need to simulate the androidx test runner.
+// https://source.corp.google.com/piper///depot/google3/third_party/android/androidx_test/ext/junit/java/androidx/test/ext/junit/runners/AndroidJUnit4.java
+// https://source.corp.google.com/piper///depot/google3/third_party/android/androidx_test/runner/android_junit_runner/java/androidx/test/internal/runner/junit4/AndroidJUnit4ClassRunner.java
+
+public class AndroidJUnit4 extends BlockJUnit4ClassRunner {
+ public AndroidJUnit4(Class<?> testClass) throws InitializationError {
+ super(testClass);
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/FlakyTest.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/FlakyTest.java
new file mode 100644
index 0000000..2470d839
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/FlakyTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 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 androidx.test.filters;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Designates a test as being flaky (non-deterministic).
+ *
+ * <p>Can then be used to filter tests on execution using -e annotation or -e notAnnotation as
+ * desired.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface FlakyTest {
+ /**
+ * An optional bug number associated with the test. -1 Means that no bug number is associated with
+ * the flaky annotation.
+ *
+ * @return int
+ */
+ int bugId() default -1;
+
+ /**
+ * Details, such as the reason of why the test is flaky.
+ *
+ * @return String
+ */
+ String detail() default "";
+}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/LargeTest.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/LargeTest.java
new file mode 100644
index 0000000..578d7dc
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/LargeTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 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 androidx.test.filters;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to assign a large test size qualifier to a test. This annotation can be used at a
+ * method or class level.
+ *
+ * <p>Test size qualifiers are a great way to structure test code and are used to assign a test to a
+ * test suite of similar run time.
+ *
+ * <p>Execution time: >1000ms
+ *
+ * <p>Large tests should be focused on testing integration of all application components. These
+ * tests fully participate in the system and may make use of all resources such as databases, file
+ * systems and network. As a rule of thumb most functional UI tests are large tests.
+ *
+ * <p>Note: This class replaces the deprecated Android platform size qualifier <a
+ * href="{@docRoot}reference/android/test/suitebuilder/annotation/LargeTest.html"><code>
+ * android.test.suitebuilder.annotation.LargeTest</code></a> and is the recommended way to annotate
+ * tests written with the AndroidX Test Library.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface LargeTest {}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/MediumTest.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/MediumTest.java
new file mode 100644
index 0000000..dfdaa53
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/MediumTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 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 androidx.test.filters;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to assign a medium test size qualifier to a test. This annotation can be used at a
+ * method or class level.
+ *
+ * <p>Test size qualifiers are a great way to structure test code and are used to assign a test to a
+ * test suite of similar run time.
+ *
+ * <p>Execution time: <1000ms
+ *
+ * <p>Medium tests should be focused on a very limited subset of components or a single component.
+ * Resource access to the file system through well defined interfaces like databases,
+ * ContentProviders, or Context is permitted. Network access should be restricted, (long-running)
+ * blocking operations should be avoided and use mock objects instead.
+ *
+ * <p>Note: This class replaces the deprecated Android platform size qualifier <a
+ * href="{@docRoot}reference/android/test/suitebuilder/annotation/MediumTest.html"><code>
+ * android.test.suitebuilder.annotation.MediumTest</code></a> and is the recommended way to annotate
+ * tests written with the AndroidX Test Library.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface MediumTest {}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/RequiresDevice.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/RequiresDevice.java
new file mode 100644
index 0000000..3d3ee33
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/RequiresDevice.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014 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 androidx.test.filters;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that a specific test should not be run on emulator.
+ *
+ * <p>It will be executed only if the test is running on the physical android device.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface RequiresDevice {}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/SdkSuppress.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/SdkSuppress.java
new file mode 100644
index 0000000..dd65ddb
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/SdkSuppress.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 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 androidx.test.filters;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that a specific test or class requires a minimum or maximum API Level to execute.
+ *
+ * <p>Test(s) will be skipped when executed on android platforms less/more than specified level
+ * (inclusive).
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface SdkSuppress {
+ /** The minimum API level to execute (inclusive) */
+ int minSdkVersion() default 1;
+ /** The maximum API level to execute (inclusive) */
+ int maxSdkVersion() default Integer.MAX_VALUE;
+ /**
+ * The {@link android.os.Build.VERSION.CODENAME} to execute on. This is intended to be used to run
+ * on a pre-release SDK, where the {@link android.os.Build.VERSION.SDK_INT} has not yet been
+ * finalized. This is treated as an OR operation with respect to the minSdkVersion and
+ * maxSdkVersion attributes.
+ *
+ * <p>For example, to filter a test so it runs on only the prerelease R SDK: <code>
+ * {@literal @}SdkSuppress(minSdkVersion = Build.VERSION_CODES.R, codeName = "R")
+ * </code>
+ */
+ String codeName() default "unset";
+}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/SmallTest.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/SmallTest.java
new file mode 100644
index 0000000..dd32df4
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/SmallTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 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 androidx.test.filters;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to assign a small test size qualifier to a test. This annotation can be used at a
+ * method or class level.
+ *
+ * <p>Test size qualifiers are a great way to structure test code and are used to assign a test to a
+ * test suite of similar run time.
+ *
+ * <p>Execution time: <200ms
+ *
+ * <p>Small tests should be run very frequently. Focused on units of code to verify specific logical
+ * conditions. These tests should runs in an isolated environment and use mock objects for external
+ * dependencies. Resource access (such as file system, network, or databases) are not permitted.
+ * Tests that interact with hardware, make binder calls, or that facilitate android instrumentation
+ * should not use this annotation.
+ *
+ * <p>Note: This class replaces the deprecated Android platform size qualifier <a
+ * href="http://developer.android.com/reference/android/test/suitebuilder/annotation/SmallTest.html">
+ * android.test.suitebuilder.annotation.SmallTest</a> and is the recommended way to annotate tests
+ * written with the AndroidX Test Library.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface SmallTest {}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/Suppress.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/Suppress.java
new file mode 100644
index 0000000..88e636c
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/filters/Suppress.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 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 androidx.test.filters;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Use this annotation on test classes or test methods that should not be included in a test suite.
+ * If the annotation appears on the class then no tests in that class will be included. If the
+ * annotation appears only on a test method then only that method will be excluded.
+ *
+ * <p>Note: This class replaces the deprecated Android platform annotation <a
+ * href="http://developer.android.com/reference/android/test/suitebuilder/annotation/Suppress.html">
+ * android.test.suitebuilder.annotation.Suppress</a> and is the recommended way to suppress tests
+ * written with the AndroidX Test Library.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface Suppress {}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/runner/AndroidJUnit4.java b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/runner/AndroidJUnit4.java
new file mode 100644
index 0000000..e137939
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-buildtime-src/androidx/test/runner/AndroidJUnit4.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2023 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 androidx.test.runner;
+
+import org.junit.runners.model.InitializationError;
+
+public class AndroidJUnit4 extends androidx.test.ext.junit.runners.AndroidJUnit4 {
+ public AndroidJUnit4(Class<?> testClass) throws InitializationError {
+ super(testClass);
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Log_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Log_host.java
new file mode 100644
index 0000000..ee55c7a
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Log_host.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.nativesubstitution;
+
+import android.util.Log;
+import android.util.Log.Level;
+
+import java.io.PrintStream;
+
+public class Log_host {
+
+ public static boolean isLoggable(String tag, @Level int level) {
+ return true;
+ }
+
+ public static int println_native(int bufID, int priority, String tag, String msg) {
+ final PrintStream out = System.out;
+ final String buffer;
+ switch (bufID) {
+ case Log.LOG_ID_MAIN: buffer = "main"; break;
+ case Log.LOG_ID_RADIO: buffer = "radio"; break;
+ case Log.LOG_ID_EVENTS: buffer = "event"; break;
+ case Log.LOG_ID_SYSTEM: buffer = "system"; break;
+ case Log.LOG_ID_CRASH: buffer = "crash"; break;
+ default: buffer = "buf:" + bufID; break;
+ };
+
+ final String prio;
+ switch (priority) {
+ case Log.VERBOSE: prio = "V"; break;
+ case Log.DEBUG: prio = "D"; break;
+ case Log.INFO: prio = "I"; break;
+ case Log.WARN: prio = "W"; break;
+ case Log.ERROR: prio = "E"; break;
+ case Log.ASSERT: prio = "A"; break;
+ default: prio = "prio:" + priority; break;
+ };
+
+ for (String s : msg.split("\\n")) {
+ out.println(String.format("logd: [%s] %s %s: %s", buffer, prio, tag, s));
+ }
+ return msg.length();
+ }
+
+ public static int logger_entry_max_payload_native() {
+ return 4068; // [ravenwood] This is what people use in various places.
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java
new file mode 100644
index 0000000..d749f07
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.nativesubstitution;
+
+import android.os.IBinder;
+
+import java.io.FileDescriptor;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Tentative, partial implementation of the Parcel native methods, using Java's
+ * {@link ByteBuffer}. It turned out there's enough semantics differences between Parcel
+ * and {@link ByteBuffer}, so it didn't actually work.
+ * (e.g. Parcel seems to allow moving the data position to be beyond its size? Which
+ * {@link ByteBuffer} wouldn't allow...)
+ */
+public class Parcel_host {
+ private Parcel_host() {
+ }
+
+ private static final AtomicLong sNextId = new AtomicLong(0);
+
+ private static final Map<Long, Parcel_host> sInstances = new ConcurrentHashMap<>();
+
+ private boolean mDeleted = false;
+
+ private byte[] mBuffer;
+ private int mSize;
+ private int mPos;
+
+ private boolean mSensitive;
+ private boolean mAllowFds;
+
+ // TODO Use the actual value from Parcel.java.
+ private static final int OK = 0;
+
+ private void validate() {
+ if (mDeleted) {
+ // TODO: Put more info
+ throw new RuntimeException("Parcel already destroyed");
+ }
+ }
+
+ private static Parcel_host getInstance(long id) {
+ Parcel_host p = sInstances.get(id);
+ if (p == null) {
+ // TODO: Put more info
+ throw new RuntimeException("Parcel doesn't exist with id=" + id);
+ }
+ p.validate();
+ return p;
+ }
+
+ public static long nativeCreate() {
+ final long id = sNextId.getAndIncrement();
+ final Parcel_host p = new Parcel_host();
+ sInstances.put(id, p);
+ p.init();
+ return id;
+ }
+
+ private void init() {
+ mBuffer = new byte[0];
+ mSize = 0;
+ mPos = 0;
+ mSensitive = false;
+ mAllowFds = false;
+ }
+
+ private void updateSize() {
+ if (mSize < mPos) {
+ mSize = mPos;
+ }
+ }
+
+ public static void nativeDestroy(long nativePtr) {
+ getInstance(nativePtr).mDeleted = true;
+ sInstances.remove(nativePtr);
+ }
+
+ public static void nativeFreeBuffer(long nativePtr) {
+ getInstance(nativePtr).freeBuffer();
+ }
+
+ public void freeBuffer() {
+ init();
+ }
+
+ private int getCapacity() {
+ return mBuffer.length;
+ }
+
+ private void ensureMoreCapacity(int size) {
+ ensureCapacity(mPos + size);
+ }
+
+ private void ensureCapacity(int targetSize) {
+ if (targetSize <= getCapacity()) {
+ return;
+ }
+ var newSize = getCapacity() * 2;
+ if (newSize < targetSize) {
+ newSize = targetSize;
+ }
+ forceSetCapacity(newSize);
+ }
+
+ private void forceSetCapacity(int newSize) {
+ var newBuf = new byte[newSize];
+
+ // Copy
+ System.arraycopy(mBuffer, 0, newBuf, 0, Math.min(newSize, getCapacity()));
+
+ this.mBuffer = newBuf;
+ }
+
+ private void ensureDataAvailable(int requestSize) {
+ if (mSize - mPos < requestSize) {
+ throw new RuntimeException(String.format(
+ "Pacel data underflow. size=%d, pos=%d, request=%d", mSize, mPos, requestSize));
+ }
+ }
+
+ public static void nativeMarkSensitive(long nativePtr) {
+ getInstance(nativePtr).mSensitive = true;
+ }
+ public static void nativeMarkForBinder(long nativePtr, IBinder binder) {
+ throw new RuntimeException("Not implemented yet");
+ }
+ public static boolean nativeIsForRpc(long nativePtr) {
+ throw new RuntimeException("Not implemented yet");
+ }
+ public static int nativeDataSize(long nativePtr) {
+ return getInstance(nativePtr).mSize;
+ }
+ public static int nativeDataAvail(long nativePtr) {
+ var p = getInstance(nativePtr);
+ return p.mSize - p.mPos;
+ }
+ public static int nativeDataPosition(long nativePtr) {
+ return getInstance(nativePtr).mPos;
+ }
+ public static int nativeDataCapacity(long nativePtr) {
+ return getInstance(nativePtr).mBuffer.length;
+ }
+ public static void nativeSetDataSize(long nativePtr, int size) {
+ var p = getInstance(nativePtr);
+ p.ensureCapacity(size);
+ getInstance(nativePtr).mSize = size;
+ }
+ public static void nativeSetDataPosition(long nativePtr, int pos) {
+ var p = getInstance(nativePtr);
+ // TODO: Should this change the size or the capacity??
+ p.mPos = pos;
+ }
+ public static void nativeSetDataCapacity(long nativePtr, int size) {
+ var p = getInstance(nativePtr);
+ if (p.getCapacity() < size) {
+ p.forceSetCapacity(size);
+ }
+ }
+
+ public static boolean nativePushAllowFds(long nativePtr, boolean allowFds) {
+ var p = getInstance(nativePtr);
+ var prev = p.mAllowFds;
+ p.mAllowFds = allowFds;
+ return prev;
+ }
+ public static void nativeRestoreAllowFds(long nativePtr, boolean lastValue) {
+ getInstance(nativePtr).mAllowFds = lastValue;
+ }
+
+ public static void nativeWriteByteArray(long nativePtr, byte[] b, int offset, int len) {
+ nativeWriteBlob(nativePtr, b, offset, len);
+ }
+
+ public static void nativeWriteBlob(long nativePtr, byte[] b, int offset, int len) {
+ var p = getInstance(nativePtr);
+
+ if (b == null) {
+ nativeWriteInt(nativePtr, -1);
+ } else {
+ final var alignedSize = align4(b.length);
+
+ nativeWriteInt(nativePtr, b.length);
+
+ p.ensureMoreCapacity(alignedSize);
+
+ System.arraycopy(b, offset, p.mBuffer, p.mPos, len);
+ p.mPos += alignedSize;
+ p.updateSize();
+ }
+ }
+
+ public static int nativeWriteInt(long nativePtr, int value) {
+ var p = getInstance(nativePtr);
+ p.ensureMoreCapacity(Integer.BYTES);
+
+ p.mBuffer[p.mPos++] = (byte) ((value >> 24) & 0xff);
+ p.mBuffer[p.mPos++] = (byte) ((value >> 16) & 0xff);
+ p.mBuffer[p.mPos++] = (byte) ((value >> 8) & 0xff);
+ p.mBuffer[p.mPos++] = (byte) ((value >> 0) & 0xff);
+
+ p.updateSize();
+
+ return OK;
+ }
+
+ public static int nativeWriteLong(long nativePtr, long value) {
+ nativeWriteInt(nativePtr, (int) (value >>> 32));
+ nativeWriteInt(nativePtr, (int) (value));
+ return OK;
+ }
+ public static int nativeWriteFloat(long nativePtr, float val) {
+ return nativeWriteInt(nativePtr, Float.floatToIntBits(val));
+ }
+ public static int nativeWriteDouble(long nativePtr, double val) {
+ return nativeWriteLong(nativePtr, Double.doubleToLongBits(val));
+ }
+ public static void nativeSignalExceptionForError(int error) {
+ throw new RuntimeException("Not implemented yet");
+ }
+
+ private static int align4(int val) {
+ return ((val + 3) / 4) * 4;
+ }
+
+ public static void nativeWriteString8(long nativePtr, String val) {
+ if (val == null) {
+ nativeWriteBlob(nativePtr, null, 0, 0);
+ } else {
+ var bytes = val.getBytes(StandardCharsets.UTF_8);
+ nativeWriteBlob(nativePtr, bytes, 0, bytes.length);
+ }
+ }
+ public static void nativeWriteString16(long nativePtr, String val) {
+ // Just reuse String8
+ nativeWriteString8(nativePtr, val);
+ }
+ public static void nativeWriteStrongBinder(long nativePtr, IBinder val) {
+ throw new RuntimeException("Not implemented yet");
+ }
+ public static void nativeWriteFileDescriptor(long nativePtr, FileDescriptor val) {
+ throw new RuntimeException("Not implemented yet");
+ }
+
+ public static byte[] nativeCreateByteArray(long nativePtr) {
+ return nativeReadBlob(nativePtr);
+ }
+
+ public static boolean nativeReadByteArray(long nativePtr, byte[] dest, int destLen) {
+ if (dest == null) {
+ return false;
+ }
+ var data = nativeReadBlob(nativePtr);
+ if (data == null) {
+ System.err.println("Percel has NULL, which is unexpected."); // TODO: Is this correct?
+ return false;
+ }
+ // TODO: Make sure the check logic is correct.
+ if (data.length != destLen) {
+ System.err.println("Byte array size mismatch: expected="
+ + data.length + " given=" + destLen);
+ return false;
+ }
+ return true;
+ }
+
+ public static byte[] nativeReadBlob(long nativePtr) {
+ final var size = nativeReadInt(nativePtr);
+ if (size == -1) {
+ return null;
+ }
+ var p = getInstance(nativePtr);
+ p.ensureDataAvailable(size);
+
+ var bytes = new byte[size];
+ System.arraycopy(p.mBuffer, p.mPos, bytes, 0, size);
+
+ p.mPos += align4(size);
+
+ return bytes;
+ }
+ public static int nativeReadInt(long nativePtr) {
+ var p = getInstance(nativePtr);
+
+ p.ensureDataAvailable(Integer.BYTES);
+
+ var ret = (((p.mBuffer[p.mPos++] & 0xff) << 24)
+ | ((p.mBuffer[p.mPos++] & 0xff) << 16)
+ | ((p.mBuffer[p.mPos++] & 0xff) << 8)
+ | ((p.mBuffer[p.mPos++] & 0xff) << 0));
+
+ return ret;
+ }
+ public static long nativeReadLong(long nativePtr) {
+ return (((long) nativeReadInt(nativePtr)) << 32)
+ | (((long) nativeReadInt(nativePtr)) & 0xffff_ffffL);
+ }
+
+ public static float nativeReadFloat(long nativePtr) {
+ return Float.intBitsToFloat(nativeReadInt(nativePtr));
+ }
+
+ public static double nativeReadDouble(long nativePtr) {
+ return Double.longBitsToDouble(nativeReadLong(nativePtr));
+ }
+
+ public static String nativeReadString8(long nativePtr) {
+ final var bytes = nativeReadBlob(nativePtr);
+ if (bytes == null) {
+ return null;
+ }
+ return new String(bytes, StandardCharsets.UTF_8);
+ }
+ public static String nativeReadString16(long nativePtr) {
+ return nativeReadString8(nativePtr);
+ }
+ public static IBinder nativeReadStrongBinder(long nativePtr) {
+ throw new RuntimeException("Not implemented yet");
+ }
+ public static FileDescriptor nativeReadFileDescriptor(long nativePtr) {
+ throw new RuntimeException("Not implemented yet");
+ }
+
+ public static byte[] nativeMarshall(long nativePtr) {
+ throw new RuntimeException("Not implemented yet");
+ }
+ public static void nativeUnmarshall(
+ long nativePtr, byte[] data, int offset, int length) {
+ throw new RuntimeException("Not implemented yet");
+ }
+ public static int nativeCompareData(long thisNativePtr, long otherNativePtr) {
+ throw new RuntimeException("Not implemented yet");
+ }
+ public static boolean nativeCompareDataInRange(
+ long ptrA, int offsetA, long ptrB, int offsetB, int length) {
+ throw new RuntimeException("Not implemented yet");
+ }
+ public static void nativeAppendFrom(
+ long thisNativePtr, long otherNativePtr, int srcOffset, int length) {
+ var dst = getInstance(thisNativePtr);
+ var src = getInstance(otherNativePtr);
+
+ dst.ensureMoreCapacity(length);
+
+ System.arraycopy(src.mBuffer, srcOffset, dst.mBuffer, dst.mPos, length);
+ dst.mPos += length; // TODO: 4 byte align?
+ dst.updateSize();
+
+ // TODO: Update the other's position?
+ }
+
+ public static boolean nativeHasFileDescriptors(long nativePtr) {
+ // Assume false for now, because we don't support writing FDs yet.
+ return false;
+ }
+ public static boolean nativeHasFileDescriptorsInRange(
+ long nativePtr, int offset, int length) {
+ // Assume false for now, because we don't support writing FDs yet.
+ return false;
+ }
+ public static void nativeWriteInterfaceToken(long nativePtr, String interfaceName) {
+ throw new RuntimeException("Not implemented yet");
+ }
+ public static void nativeEnforceInterface(long nativePtr, String interfaceName) {
+ throw new RuntimeException("Not implemented yet");
+ }
+
+ public static boolean nativeReplaceCallingWorkSourceUid(
+ long nativePtr, int workSourceUid) {
+ throw new RuntimeException("Not implemented yet");
+ }
+ public static int nativeReadCallingWorkSourceUid(long nativePtr) {
+ throw new RuntimeException("Not implemented yet");
+ }
+
+ public static long nativeGetOpenAshmemSize(long nativePtr) {
+ throw new RuntimeException("Not implemented yet");
+ }
+ public static long getGlobalAllocSize() {
+ throw new RuntimeException("Not implemented yet");
+ }
+ public static long getGlobalAllocCount() {
+ throw new RuntimeException("Not implemented yet");
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/SystemProperties_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/SystemProperties_host.java
new file mode 100644
index 0000000..1ec1d5f
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/SystemProperties_host.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.nativesubstitution;
+
+public class SystemProperties_host {
+ public static String native_get(String key, String def) {
+ throw new RuntimeException("Not implemented yet");
+ }
+ public static int native_get_int(String key, int def) {
+ throw new RuntimeException("Not implemented yet");
+ }
+ public static long native_get_long(String key, long def) {
+ throw new RuntimeException("Not implemented yet");
+ }
+ public static boolean native_get_boolean(String key, boolean def) {
+ throw new RuntimeException("Not implemented yet");
+ }
+
+ public static long native_find(String name) {
+ throw new RuntimeException("Not implemented yet");
+ }
+ public static String native_get(long handle) {
+ throw new RuntimeException("Not implemented yet");
+ }
+ public static int native_get_int(long handle, int def) {
+ throw new RuntimeException("Not implemented yet");
+ }
+ public static long native_get_long(long handle, long def) {
+ throw new RuntimeException("Not implemented yet");
+ }
+ public static boolean native_get_boolean(long handle, boolean def) {
+ throw new RuntimeException("Not implemented yet");
+ }
+ public static void native_set(String key, String def) {
+ throw new RuntimeException("Not implemented yet");
+ }
+ public static void native_add_change_callback() {
+ throw new RuntimeException("Not implemented yet");
+ }
+ public static void native_report_sysprop_change() {
+ throw new RuntimeException("Not implemented yet");
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/runtimehelper/ClassLoadHook.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/runtimehelper/ClassLoadHook.java
new file mode 100644
index 0000000..4c2d3c4
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/runtimehelper/ClassLoadHook.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.runtimehelper;
+
+import com.android.hoststubgen.hosthelper.HostTestException;
+
+import java.io.File;
+import java.io.PrintStream;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Standard class to handle class load hook.
+ *
+ * We use this to initialize the environment necessary for some classes. (e.g. load native libs.)
+ */
+public class ClassLoadHook {
+ private static PrintStream out = System.out;
+
+ /**
+ * If true, we won't load `libandroid_runtime`
+ *
+ * <p>Looks like there's some complexity in running a host test with JNI with `atest`,
+ * so we need a way to remove the dependency.
+ */
+ private static final boolean SKIP_LOADING_LIBANDROID = "1".equals(System.getenv(
+ "HOSTTEST_SKIP_LOADING_LIBANDROID"));
+
+ public static final String CORE_NATIVE_CLASSES = "core_native_classes";
+ public static final String ICU_DATA_PATH = "icu.data.path";
+ public static final String KEYBOARD_PATHS = "keyboard_paths";
+ public static final String GRAPHICS_NATIVE_CLASSES = "graphics_native_classes";
+
+ public static final String VALUE_N_A = "**n/a**";
+ public static final String LIBANDROID_RUNTIME_NAME = "libandroid_runtime";
+
+ private static String sInitialDir = new File("").getAbsolutePath();
+
+ static {
+ log("Initialized. Current dir=" + sInitialDir);
+ }
+
+ private ClassLoadHook() {
+ }
+
+ /**
+ * Called when classes with
+ * {@code @HostSideTestClassLoadHook("com.android.hoststubgen.runtimehelper.ClassLoadHook.onClassLoaded") }
+ * are loaded.
+ */
+ public static void onClassLoaded(Class<?> clazz) {
+ System.out.println("Framework class loaded: " + clazz.getCanonicalName());
+
+ if (android.util.Log.class == clazz) {
+ loadFrameworkNativeCode();
+ }
+ }
+
+ private static void log(String message) {
+ out.println("ClassLoadHook: " + message);
+ }
+
+ private static void log(String fmt, Object... args) {
+ log(String.format(fmt, args));
+ }
+
+ private static void ensurePropertyNotSet(String key) {
+ if (System.getProperty(key) != null) {
+ throw new HostTestException("System property \"" + key + "\" is set unexpectedly");
+ }
+ }
+
+ private static void setProperty(String key, String value) {
+ System.setProperty(key, value);
+ log("Property set: %s=\"%s\"", key, value);
+ }
+
+ private static void dumpSystemProperties() {
+ for (var prop : System.getProperties().entrySet()) {
+ log(" %s=\"%s\"", prop.getKey(), prop.getValue());
+ }
+ }
+
+ private static void loadJniLibrary(String name) {
+ final String path = sInitialDir + "/lib64/" + name + ".so";
+ System.out.println("Loading " + path + " ...");
+ System.load(path);
+ System.out.println("Done loading " + path);
+ }
+
+ private static boolean sLoadFrameworkNativeCodeCalled = false;
+
+ /**
+ * Load `libandroid_runtime` if needed.
+ */
+ private static void loadFrameworkNativeCode() {
+ // This is called from class-initializers, so no synchronization is needed.
+ if (sLoadFrameworkNativeCodeCalled) {
+ // This method has already been called before.s
+ return;
+ }
+ sLoadFrameworkNativeCodeCalled = true;
+
+ // libandroid_runtime uses Java's system properties to decide what JNI methods to set up.
+ // Set up these properties for host-side tests.
+
+ if ("1".equals(System.getenv("HOSTTEST_DUMP_PROPERTIES"))) {
+ log("Java system properties:");
+ dumpSystemProperties();
+ }
+
+ if (SKIP_LOADING_LIBANDROID) {
+ log("Skip loading " + LIBANDROID_RUNTIME_NAME);
+ }
+
+ // Make sure these properties are not set.
+ ensurePropertyNotSet(CORE_NATIVE_CLASSES);
+ ensurePropertyNotSet(ICU_DATA_PATH);
+ ensurePropertyNotSet(KEYBOARD_PATHS);
+ ensurePropertyNotSet(GRAPHICS_NATIVE_CLASSES);
+
+ // Tell libandroid what JNI to use.
+ final var jniClasses = getCoreNativeClassesToUse();
+ if (jniClasses.isEmpty()) {
+ log("No classes require JNI methods, skip loading " + LIBANDROID_RUNTIME_NAME);
+ return;
+ }
+ setProperty(CORE_NATIVE_CLASSES, jniClasses);
+ setProperty(GRAPHICS_NATIVE_CLASSES, "");
+ setProperty(ICU_DATA_PATH, VALUE_N_A);
+ setProperty(KEYBOARD_PATHS, VALUE_N_A);
+
+ loadJniLibrary(LIBANDROID_RUNTIME_NAME);
+ }
+
+ /**
+ * @return if a given method is a native method or not.
+ */
+ private static boolean isNativeMethod(Class<?> clazz, String methodName, Class<?>... argTypes) {
+ try {
+ final var method = clazz.getMethod(methodName, argTypes);
+ return Modifier.isNative(method.getModifiers());
+ } catch (NoSuchMethodException e) {
+ throw new HostTestException(String.format(
+ "Class %s doesn't have method %s with args %s",
+ clazz.getCanonicalName(),
+ methodName,
+ Arrays.toString(argTypes)), e);
+ }
+ }
+
+ /**
+ * Create a list of classes as comma-separated that require JNI methods to be set up.
+ *
+ * <p>This list is used by frameworks/base/core/jni/LayoutlibLoader.cpp to decide
+ * what JNI methods to set up.
+ */
+ private static String getCoreNativeClassesToUse() {
+ final var coreNativeClassesToLoad = new ArrayList<String>();
+
+ if (isNativeMethod(android.util.Log.class, "isLoggable",
+ String.class, int.class)) {
+ coreNativeClassesToLoad.add("android.util.Log");
+ }
+
+ return String.join(",", coreNativeClassesToLoad);
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/dalvik/system/VMRuntime.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/dalvik/system/VMRuntime.java
new file mode 100644
index 0000000..7d2b00d
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/dalvik/system/VMRuntime.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 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 dalvik.system;
+
+// [ravenwood] It's in libart, so until we get ART to work, we need to use a fake.
+// The original is here:
+// $ANDROID_BUILD_TOP/libcore/libart/src/main/java/dalvik/system/VMRuntime.java
+
+import java.lang.reflect.Array;
+
+public class VMRuntime {
+ private static final VMRuntime THE_ONE = new VMRuntime();
+
+ private VMRuntime() {
+ }
+
+ public static VMRuntime getRuntime() {
+ return THE_ONE;
+ }
+
+ public boolean is64Bit() {
+ return true;
+ }
+
+ public static boolean is64BitAbi(String abi) {
+ return true;
+ }
+
+ public Object newUnpaddedArray(Class<?> componentType, int minLength) {
+ return Array.newInstance(componentType, minLength);
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/EmptyArray.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/EmptyArray.java
new file mode 100644
index 0000000..a1ae35a
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/EmptyArray.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 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 libcore.util;
+
+import java.lang.annotation.Annotation;
+
+// [ravenwood] Copied from libcore. TODO: Figure out what to do with libcore.
+public class EmptyArray {
+ private EmptyArray() {}
+
+ public static final boolean[] BOOLEAN = new boolean[0];
+
+ public static final byte[] BYTE = new byte[0];
+
+ public static final char[] CHAR = new char[0];
+
+ public static final double[] DOUBLE = new double[0];
+
+ public static final float[] FLOAT = new float[0];
+
+ public static final int[] INT = new int[0];
+
+ public static final long[] LONG = new long[0];
+
+ public static final Class<?>[] CLASS = new Class[0];
+
+ public static final Object[] OBJECT = new Object[0];
+
+ public static final String[] STRING = new String[0];
+
+ public static final Throwable[] THROWABLE = new Throwable[0];
+
+ public static final StackTraceElement[] STACK_TRACE_ELEMENT = new StackTraceElement[0];
+
+ public static final java.lang.reflect.Type[] TYPE = new java.lang.reflect.Type[0];
+
+ public static final java.lang.reflect.TypeVariable[] TYPE_VARIABLE =
+ new java.lang.reflect.TypeVariable[0];
+ public static final Annotation[] ANNOTATION = new Annotation[0];
+
+}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/SneakyThrow.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/SneakyThrow.java
new file mode 100644
index 0000000..e142c46
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/SneakyThrow.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 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 libcore.util;
+
+// [ravenwood] Copied from libcore. TODO: Figure out what to do with libcore.
+
+public class SneakyThrow {
+
+ private SneakyThrow() {
+ }
+
+ public static void sneakyThrow(Throwable t) {
+ SneakyThrow.<RuntimeException>sneakyThrow_(t);
+ }
+
+ private static <T extends Throwable> void sneakyThrow_(Throwable t) throws T {
+ throw (T) t;
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedKeepClass.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedKeepClass.java
new file mode 100644
index 0000000..4c37579
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedKeepClass.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.hosthelper;
+
+import static java.lang.annotation.ElementType.TYPE;
+
+import org.objectweb.asm.Type;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation added to all "stub" classes generated by HostStubGen.
+ */
+@Target({TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface HostStubGenProcessedKeepClass {
+ String CLASS_INTERNAL_NAME = Type.getInternalName(HostStubGenProcessedKeepClass.class);
+ String CLASS_DESCRIPTOR = "L" + CLASS_INTERNAL_NAME + ";";
+}
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedStubClass.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedStubClass.java
new file mode 100644
index 0000000..34e0030
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedStubClass.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.hosthelper;
+
+import static java.lang.annotation.ElementType.TYPE;
+
+import org.objectweb.asm.Type;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation added to all "stub" classes generated by HostStubGen.
+ */
+@Target({TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface HostStubGenProcessedStubClass {
+ String CLASS_INTERNAL_NAME = Type.getInternalName(HostStubGenProcessedStubClass.class);
+ String CLASS_DESCRIPTOR = "L" + CLASS_INTERNAL_NAME + ";";
+}
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java
new file mode 100644
index 0000000..c54c2c1
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.hosthelper;
+
+public class HostTestException extends RuntimeException {
+ public HostTestException(String message) {
+ super(message);
+ }
+
+ public HostTestException(String message, Throwable inner) {
+ super(message, inner);
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java
new file mode 100644
index 0000000..29f7be0
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.hosthelper;
+
+import com.google.common.reflect.ClassPath;
+import com.google.common.reflect.ClassPath.ClassInfo;
+
+import junit.framework.JUnit4TestAdapter;
+import junit.framework.TestSuite;
+
+import java.util.regex.Pattern;
+
+/**
+ * A very simple Junit {@link TestSuite} builder that finds all classes that look like test classes.
+ *
+ * We use it to run ravenwood test jars from the command line.
+ */
+public class HostTestSuite {
+ private static final String CLASS_NAME_REGEX_ENV = "HOST_TEST_CLASS_NAME_REGEX";
+
+ /**
+ * Called by junit, and return all test-looking classes as a suite.
+ */
+ public static TestSuite suite() {
+ TestSuite suite = new TestSuite();
+
+ final Pattern classNamePattern;
+ final var filterRegex = System.getenv(CLASS_NAME_REGEX_ENV);
+ if (filterRegex == null) {
+ classNamePattern = Pattern.compile("");
+ } else {
+ classNamePattern = Pattern.compile(filterRegex);
+ }
+ try {
+ // We use guava to list all classes.
+ ClassPath cp = ClassPath.from(HostTestSuite.class.getClassLoader());
+
+ for (var classInfo : cp.getAllClasses()) {
+ Class<?> clazz = asTestClass(classInfo);
+ if (clazz != null) {
+ if (classNamePattern.matcher(clazz.getSimpleName()).find()) {
+ System.out.println("Test class found " + clazz.getName());
+ } else {
+ System.out.println("Skipping test class (for $"
+ + CLASS_NAME_REGEX_ENV + "): " + clazz.getName());
+ }
+ suite.addTest(new JUnit4TestAdapter(clazz));
+ }
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return suite;
+ }
+
+ /**
+ * Decide whether a class looks like a test class or not, and if so, return it as a Class
+ * instance.
+ */
+ private static Class<?> asTestClass(ClassInfo classInfo) {
+ try {
+ final Class<?> clazz = classInfo.load();
+
+ // Does it extend junit.framework.TestCase?
+ if (junit.framework.TestCase.class.isAssignableFrom(clazz)) {
+ // Ignore classes in JUnit itself, or the android(x) test lib.
+ if (classInfo.getName().startsWith("junit.")
+ || classInfo.getName().startsWith("org.junit.")
+ || classInfo.getName().startsWith("android.test.")
+ || classInfo.getName().startsWith("androidx.test.")) {
+ return null; // Ignore junit classes.
+ }
+ return clazz;
+ }
+ // Does it have any "@Test" method?
+ for (var method : clazz.getMethods()) {
+ for (var an : method.getAnnotations()) {
+ if (an.annotationType() == org.junit.Test.class) {
+ return clazz;
+ }
+ }
+ }
+ return null;
+ } catch (java.lang.NoClassDefFoundError e) {
+ // Ignore unloadable classes.
+ return null;
+ }
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
new file mode 100644
index 0000000..f7719a6
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.hosthelper;
+
+import org.objectweb.asm.Type;
+
+import java.io.PrintStream;
+import java.lang.StackWalker.Option;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+
+import javax.annotation.concurrent.GuardedBy;
+
+/**
+ * Utilities used in the host side test environment.
+ */
+public class HostTestUtils {
+ private HostTestUtils() {
+ }
+
+ public static final String CLASS_INTERNAL_NAME = Type.getInternalName(HostTestUtils.class);
+
+ /** If true, we won't print method call log. */
+ private static final boolean SKIP_METHOD_LOG = "1".equals(System.getenv(
+ "HOSTTEST_SKIP_METHOD_LOG"));
+
+ /** If true, we won't perform non-stub method direct call check. */
+ private static final boolean SKIP_NON_STUB_METHOD_CHECK = "1".equals(System.getenv(
+ "HOSTTEST_SKIP_NON_STUB_METHOD_CHECK"));
+
+
+ /**
+ * Method call log will be printed to it.
+ */
+ public static PrintStream logPrintStream = System.out;
+
+ /**
+ * Called from methods with FilterPolicy.Throw.
+ */
+ public static void onThrowMethodCalled() {
+ // TODO: Maybe add call tracking?
+ throw new RuntimeException("This method is not supported on the host side");
+ }
+
+ /**
+ * Called from methods with FilterPolicy.Log.
+ */
+ public static void logMethodCall(
+ String methodClass,
+ String methodName,
+ String methodDescriptor
+ ) {
+ if (SKIP_METHOD_LOG) {
+ return;
+ }
+ logPrintStream.println("# " + methodClass + "." + methodName + methodDescriptor);
+ }
+
+ private static final StackWalker sStackWalker =
+ StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE);
+
+ /**
+ * Return a {@link StackWalker} that supports {@link StackWalker#getCallerClass()}.
+ */
+ public static StackWalker getStackWalker() {
+ return sStackWalker;
+ }
+
+ /**
+ * Cache used by {@link #isClassAllowedToCallNonStubMethods}.
+ */
+ @GuardedBy("sAllowedClasses")
+ private static final HashMap<Class, Boolean> sAllowedClasses = new HashMap();
+
+ /**
+ * Return true if a given class is allowed to access non-stub methods -- that is, if the class
+ * is in the hoststubgen generated JARs. (not in the test jar.)
+ */
+ private static boolean isClassAllowedToCallNonStubMethods(Class<?> clazz) {
+ synchronized (sAllowedClasses) {
+ var cached = sAllowedClasses.get(clazz);
+ if (cached != null) {
+ return cached;
+ }
+ }
+ // All processed classes have this annotation.
+ var allowed = clazz.getAnnotation(HostStubGenProcessedKeepClass.class) != null;
+
+ // Java classes should be able to access any methods. (via callbacks, etc.)
+ if (!allowed) {
+ if (clazz.getPackageName().startsWith("java.")
+ || clazz.getPackageName().startsWith("javax.")) {
+ allowed = true;
+ }
+ }
+ synchronized (sAllowedClasses) {
+ sAllowedClasses.put(clazz, allowed);
+ }
+ return allowed;
+ }
+
+ /**
+ * Called when non-stub methods are called. We do a host-unsupported method direct call check
+ * in here.
+ */
+ public static void onNonStubMethodCalled(
+ String methodClass,
+ String methodName,
+ String methodDescriptor,
+ Class<?> callerClass) {
+ if (SKIP_NON_STUB_METHOD_CHECK) {
+ return;
+ }
+ if (isClassAllowedToCallNonStubMethods(callerClass)) {
+ return; // Generated class is allowed to call framework class.
+ }
+ logPrintStream.println("! " + methodClass + "." + methodName + methodDescriptor
+ + " called by " + callerClass.getCanonicalName());
+ }
+
+ /**
+ * Called when any top level class (not nested classes) in the impl jar is loaded.
+ *
+ * When HostStubGen inject a class-load hook, it's always a call to this method, with the
+ * actual method name as the second argument.
+ *
+ * This method discovers the hook method with reflections and call it.
+ *
+ * TODO: Add a unit test.
+ */
+ public static void onClassLoaded(Class<?> loadedClass, String callbackMethod) {
+ logPrintStream.println("! Class loaded: " + loadedClass.getCanonicalName()
+ + " calling hook " + callbackMethod);
+
+ // Forward the call to callbackMethod.
+ final int lastPeriod = callbackMethod.lastIndexOf(".");
+ final String className = callbackMethod.substring(0, lastPeriod);
+ final String methodName = callbackMethod.substring(lastPeriod + 1);
+
+ if (lastPeriod < 0 || className.isEmpty() || methodName.isEmpty()) {
+ throw new HostTestException(String.format(
+ "Unable to find class load hook: malformed method name \"%s\"",
+ callbackMethod));
+ }
+
+ Class<?> clazz = null;
+ try {
+ clazz = Class.forName(className);
+ } catch (Exception e) {
+ throw new HostTestException(String.format(
+ "Unable to find class load hook: Class %s not found", className), e);
+ }
+ if (!Modifier.isPublic(clazz.getModifiers())) {
+ throw new HostTestException(String.format(
+ "Unable to find class load hook: Class %s must be public", className));
+ }
+
+ Method method = null;
+ try {
+ method = clazz.getMethod(methodName, Class.class);
+ } catch (Exception e) {
+ throw new HostTestException(String.format(
+ "Unable to find class load hook: class %s doesn't have method %s"
+ + " (method must take exactly one parameter of type Class, and public static)",
+ className,
+ methodName), e);
+ }
+ if (!(Modifier.isPublic(method.getModifiers())
+ && Modifier.isStatic(method.getModifiers()))) {
+ throw new HostTestException(String.format(
+ "Unable to find class load hook: Method %s in class %s must be public static",
+ methodName, className));
+ }
+ try {
+ method.invoke(null, loadedClass);
+ } catch (Exception e) {
+ throw new HostTestException(String.format(
+ "Unable to invoke class load hook %s.%s",
+ className,
+ methodName), e);
+ }
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt b/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
new file mode 100644
index 0000000..828d2a3
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
@@ -0,0 +1,39 @@
+# File containing standard options to HostStubGen
+
+--debug
+
+# Uncomment below lines to enable each feature.
+--enable-non-stub-method-check
+# --no-non-stub-method-check
+
+# --enable-method-logging
+
+
+# Standard annotations.
+# Note, each line is a single argument, so we need newlines after each `--xxx-annotation`.
+--stub-annotation
+ android.hosttest.annotation.HostSideTestStub
+
+--keep-annotation
+ android.hosttest.annotation.HostSideTestKeep
+
+--stub-class-annotation
+ android.hosttest.annotation.HostSideTestWholeClassStub
+
+--keep-class-annotation
+ android.hosttest.annotation.HostSideTestWholeClassKeep
+
+--throw-annotation
+ android.hosttest.annotation.HostSideTestThrow
+
+--remove-annotation
+ android.hosttest.annotation.HostSideTestRemove
+
+--substitute-annotation
+ android.hosttest.annotation.HostSideTestSubstitute
+
+--native-substitute-annotation
+ android.hosttest.annotation.HostSideTestNativeSubstitutionClass
+
+--class-load-hook-annotation
+ android.hosttest.annotation.HostSideTestClassLoadHook
diff --git a/tools/hoststubgen/hoststubgen/jarjar-rules.txt b/tools/hoststubgen/hoststubgen/jarjar-rules.txt
new file mode 100644
index 0000000..4e61ba6
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/jarjar-rules.txt
@@ -0,0 +1,2 @@
+# Rename guava
+rule com.google.common.** com.android.hoststubgen.x.@0
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Exceptions.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Exceptions.kt
new file mode 100644
index 0000000..207ba52
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Exceptions.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen
+
+/**
+ * We will not print the stack trace for exceptions implementing it.
+ */
+interface UserErrorException
+
+/**
+ * Exceptions about parsing class files.
+ */
+class ClassParseException(message: String) : Exception(message)
+
+/**
+ * Use it for internal exception that really shouldn't happen.
+ */
+class HostStubGenInternalException(message: String) : Exception(message)
+
+/**
+ * Exceptions about the content in a jar file.
+ */
+class InvalidJarFileException(message: String) : Exception(message), UserErrorException
+
+/**
+ * Exceptions missing classes, fields, methods, etc.
+ */
+class UnknownApiException(message: String) : Exception(message), UserErrorException
+
+/**
+ * Exceptions related to invalid annotations -- e.g. more than one visibility annotation
+ * on a single API.
+ */
+class InvalidAnnotationException(message: String) : Exception(message), UserErrorException
+
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
new file mode 100644
index 0000000..8db4b69
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen
+
+import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.filters.AnnotationBasedFilter
+import com.android.hoststubgen.filters.ClassWidePolicyPropagatingFilter
+import com.android.hoststubgen.filters.ConstantFilter
+import com.android.hoststubgen.filters.FilterPolicy
+import com.android.hoststubgen.filters.ImplicitOutputFilter
+import com.android.hoststubgen.filters.KeepAllClassesFilter
+import com.android.hoststubgen.filters.OutputFilter
+import com.android.hoststubgen.filters.StubIntersectingFilter
+import com.android.hoststubgen.filters.createFilterFromTextPolicyFile
+import com.android.hoststubgen.filters.printAsTextPolicy
+import com.android.hoststubgen.visitors.BaseAdapter
+import org.objectweb.asm.ClassReader
+import org.objectweb.asm.ClassVisitor
+import org.objectweb.asm.ClassWriter
+import org.objectweb.asm.tree.ClassNode
+import org.objectweb.asm.util.CheckClassAdapter
+import java.io.BufferedInputStream
+import java.io.FileOutputStream
+import java.io.InputStream
+import java.io.OutputStream
+import java.io.PrintWriter
+import java.util.zip.ZipEntry
+import java.util.zip.ZipFile
+import java.util.zip.ZipOutputStream
+
+/**
+ * Actual main class.
+ */
+class HostStubGen(val options: HostStubGenOptions) {
+ fun run() {
+ val errors = HostStubGenErrors()
+
+ // Load all classes.
+ val allClasses = loadClassStructures(options.inJar)
+
+ // Dump the classes, if specified.
+ options.inputJarDumpFile?.let {
+ PrintWriter(it).use { pw -> allClasses.dump(pw) }
+ log.i("Dump file created at $it")
+ }
+
+ options.inputJarAsKeepAllFile?.let {
+ PrintWriter(it).use {
+ pw -> allClasses.forEach {
+ classNode -> printAsTextPolicy(pw, classNode)
+ }
+ }
+ log.i("Dump file created at $it")
+ }
+
+ // Build the filters.
+ val filter = buildFilter(errors, allClasses, options)
+
+ // Transform the jar.
+ convert(
+ options.inJar,
+ options.outStubJar,
+ options.outImplJar,
+ filter,
+ options.enableClassChecker,
+ allClasses,
+ errors,
+ )
+ }
+
+ /**
+ * Load all the classes, without code.
+ */
+ private fun loadClassStructures(inJar: String): ClassNodes {
+ log.i("Reading class structure from $inJar ...")
+ val start = System.currentTimeMillis()
+
+ val allClasses = ClassNodes()
+
+ log.withIndent {
+ ZipFile(inJar).use { inZip ->
+ val inEntries = inZip.entries()
+
+ while (inEntries.hasMoreElements()) {
+ val entry = inEntries.nextElement()
+
+ BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
+ if (entry.name.endsWith(".class")) {
+ val cr = ClassReader(bis)
+ val cn = ClassNode()
+ cr.accept(cn, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG
+ or ClassReader.SKIP_FRAMES)
+ if (!allClasses.addClass(cn)) {
+ log.w("Duplicate class found: ${cn.name}")
+ }
+ } else if (entry.name.endsWith(".dex")) {
+ // Seems like it's an ART jar file. We can't process it.
+ // It's a fatal error.
+ throw InvalidJarFileException(
+ "$inJar is not a desktop jar file. It contains a *.dex file.")
+ } else {
+ // Unknown file type. Skip.
+ while (bis.available() > 0) {
+ bis.skip((1024 * 1024).toLong())
+ }
+ }
+ }
+ }
+ }
+ }
+ if (allClasses.size == 0) {
+ log.w("$inJar contains no *.class files.")
+ }
+
+ val end = System.currentTimeMillis()
+ log.v("Done reading class structure in %.1f second(s).", (end - start) / 1000.0)
+ return allClasses
+ }
+
+ /**
+ * Build the filter, which decides what classes/methods/fields should be put in stub or impl
+ * jars, and "how". (e.g. with substitution?)
+ */
+ private fun buildFilter(
+ errors: HostStubGenErrors,
+ allClasses: ClassNodes,
+ options: HostStubGenOptions,
+ ): OutputFilter {
+ // We build a "chain" of multiple filters here.
+ //
+ // The filters are build in from "inside", meaning the first filter created here is
+ // the last filter used, so it has the least precedence.
+ //
+ // So, for example, the "remove" annotation, which is handled by AnnotationBasedFilter,
+ // can override a class-wide annotation, which is handled by
+ // ClassWidePolicyPropagatingFilter, and any annotations can be overridden by the
+ // text-file based filter, which is handled by parseTextFilterPolicyFile.
+
+ // The first filter is for the default policy from the command line options.
+ var filter: OutputFilter = ConstantFilter(options.defaultPolicy, "default-by-options")
+
+ // Next, we need a filter that resolves "class-wide" policies.
+ // This is used when a member (methods, fields, nested classes) don't get any polices
+ // from upper filters. e.g. when a method has no annotations, then this filter will apply
+ // the class-wide policy, if any. (if not, we'll fall back to the above filter.)
+ val classWidePropagator = ClassWidePolicyPropagatingFilter(filter)
+
+ // Next, Java annotation based filter.
+ filter = AnnotationBasedFilter(
+ errors,
+ allClasses,
+ options.stubAnnotations,
+ options.keepAnnotations,
+ options.stubClassAnnotations,
+ options.keepClassAnnotations,
+ options.throwAnnotations,
+ options.removeAnnotations,
+ options.substituteAnnotations,
+ options.nativeSubstituteAnnotations,
+ options.classLoadHookAnnotations,
+ classWidePropagator
+ )
+
+ // Next, "text based" filter, which allows to override polices without touching
+ // the target code.
+ options.policyOverrideFile?.let {
+ filter = createFilterFromTextPolicyFile(it, allClasses, filter)
+ }
+
+ // If `--intersect-stub-jar` is provided, load from these jar files too.
+ // We use this to restrict stub APIs to public/system/test APIs,
+ // by intersecting with a stub jar file created by metalava.
+ if (options.intersectStubJars.size > 0) {
+ val intersectingJars = loadIntersectingJars(options.intersectStubJars)
+
+ filter = StubIntersectingFilter(errors, intersectingJars, filter)
+ }
+
+ // Apply the implicit filter.
+ filter = ImplicitOutputFilter(errors, allClasses, filter)
+
+ // Optionally keep all classes.
+ if (options.keepAllClasses) {
+ filter = KeepAllClassesFilter(filter)
+ }
+
+ return filter
+ }
+
+ /**
+ * Load jar files specified with "--intersect-stub-jar".
+ */
+ private fun loadIntersectingJars(filenames: Set<String>): Map<String, ClassNodes> {
+ val intersectingJars = mutableMapOf<String, ClassNodes>()
+
+ filenames.forEach { filename ->
+ intersectingJars[filename] = loadClassStructures(filename)
+ }
+ return intersectingJars
+ }
+
+ /**
+ * Convert a JAR file into "stub" and "impl" JAR files.
+ */
+ private fun convert(
+ inJar: String,
+ outStubJar: String,
+ outImplJar: String,
+ filter: OutputFilter,
+ enableChecker: Boolean,
+ classes: ClassNodes,
+ errors: HostStubGenErrors,
+ ) {
+ log.i("Converting %s into [stub: %s, impl: %s] ...", inJar, outStubJar, outImplJar)
+ log.i("Checker is %s", if (enableChecker) "enabled" else "disabled")
+
+ val start = System.currentTimeMillis()
+
+ log.withIndent {
+ // Open the input jar file and process each entry.
+ ZipFile(inJar).use { inZip ->
+ ZipOutputStream(FileOutputStream(outStubJar)).use { stubOutStream ->
+ ZipOutputStream(FileOutputStream(outImplJar)).use { implOutStream ->
+ val inEntries = inZip.entries()
+ while (inEntries.hasMoreElements()) {
+ val entry = inEntries.nextElement()
+ convertSingleEntry(inZip, entry, stubOutStream, implOutStream,
+ filter, enableChecker, classes, errors)
+ }
+ log.i("Converted all entries.")
+ }
+ }
+ log.i("Created stub: $outStubJar")
+ log.i("Created impl: $outImplJar")
+ }
+ }
+ val end = System.currentTimeMillis()
+ log.v("Done transforming the jar in %.1f second(s).", (end - start) / 1000.0)
+ }
+
+ /**
+ * Convert a single ZIP entry, which may or may not be a class file.
+ */
+ private fun convertSingleEntry(
+ inZip: ZipFile,
+ entry: ZipEntry,
+ stubOutStream: ZipOutputStream,
+ implOutStream: ZipOutputStream,
+ filter: OutputFilter,
+ enableChecker: Boolean,
+ classes: ClassNodes,
+ errors: HostStubGenErrors,
+ ) {
+ log.d("Entry: %s", entry.name)
+ log.withIndent {
+ val name = entry.name
+
+ // Just ignore all the directories. (TODO: make sure it's okay)
+ if (name.endsWith("/")) {
+ return
+ }
+
+ // If it's a class, convert it.
+ if (name.endsWith(".class")) {
+ processSingleClass(inZip, entry, stubOutStream, implOutStream, filter,
+ enableChecker, classes, errors)
+ return
+ }
+
+ // Handle other file types...
+
+ // - *.uau seems to contain hidden API information.
+ // - *_compat_config.xml is also about compat-framework.
+ if (name.endsWith(".uau") ||
+ name.endsWith("_compat_config.xml")) {
+ log.d("Not needed: %s", entry.name)
+ return
+ }
+
+ // Unknown type, we just copy it to both output zip files.
+ // TODO: We probably shouldn't do it for stub jar?
+ log.v("Copying: %s", entry.name)
+ copyZipEntry(inZip, entry, stubOutStream)
+ copyZipEntry(inZip, entry, implOutStream)
+ }
+ }
+
+ /**
+ * Copy a single ZIP entry to the output.
+ */
+ private fun copyZipEntry(
+ inZip: ZipFile,
+ entry: ZipEntry,
+ out: ZipOutputStream,
+ ) {
+ BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
+ // Copy unknown entries as is to the impl out. (but not to the stub out.)
+ val outEntry = ZipEntry(entry.name)
+ out.putNextEntry(outEntry)
+ while (bis.available() > 0) {
+ out.write(bis.read())
+ }
+ out.closeEntry()
+ }
+ }
+
+ /**
+ * Convert a single class to "stub" and "impl".
+ */
+ private fun processSingleClass(
+ inZip: ZipFile,
+ entry: ZipEntry,
+ stubOutStream: ZipOutputStream,
+ implOutStream: ZipOutputStream,
+ filter: OutputFilter,
+ enableChecker: Boolean,
+ classes: ClassNodes,
+ errors: HostStubGenErrors,
+ ) {
+ val className = entry.name.replaceFirst("\\.class$".toRegex(), "")
+ val classPolicy = filter.getPolicyForClass(className)
+ if (classPolicy.policy == FilterPolicy.Remove) {
+ log.d("Removing class: %s %s", className, classPolicy)
+ return
+ }
+ // Generate stub first.
+ if (classPolicy.policy.needsInStub) {
+ log.v("Creating stub class: %s Policy: %s", className, classPolicy)
+ log.withIndent {
+ BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
+ val newEntry = ZipEntry(entry.name)
+ stubOutStream.putNextEntry(newEntry)
+ convertClass(/*forImpl=*/false, bis, stubOutStream, filter, enableChecker,
+ classes, errors)
+ stubOutStream.closeEntry()
+ }
+ }
+ }
+ log.v("Creating impl class: %s Policy: %s", className, classPolicy)
+ if (classPolicy.policy.needsInImpl) {
+ log.withIndent {
+ BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
+ val newEntry = ZipEntry(entry.name)
+ implOutStream.putNextEntry(newEntry)
+ convertClass(/*forImpl=*/true, bis, implOutStream, filter, enableChecker,
+ classes, errors)
+ implOutStream.closeEntry()
+ }
+ }
+ }
+ }
+
+ /**
+ * Convert a single class to either "stub" or "impl".
+ */
+ private fun convertClass(
+ forImpl: Boolean,
+ input: InputStream,
+ out: OutputStream,
+ filter: OutputFilter,
+ enableChecker: Boolean,
+ classes: ClassNodes,
+ errors: HostStubGenErrors,
+ ) {
+ val cr = ClassReader(input)
+
+ // COMPUTE_FRAMES wouldn't be happy if code uses
+ val flags = ClassWriter.COMPUTE_MAXS // or ClassWriter.COMPUTE_FRAMES
+ val cw = ClassWriter(flags)
+
+ // Connect to the class writer
+ var outVisitor: ClassVisitor = cw
+ if (enableChecker) {
+ outVisitor = CheckClassAdapter(outVisitor)
+ }
+ val visitorOptions = BaseAdapter.Options(
+ enablePreTrace = options.enablePreTrace,
+ enablePostTrace = options.enablePostTrace,
+ enableMethodLogging = options.enablePreTrace,
+ enableNonStubMethodCallDetection = options.enableNonStubMethodCallDetection,
+ errors = errors,
+ )
+ outVisitor = BaseAdapter.getVisitor(classes, outVisitor, filter, forImpl, visitorOptions)
+
+ cr.accept(outVisitor, ClassReader.EXPAND_FRAMES)
+ val data = cw.toByteArray()
+ out.write(data)
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt
new file mode 100644
index 0000000..9df0489
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen
+
+class HostStubGenErrors {
+ fun onErrorFound(message: String) {
+ // For now, we just throw as soon as any error is found, but eventually we should keep
+ // all errors and print them at the end.
+ throw RuntimeException(message)
+ }
+}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
new file mode 100644
index 0000000..5e71a36
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen
+
+import java.io.OutputStream
+import java.io.PrintStream
+
+val log: HostStubGenLogger = HostStubGenLogger()
+
+/** Logging level */
+enum class LogLevel {
+ None,
+ Error,
+ Warn,
+ Info,
+ Verbose,
+ Debug,
+}
+
+/** Simple logging class. */
+class HostStubGenLogger(
+ private var out: PrintStream = System.out!!,
+ var level: LogLevel = LogLevel.Info,
+) {
+ companion object {
+ private val sNullPrintStream: PrintStream = PrintStream(OutputStream.nullOutputStream())
+ }
+
+ private var indentLevel: Int = 0
+ get() = field
+ set(value) {
+ field = value
+ indent = " ".repeat(value)
+ }
+ private var indent: String = ""
+
+ fun indent() {
+ indentLevel++
+ }
+
+ fun unindent() {
+ if (indentLevel <= 0) {
+ throw IllegalStateException("Unbalanced unindent() call.")
+ }
+ indentLevel--
+ }
+
+ inline fun <T> withIndent(block: () -> T): T {
+ try {
+ indent()
+ return block()
+ } finally {
+ unindent()
+ }
+ }
+
+ fun isEnabled(level: LogLevel): Boolean {
+ return level.ordinal <= this.level.ordinal
+ }
+
+ private fun println(message: String) {
+ out.print(indent)
+ out.println(message)
+ }
+
+ /** Log an error. */
+ fun e(message: String) {
+ if (level.ordinal < LogLevel.Error.ordinal) {
+ return
+ }
+ println(message)
+ }
+
+ /** Log an error. */
+ fun e(format: String, vararg args: Any?) {
+ if (level.ordinal < LogLevel.Error.ordinal) {
+ return
+ }
+ e(String.format(format, *args))
+ }
+
+ /** Log a warning. */
+ fun w(message: String) {
+ if (level.ordinal < LogLevel.Warn.ordinal) {
+ return
+ }
+ println(message)
+ }
+
+ /** Log a warning. */
+ fun w(format: String, vararg args: Any?) {
+ if (level.ordinal < LogLevel.Warn.ordinal) {
+ return
+ }
+ w(String.format(format, *args))
+ }
+
+ /** Log an info message. */
+ fun i(message: String) {
+ if (level.ordinal < LogLevel.Info.ordinal) {
+ return
+ }
+ println(message)
+ }
+
+ /** Log a debug message. */
+ fun i(format: String, vararg args: Any?) {
+ if (level.ordinal < LogLevel.Warn.ordinal) {
+ return
+ }
+ i(String.format(format, *args))
+ }
+
+ /** Log a verbose message. */
+ fun v(message: String) {
+ if (level.ordinal < LogLevel.Verbose.ordinal) {
+ return
+ }
+ println(message)
+ }
+
+ /** Log a verbose message. */
+ fun v(format: String, vararg args: Any?) {
+ if (level.ordinal < LogLevel.Verbose.ordinal) {
+ return
+ }
+ v(String.format(format, *args))
+ }
+
+ /** Log a debug message. */
+ fun d(message: String) {
+ if (level.ordinal < LogLevel.Debug.ordinal) {
+ return
+ }
+ println(message)
+ }
+
+ /** Log a debug message. */
+ fun d(format: String, vararg args: Any?) {
+ if (level.ordinal < LogLevel.Warn.ordinal) {
+ return
+ }
+ d(String.format(format, *args))
+ }
+
+ inline fun forVerbose(block: () -> Unit) {
+ if (isEnabled(LogLevel.Verbose)) {
+ block()
+ }
+ }
+
+ inline fun forDebug(block: () -> Unit) {
+ if (isEnabled(LogLevel.Debug)) {
+ block()
+ }
+ }
+
+ /** Return a stream for error. */
+ fun getErrorPrintStream(): PrintStream {
+ if (level.ordinal < LogLevel.Error.ordinal) {
+ return sNullPrintStream
+ }
+
+ // TODO Apply indent
+ return PrintStream(out)
+ }
+
+ /** Return a stream for verbose messages. */
+ fun getVerbosePrintStream(): PrintStream {
+ if (level.ordinal < LogLevel.Verbose.ordinal) {
+ return sNullPrintStream
+ }
+ // TODO Apply indent
+ return PrintStream(out)
+ }
+
+ /** Return a stream for debug messages. */
+ fun getInfoPrintStream(): PrintStream {
+ if (level.ordinal < LogLevel.Info.ordinal) {
+ return sNullPrintStream
+ }
+ // TODO Apply indent
+ return PrintStream(out)
+ }
+}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
new file mode 100644
index 0000000..9a54ecf
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen
+
+import com.android.hoststubgen.filters.FilterPolicy
+import java.io.BufferedReader
+import java.io.File
+import java.io.FileReader
+
+/**
+ * Options that can be set from command line arguments.
+ */
+class HostStubGenOptions(
+ /** Input jar file*/
+ var inJar: String = "",
+
+ /** Output stub jar file */
+ var outStubJar: String = "",
+
+ /** Output implementation jar file */
+ var outImplJar: String = "",
+
+ var inputJarDumpFile: String? = null,
+
+ var inputJarAsKeepAllFile: String? = null,
+
+ var stubAnnotations: MutableSet<String> = mutableSetOf(),
+ var keepAnnotations: MutableSet<String> = mutableSetOf(),
+ var throwAnnotations: MutableSet<String> = mutableSetOf(),
+ var removeAnnotations: MutableSet<String> = mutableSetOf(),
+ var stubClassAnnotations: MutableSet<String> = mutableSetOf(),
+ var keepClassAnnotations: MutableSet<String> = mutableSetOf(),
+
+ var substituteAnnotations: MutableSet<String> = mutableSetOf(),
+ var nativeSubstituteAnnotations: MutableSet<String> = mutableSetOf(),
+ var classLoadHookAnnotations: MutableSet<String> = mutableSetOf(),
+
+ var intersectStubJars: MutableSet<String> = mutableSetOf(),
+
+ var policyOverrideFile: String? = null,
+
+ var defaultPolicy: FilterPolicy = FilterPolicy.Remove,
+ var keepAllClasses: Boolean = false,
+
+ var logLevel: LogLevel = LogLevel.Info,
+
+ var cleanUpOnError: Boolean = false,
+
+ var enableClassChecker: Boolean = false,
+ var enablePreTrace: Boolean = false,
+ var enablePostTrace: Boolean = false,
+
+ var enableMethodLogging: Boolean = false,
+
+ var enableNonStubMethodCallDetection: Boolean = true,
+) {
+ companion object {
+
+ private fun String.ensureFileExists(): String {
+ if (!File(this).exists()) {
+ throw InputFileNotFoundException(this)
+ }
+ return this
+ }
+
+ fun parseArgs(args: Array<String>): HostStubGenOptions {
+ val ret = HostStubGenOptions()
+
+ val ai = ArgIterator(expandAtFiles(args))
+
+ var allAnnotations = mutableSetOf<String>()
+
+ fun ensureUniqueAnnotation(name: String): String {
+ if (!allAnnotations.add(name)) {
+ throw DuplicateAnnotationException(ai.current)
+ }
+ return name
+ }
+
+ while (true) {
+ val arg = ai.nextArgOptional()
+ if (arg == null) {
+ break
+ }
+
+ when (arg) {
+ // TODO: Write help
+ "-h", "--h" -> TODO("Help is not implemented yet")
+
+ "-v", "--verbose" -> ret.logLevel = LogLevel.Verbose
+ "-d", "--debug" -> ret.logLevel = LogLevel.Debug
+ "-q", "--quiet" -> ret.logLevel = LogLevel.None
+
+ "--in-jar" -> ret.inJar = ai.nextArgRequired(arg).ensureFileExists()
+ "--out-stub-jar" -> ret.outStubJar = ai.nextArgRequired(arg)
+ "--out-impl-jar" -> ret.outImplJar = ai.nextArgRequired(arg)
+
+ "--policy-override-file" ->
+ ret.policyOverrideFile = ai.nextArgRequired(arg).ensureFileExists()
+
+ "--clean-up-on-error" -> ret.cleanUpOnError = true
+ "--no-clean-up-on-error" -> ret.cleanUpOnError = false
+
+ "--default-remove" -> ret.defaultPolicy = FilterPolicy.Remove
+ "--default-throw" -> ret.defaultPolicy = FilterPolicy.Throw
+ "--default-keep" -> ret.defaultPolicy = FilterPolicy.Keep
+ "--default-stub" -> ret.defaultPolicy = FilterPolicy.Stub
+
+ "--keep-all-classes" -> ret.keepAllClasses = true
+ "--no-keep-all-classes" -> ret.keepAllClasses = false
+
+ "--stub-annotation" ->
+ ret.stubAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg))
+
+ "--keep-annotation" ->
+ ret.keepAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg))
+
+ "--stub-class-annotation" ->
+ ret.stubClassAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg))
+
+ "--keep-class-annotation" ->
+ ret.keepClassAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg))
+
+ "--throw-annotation" ->
+ ret.throwAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg))
+
+ "--remove-annotation" ->
+ ret.removeAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg))
+
+ "--substitute-annotation" ->
+ ret.substituteAnnotations += ensureUniqueAnnotation(ai.nextArgRequired(arg))
+
+ "--native-substitute-annotation" ->
+ ret.nativeSubstituteAnnotations +=
+ ensureUniqueAnnotation(ai.nextArgRequired(arg))
+
+ "--class-load-hook-annotation" ->
+ ret.classLoadHookAnnotations +=
+ ensureUniqueAnnotation(ai.nextArgRequired(arg))
+
+ "--intersect-stub-jar" ->
+ ret.intersectStubJars += ai.nextArgRequired(arg).ensureFileExists()
+
+ "--gen-keep-all-file" ->
+ ret.inputJarAsKeepAllFile = ai.nextArgRequired(arg)
+
+ // Following options are for debugging.
+ "--enable-class-checker" -> ret.enableClassChecker = true
+ "--no-class-checker" -> ret.enableClassChecker = false
+
+ "--enable-pre-trace" -> ret.enablePreTrace = true
+ "--no-pre-trace" -> ret.enablePreTrace = false
+
+ "--enable-post-trace" -> ret.enablePostTrace = true
+ "--no-post-trace" -> ret.enablePostTrace = false
+
+ "--enable-method-logging" -> ret.enableMethodLogging = true
+ "--no-method-logging" -> ret.enableMethodLogging = false
+
+ "--enable-non-stub-method-check" -> ret.enableNonStubMethodCallDetection = true
+ "--no-non-stub-method-check" -> ret.enableNonStubMethodCallDetection = false
+
+ "--gen-input-dump-file" -> ret.inputJarDumpFile = ai.nextArgRequired(arg)
+
+ else -> throw ArgumentsException("Unknown option: $arg")
+ }
+ }
+ if (ret.inJar.isEmpty()) {
+ throw ArgumentsException("Required option missing: --in-jar")
+ }
+ if (ret.outStubJar.isEmpty()) {
+ throw ArgumentsException("Required option missing: --out-stub-jar")
+ }
+ if (ret.outImplJar.isEmpty()) {
+ throw ArgumentsException("Required option missing: --out-impl-jar")
+ }
+
+ return ret
+ }
+
+ /**
+ * Scan the arguments, and if any of them starts with an `@`, then load from the file
+ * and use its content as arguments.
+ *
+ * In this file, each line is treated as a single argument.
+ *
+ * The file can contain '#' as comments.
+ */
+ private fun expandAtFiles(args: Array<String>): List<String> {
+ val ret = mutableListOf<String>()
+
+ args.forEach { arg ->
+ if (!arg.startsWith('@')) {
+ ret += arg
+ return@forEach
+ }
+ // Read from the file, and add each line to the result.
+ val filename = arg.substring(1).ensureFileExists()
+
+ log.v("Expanding options file $filename")
+
+ BufferedReader(FileReader(filename)).use { reader ->
+ while (true) {
+ var line = reader.readLine()
+ if (line == null) {
+ break // EOF
+ }
+
+ line = normalizeTextLine(line)
+ if (line.isNotEmpty()) {
+ ret += line
+ }
+ }
+ }
+ }
+ return ret
+ }
+ }
+
+ open class ArgumentsException(message: String?) : Exception(message), UserErrorException
+
+ /** Thrown when the same annotation is used with different annotation arguments. */
+ class DuplicateAnnotationException(annotationName: String?) :
+ ArgumentsException("Duplicate annotation specified: '$annotationName'")
+
+ /** Thrown when an input file does not exist. */
+ class InputFileNotFoundException(filename: String) :
+ ArgumentsException("File '$filename' not found")
+
+ private class ArgIterator(
+ private val args: List<String>,
+ private var currentIndex: Int = -1
+ ) {
+ val current: String
+ get() = args.get(currentIndex)
+
+ /**
+ * Get the next argument, or [null] if there's no more arguments.
+ */
+ fun nextArgOptional(): String? {
+ if ((currentIndex + 1) >= args.size) {
+ return null
+ }
+ return args.get(++currentIndex)
+ }
+
+ /**
+ * Get the next argument, or throw if
+ */
+ fun nextArgRequired(argName: String): String {
+ nextArgOptional().let {
+ if (it == null) {
+ throw ArgumentsException("Missing parameter for option $argName")
+ }
+ if (it.isEmpty()) {
+ throw ArgumentsException("Parameter can't be empty for option $argName")
+ }
+ return it
+ }
+ }
+ }
+
+ override fun toString(): String {
+ return """
+ HostStubGenOptions{
+ inJar='$inJar',
+ outStubJar='$outStubJar',
+ outImplJar='$outImplJar',
+ inputJarDumpFile=$inputJarDumpFile,
+ inputJarAsKeepAllFile=$inputJarAsKeepAllFile,
+ stubAnnotations=$stubAnnotations,
+ keepAnnotations=$keepAnnotations,
+ throwAnnotations=$throwAnnotations,
+ removeAnnotations=$removeAnnotations,
+ stubClassAnnotations=$stubClassAnnotations,
+ keepClassAnnotations=$keepClassAnnotations,
+ substituteAnnotations=$substituteAnnotations,
+ nativeSubstituteAnnotations=$nativeSubstituteAnnotations,
+ classLoadHookAnnotations=$classLoadHookAnnotations,
+ intersectStubJars=$intersectStubJars,
+ policyOverrideFile=$policyOverrideFile,
+ defaultPolicy=$defaultPolicy,
+ keepAllClasses=$keepAllClasses,
+ logLevel=$logLevel,
+ cleanUpOnError=$cleanUpOnError,
+ enableClassChecker=$enableClassChecker,
+ enablePreTrace=$enablePreTrace,
+ enablePostTrace=$enablePostTrace,
+ enableMethodLogging=$enableMethodLogging,
+ enableNonStubMethodCallDetection=$enableNonStubMethodCallDetection,
+ }
+ """.trimIndent()
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt
new file mode 100644
index 0000000..0321d9d
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Main.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+@file:JvmName("Main")
+
+package com.android.hoststubgen
+
+const val COMMAND_NAME = "HostStubGen"
+
+/**
+ * Entry point.
+ */
+fun main(args: Array<String>) {
+ var success = false
+ var clanupOnError = false
+ try {
+ // Parse the command line arguments.
+ val options = HostStubGenOptions.parseArgs(args)
+ clanupOnError = options.cleanUpOnError
+
+ log.level = options.logLevel
+
+ log.v("HostStubGen started")
+ log.v("Options: $options")
+
+ // Run.
+ HostStubGen(options).run()
+
+ success = true
+ } catch (e: Exception) {
+ log.e("$COMMAND_NAME: Error: ${e.message}")
+ if (e !is UserErrorException) {
+ e.printStackTrace(log.getErrorPrintStream())
+ }
+ if (clanupOnError) {
+ TODO("clanupOnError is not implemented yet")
+ }
+ }
+
+ log.v("HostStubGen finished")
+
+ System.exit(if (success) 0 else 1 )
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt
new file mode 100644
index 0000000..9fbd6d0
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen
+
+/**
+ * A regex that maches whitespate.
+ */
+val whitespaceRegex = """\s+""".toRegex()
+
+/**
+ * Remove the comment ('#' and following) and surrounding whitespace from a line.
+ */
+fun normalizeTextLine(s: String): String {
+ // Remove # and after. (comment)
+ val pos = s.indexOf('#')
+ val uncommented = if (pos < 0) s else s.substring(0, pos)
+
+ // Remove surrounding whitespace.
+ return uncommented.trim()
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
new file mode 100644
index 0000000..a51bdcf
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.asm
+
+import com.android.hoststubgen.ClassParseException
+import com.android.hoststubgen.HostStubGenInternalException
+import org.objectweb.asm.MethodVisitor
+import org.objectweb.asm.Opcodes
+import org.objectweb.asm.Type
+import org.objectweb.asm.tree.AnnotationNode
+import org.objectweb.asm.tree.ClassNode
+
+
+/** Name of the class initializer method. */
+val CLASS_INITIALIZER_NAME = "<clinit>"
+
+/** Descriptor of the class initializer method. */
+val CLASS_INITIALIZER_DESC = "()V"
+
+/**
+ * Find any of [anyAnnotations] from the list of visible / invisible annotations.
+ */
+fun findAnyAnnotation(
+ anyAnnotations: Set<String>,
+ visibleAnnotations: List<AnnotationNode>?,
+ invisibleAnnotations: List<AnnotationNode>?,
+ ): AnnotationNode? {
+ for (an in visibleAnnotations ?: emptyList()) {
+ if (anyAnnotations.contains(an.desc)) {
+ return an
+ }
+ }
+ for (an in invisibleAnnotations ?: emptyList()) {
+ if (anyAnnotations.contains(an.desc)) {
+ return an
+ }
+ }
+ return null
+}
+
+fun findAnnotationValueAsString(an: AnnotationNode, propertyName: String): String? {
+ for (i in 0..(an.values?.size ?: 0) - 2 step 2) {
+ val name = an.values[i]
+
+ if (name != propertyName) {
+ continue
+ }
+ val value = an.values[i + 1]
+ if (value is String) {
+ return value
+ }
+ throw ClassParseException(
+ "The type of '$name' in annotation \"${an.desc}\" must be String" +
+ ", but is ${value?.javaClass?.canonicalName}")
+ }
+ return null
+}
+
+private val removeLastElement = """[./][^./]*$""".toRegex()
+
+fun getPackageNameFromClassName(className: String): String {
+ return className.replace(removeLastElement, "")
+}
+
+fun resolveClassName(className: String, packageName: String): String {
+ if (className.contains('.') || className.contains('/')) {
+ return className
+ }
+ return "$packageName.$className"
+}
+
+fun String.toJvmClassName(): String {
+ return this.replace('.', '/')
+}
+
+fun String.toHumanReadableClassName(): String {
+ return this.replace('/', '.')
+}
+
+fun String.toHumanReadableMethodName(): String {
+ return this.replace('/', '.')
+}
+
+private val numericalInnerClassName = """.*\$\d+$""".toRegex()
+
+fun isAnonymousInnerClass(cn: ClassNode): Boolean {
+ // TODO: Is there a better way?
+ return cn.name.matches(numericalInnerClassName)
+}
+
+/**
+ * Take a class name. If it's a nested class, then return the name of its direct outer class name.
+ * Otherwise, return null.
+ */
+fun getDirectOuterClassName(className: String): String? {
+ val pos = className.indexOf('$')
+ if (pos < 0) {
+ return null
+ }
+ return className.substring(0, pos)
+}
+
+/**
+ * Write bytecode to push all the method arguments to the stack.
+ * The number of arguments and their type are taken from [methodDescriptor].
+ */
+fun writeByteCodeToPushArguments(methodDescriptor: String, writer: MethodVisitor) {
+ var i = -1
+ Type.getArgumentTypes(methodDescriptor).forEach { type ->
+ i++
+
+ // See https://en.wikipedia.org/wiki/List_of_Java_bytecode_instructions
+
+ // Note, long and double will consume two local variable spaces, so the extra `i++`.
+ when (type) {
+ Type.VOID_TYPE -> throw HostStubGenInternalException("VOID_TYPE not expected")
+ Type.BOOLEAN_TYPE, Type.INT_TYPE, Type.SHORT_TYPE, Type.CHAR_TYPE
+ -> writer.visitVarInsn(Opcodes.ILOAD, i)
+ Type.LONG_TYPE -> writer.visitVarInsn(Opcodes.LLOAD, i++)
+ Type.FLOAT_TYPE -> writer.visitVarInsn(Opcodes.FLOAD, i)
+ Type.DOUBLE_TYPE -> writer.visitVarInsn(Opcodes.DLOAD, i++)
+ else -> writer.visitVarInsn(Opcodes.ALOAD, i)
+ }
+ }
+}
+
+/**
+ * Write bytecode to "RETURN" that matches the method's return type, according to
+ * [methodDescriptor].
+ */
+fun writeByteCodeToReturn(methodDescriptor: String, writer: MethodVisitor) {
+ Type.getReturnType(methodDescriptor).let { type ->
+ // See https://en.wikipedia.org/wiki/List_of_Java_bytecode_instructions
+ when (type) {
+ Type.VOID_TYPE -> writer.visitInsn(Opcodes.RETURN)
+ Type.BOOLEAN_TYPE, Type.INT_TYPE, Type.SHORT_TYPE, Type.CHAR_TYPE
+ -> writer.visitInsn(Opcodes.IRETURN)
+ Type.LONG_TYPE -> writer.visitInsn(Opcodes.LRETURN)
+ Type.FLOAT_TYPE -> writer.visitInsn(Opcodes.FRETURN)
+ Type.DOUBLE_TYPE -> writer.visitInsn(Opcodes.DRETURN)
+ else -> writer.visitInsn(Opcodes.ARETURN)
+ }
+ }
+}
+
+/**
+ * Return the "visibility" modifier from an `access` integer.
+ *
+ * (see https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.1-200-E.1)
+ */
+fun getVisibilityModifier(access: Int): Int {
+ return access and (Opcodes.ACC_PUBLIC or Opcodes.ACC_PRIVATE or Opcodes.ACC_PROTECTED)
+}
+
+/**
+ * Return true if an `access` integer is "private" or "package private".
+ */
+fun isVisibilityPrivateOrPackagePrivate(access: Int): Boolean {
+ return when (getVisibilityModifier(access)) {
+ 0 -> true // Package private.
+ Opcodes.ACC_PRIVATE -> true
+ else -> false
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
new file mode 100644
index 0000000..4df0bfc
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
@@ -0,0 +1,149 @@
+package com.android.hoststubgen.asm
+
+import com.android.hoststubgen.ClassParseException
+import org.objectweb.asm.tree.AnnotationNode
+import org.objectweb.asm.tree.ClassNode
+import org.objectweb.asm.tree.FieldNode
+import org.objectweb.asm.tree.MethodNode
+import org.objectweb.asm.tree.TypeAnnotationNode
+import java.io.PrintWriter
+import java.util.Arrays
+
+/**
+ * Stores all classes loaded from a jar file, in a form of [ClassNode]
+ */
+class ClassNodes {
+ val mAllClasses: MutableMap<String, ClassNode> = HashMap()
+
+ /**
+ * Total number of classes registered.
+ */
+ val size: Int
+ get() = mAllClasses.size
+
+ /** Add a [ClassNode] */
+ fun addClass(cn: ClassNode): Boolean {
+ if (mAllClasses.containsKey(cn.name)) {
+ return false
+ }
+ mAllClasses[cn.name.toJvmClassName()] = cn
+ return true
+ }
+
+ /** Get a class's [ClassNodes] (which may not exist) */
+ fun findClass(name: String): ClassNode? {
+ return mAllClasses[name.toJvmClassName()]
+ }
+
+ /** Get a class's [ClassNodes] (which must exists) */
+ fun getClass(name: String): ClassNode {
+ return findClass(name) ?: throw ClassParseException("Class $name not found")
+ }
+
+ /** Find a field, which may not exist. */
+ fun findField(
+ className: String,
+ fieldName: String,
+ ): FieldNode? {
+ return findClass(className)?.fields?.firstOrNull { it.name == fieldName }?.let { fn ->
+ return fn
+ }
+ }
+
+ /** Find a method, which may not exist. */
+ fun findMethod(
+ className: String,
+ methodName: String,
+ descriptor: String,
+ ): MethodNode? {
+ return findClass(className)?.methods
+ ?.firstOrNull { it.name == methodName && it.desc == descriptor }?.let { mn ->
+ return mn
+ }
+ }
+
+ /** @return true if a class has a class initializer. */
+ fun hasClassInitializer(className: String): Boolean {
+ return findMethod(className, CLASS_INITIALIZER_NAME, CLASS_INITIALIZER_DESC) != null
+ }
+
+ /** Run the lambda on each class in alphabetical order. */
+ fun forEach(consumer: (classNode: ClassNode) -> Unit) {
+ val keys = mAllClasses.keys.toTypedArray()
+ Arrays.sort(keys)
+
+ for (name in keys) {
+ consumer(mAllClasses[name]!!)
+ }
+ }
+
+ /**
+ * Dump all classes.
+ */
+ fun dump(pw: PrintWriter) {
+ forEach { classNode -> dumpClass(pw, classNode) }
+ }
+
+ private fun dumpClass(pw: PrintWriter, cn: ClassNode) {
+ pw.printf("Class: %s [access: %x]\n", cn.name, cn.access)
+ dumpAnnotations(pw, " ",
+ cn.visibleTypeAnnotations, cn.invisibleTypeAnnotations,
+ cn.visibleAnnotations, cn.invisibleAnnotations,
+ )
+
+ for (f in cn.fields ?: emptyList()) {
+ pw.printf(" Field: %s [sig: %s] [desc: %s] [access: %x]\n",
+ f.name, f.signature, f.desc, f.access)
+ dumpAnnotations(pw, " ",
+ f.visibleTypeAnnotations, f.invisibleTypeAnnotations,
+ f.visibleAnnotations, f.invisibleAnnotations,
+ )
+ }
+ for (m in cn.methods ?: emptyList()) {
+ pw.printf(" Method: %s [sig: %s] [desc: %s] [access: %x]\n",
+ m.name, m.signature, m.desc, m.access)
+ dumpAnnotations(pw, " ",
+ m.visibleTypeAnnotations, m.invisibleTypeAnnotations,
+ m.visibleAnnotations, m.invisibleAnnotations,
+ )
+ }
+ }
+
+ private fun dumpAnnotations(
+ pw: PrintWriter,
+ prefix: String,
+ visibleTypeAnnotations: List<TypeAnnotationNode>?,
+ invisibleTypeAnnotations: List<TypeAnnotationNode>?,
+ visibleAnnotations: List<AnnotationNode>?,
+ invisibleAnnotations: List<AnnotationNode>?,
+ ) {
+ for (an in visibleTypeAnnotations ?: emptyList()) {
+ pw.printf("%sTypeAnnotation(vis): %s\n", prefix, an.desc)
+ }
+ for (an in invisibleTypeAnnotations ?: emptyList()) {
+ pw.printf("%sTypeAnnotation(inv): %s\n", prefix, an.desc)
+ }
+ for (an in visibleAnnotations ?: emptyList()) {
+ pw.printf("%sAnnotation(vis): %s\n", prefix, an.desc)
+ if (an.values == null) {
+ continue
+ }
+ var i = 0
+ while (i < an.values.size - 1) {
+ pw.printf("%s - %s -> %s \n", prefix, an.values[i], an.values[i + 1])
+ i += 2
+ }
+ }
+ for (an in invisibleAnnotations ?: emptyList()) {
+ pw.printf("%sAnnotation(inv): %s\n", prefix, an.desc)
+ if (an.values == null) {
+ continue
+ }
+ var i = 0
+ while (i < an.values.size - 1) {
+ pw.printf("%s - %s -> %s \n", prefix, an.values[i], an.values[i + 1])
+ i += 2
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
new file mode 100644
index 0000000..454569d
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.filters
+
+import com.android.hoststubgen.ClassParseException
+import com.android.hoststubgen.HostStubGenErrors
+import com.android.hoststubgen.HostStubGenInternalException
+import com.android.hoststubgen.InvalidAnnotationException
+import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.findAnnotationValueAsString
+import com.android.hoststubgen.asm.findAnyAnnotation
+import com.android.hoststubgen.asm.toHumanReadableMethodName
+import com.android.hoststubgen.asm.toJvmClassName
+import com.android.hoststubgen.log
+import org.objectweb.asm.tree.AnnotationNode
+import org.objectweb.asm.tree.ClassNode
+
+// TODO: Detect invalid cases, such as...
+// - Class's visibility is lower than the members'.
+// - HostSideTestSubstituteWith is set, but it doesn't have @Stub or @Keep
+
+/**
+ * [OutputFilter] using Java annotations.
+ */
+class AnnotationBasedFilter(
+ private val errors: HostStubGenErrors,
+ private val classes: ClassNodes,
+ stubAnnotations_: Set<String>,
+ keepAnnotations_: Set<String>,
+ stubClassAnnotations_: Set<String>,
+ keepClassAnnotations_: Set<String>,
+ throwAnnotations_: Set<String>,
+ removeAnnotations_: Set<String>,
+ substituteAnnotations_: Set<String>,
+ nativeSubstituteAnnotations_: Set<String>,
+ classLoadHookAnnotations_: Set<String>,
+ fallback: OutputFilter,
+) : DelegatingFilter(fallback) {
+ private var stubAnnotations = convertToInternalNames(stubAnnotations_)
+ private var keepAnnotations = convertToInternalNames(keepAnnotations_)
+ private var stubClassAnnotations = convertToInternalNames(stubClassAnnotations_)
+ private var keepClassAnnotations = convertToInternalNames(keepClassAnnotations_)
+ private var throwAnnotations = convertToInternalNames(throwAnnotations_)
+ private var removeAnnotations = convertToInternalNames(removeAnnotations_)
+ private var substituteAnnotations = convertToInternalNames(substituteAnnotations_)
+ private var nativeSubstituteAnnotations = convertToInternalNames(nativeSubstituteAnnotations_)
+ private var classLoadHookAnnotations = convertToInternalNames(classLoadHookAnnotations_)
+
+ /** Annotations that control API visibility. */
+ private var visibilityAnnotations: Set<String> = convertToInternalNames(
+ stubAnnotations_ +
+ keepAnnotations_ +
+ stubClassAnnotations_ +
+ keepClassAnnotations_ +
+ throwAnnotations_ +
+ removeAnnotations_)
+
+ /**
+ * All the annotations we use. Note, this one is in a [convertToJvmNames] format unlike
+ * other ones, because of how it's used.
+ */
+ private var allAnnotations: Set<String> = convertToJvmNames(
+ stubAnnotations_ +
+ keepAnnotations_ +
+ stubClassAnnotations_ +
+ keepClassAnnotations_ +
+ throwAnnotations_ +
+ removeAnnotations_ +
+ substituteAnnotations_ +
+ nativeSubstituteAnnotations_ +
+ classLoadHookAnnotations_)
+
+ private val substitutionHelper = SubstitutionHelper()
+
+ private val reasonAnnotation = "annotation"
+ private val reasonClassAnnotation = "class-annotation"
+
+ /**
+ * Throw if an item has more than one visibility annotations.
+ *
+ * name1 - 4 are only used in exception messages. We take them as separate strings
+ * to avoid unnecessary string concatenations.
+ */
+ private fun detectInvalidAnnotations(
+ visibles: List<AnnotationNode>?,
+ invisibles: List<AnnotationNode>?,
+ type: String,
+ name1: String,
+ name2: String,
+ name3: String,
+ ) {
+ var count = 0
+ for (an in visibles ?: emptyList()) {
+ if (visibilityAnnotations.contains(an.desc)) {
+ count++
+ }
+ }
+ for (an in invisibles ?: emptyList()) {
+ if (visibilityAnnotations.contains(an.desc)) {
+ count++
+ }
+ }
+ if (count > 1) {
+ val description = if (name2 == "" && name3 == "") {
+ "$type $name1"
+ } else {
+ "$type $name1.$name2$name3"
+ }
+ throw InvalidAnnotationException(
+ "Found more than one visibility annotations on $description")
+ }
+ }
+
+ /**
+ * Find a visibility annotation.
+ *
+ * name1 - 4 are only used in exception messages.
+ */
+ private fun findAnnotation(
+ visibles: List<AnnotationNode>?,
+ invisibles: List<AnnotationNode>?,
+ type: String,
+ name1: String,
+ name2: String = "",
+ name3: String = "",
+ ): FilterPolicyWithReason? {
+ detectInvalidAnnotations(visibles, invisibles, type, name1, name2, name3)
+
+ findAnyAnnotation(stubAnnotations, visibles, invisibles)?.let {
+ return FilterPolicy.Stub.withReason(reasonAnnotation)
+ }
+ findAnyAnnotation(stubClassAnnotations, visibles, invisibles)?.let {
+ return FilterPolicy.StubClass.withReason(reasonClassAnnotation)
+ }
+ findAnyAnnotation(keepAnnotations, visibles, invisibles)?.let {
+ return FilterPolicy.Keep.withReason(reasonAnnotation)
+ }
+ findAnyAnnotation(keepClassAnnotations, visibles, invisibles)?.let {
+ return FilterPolicy.KeepClass.withReason(reasonClassAnnotation)
+ }
+ findAnyAnnotation(throwAnnotations, visibles, invisibles)?.let {
+ return FilterPolicy.Throw.withReason(reasonAnnotation)
+ }
+ findAnyAnnotation(removeAnnotations, visibles, invisibles)?.let {
+ return FilterPolicy.Remove.withReason(reasonAnnotation)
+ }
+ return null
+ }
+
+ override fun getPolicyForClass(className: String): FilterPolicyWithReason {
+ val cn = classes.getClass(className)
+
+ findAnnotation(
+ cn.visibleAnnotations,
+ cn.invisibleAnnotations,
+ "class",
+ className)?.let {
+ return it
+ }
+
+ // If it's any of the annotations, then always keep it.
+ if (allAnnotations.contains(className)) {
+ return FilterPolicy.KeepClass.withReason("HostStubGen Annotation")
+ }
+
+ return super.getPolicyForClass(className)
+ }
+
+ override fun getPolicyForField(
+ className: String,
+ fieldName: String
+ ): FilterPolicyWithReason {
+ val cn = classes.getClass(className)
+
+ cn.fields?.firstOrNull { it.name == fieldName }?.let {fn ->
+ findAnnotation(
+ fn.visibleAnnotations,
+ fn.invisibleAnnotations,
+ "field",
+ className,
+ fieldName
+ )?.let { policy ->
+ // If the item has an annotation, then use it.
+ return policy
+ }
+ }
+ return super.getPolicyForField(className, fieldName)
+ }
+
+ override fun getPolicyForMethod(
+ className: String,
+ methodName: String,
+ descriptor: String
+ ): FilterPolicyWithReason {
+ val cn = classes.getClass(className)
+
+ cn.methods?.firstOrNull { it.name == methodName && it.desc == descriptor }?.let { mn ->
+ // @SubstituteWith is going to complicate the policy here, so we ask helper
+ // what to do.
+ substitutionHelper.getPolicyFromSubstitution(cn, mn.name, mn.desc)?.let {
+ return it
+ }
+
+ // If there's no substitution, then we check the annotation.
+ findAnnotation(
+ mn.visibleAnnotations,
+ mn.invisibleAnnotations,
+ "method",
+ className,
+ methodName,
+ descriptor
+ )?.let { policy ->
+ return policy
+ }
+ }
+ return super.getPolicyForMethod(className, methodName, descriptor)
+ }
+
+ override fun getRenameTo(
+ className: String,
+ methodName: String,
+ descriptor: String
+ ): String? {
+ val cn = classes.getClass(className)
+
+ // If the method has a "substitute with" annotation, then return its "value" parameter.
+ cn.methods?.firstOrNull { it.name == methodName && it.desc == descriptor }?.let { mn ->
+ return substitutionHelper.getRenameTo(cn, mn.name, mn.desc)
+ }
+ return null
+ }
+
+ override fun getNativeSubstitutionClass(className: String): String? {
+ classes.getClass(className).let { cn ->
+ findAnyAnnotation(nativeSubstituteAnnotations,
+ cn.visibleAnnotations, cn.invisibleAnnotations)?.let { an ->
+ return getAnnotationField(an, "value")?.toJvmClassName()
+ }
+ }
+ return null
+ }
+
+ override fun getClassLoadHook(className: String): String? {
+ classes.getClass(className).let { cn ->
+ findAnyAnnotation(classLoadHookAnnotations,
+ cn.visibleAnnotations, cn.invisibleAnnotations)?.let { an ->
+ return getAnnotationField(an, "value")?.toHumanReadableMethodName()
+ }
+ }
+ return null
+ }
+
+ private data class MethodKey(val name: String, val desc: String)
+
+ /**
+ * In order to handle substitution, we need to build a reverse mapping of substitution
+ * methods.
+ *
+ * This class automatically builds such a map internally that the above methods can
+ * take advantage of.
+ */
+ private inner class SubstitutionHelper {
+ private var currentClass: ClassNode? = null
+
+ private var policiesFromSubstitution = mutableMapOf<MethodKey, FilterPolicyWithReason>()
+ private var substituteToMethods = mutableMapOf<MethodKey, String>()
+
+ fun getPolicyFromSubstitution(cn: ClassNode, methodName: String, descriptor: String):
+ FilterPolicyWithReason? {
+ setClass(cn)
+ return policiesFromSubstitution[MethodKey(methodName, descriptor)]
+ }
+
+ fun getRenameTo(cn: ClassNode, methodName: String, descriptor: String): String? {
+ setClass(cn)
+ return substituteToMethods[MethodKey(methodName, descriptor)]
+ }
+
+ /**
+ * Every time we see a different class, we scan all its methods for substitution attributes,
+ * and compute (implicit) policies caused by them.
+ *
+ * For example, for the following methods:
+ *
+ * @Stub
+ * @Substitute(suffix = "_host")
+ * private void foo() {
+ * // This isn't supported on the host side.
+ * }
+ * private void foo_host() {
+ * // Host side implementation
+ * }
+ *
+ * We internally handle them as:
+ *
+ * foo() -> Remove
+ * foo_host() -> Stub, and then rename it to foo().
+ */
+ private fun setClass(cn: ClassNode) {
+ if (currentClass == cn) {
+ return
+ }
+ // If the class is changing, we'll rebuild the internal structure.
+ currentClass = cn
+
+ policiesFromSubstitution.clear()
+ substituteToMethods.clear()
+
+ for (mn in cn.methods ?: emptyList()) {
+ findAnyAnnotation(substituteAnnotations,
+ mn.visibleAnnotations,
+ mn.invisibleAnnotations)?.let { an ->
+
+ // Find the policy for this method.
+ val policy = outermostFilter.getPolicyForMethod(cn.name, mn.name, mn.desc)
+ .policy.resolveClassWidePolicy()
+ // Make sure it's either Stub or Keep.
+ if (!(policy.needsInStub || policy.needsInImpl)) {
+ // TODO: Use the real annotation names in the message
+ errors.onErrorFound("@SubstituteWith must have either @Stub or @Keep")
+ return@let
+ }
+ if (!policy.isUsableWithMethods) {
+ throw HostStubGenInternalException("Policy $policy shouldn't show up here")
+ }
+
+ val suffix = getAnnotationField(an, "suffix") ?: return@let
+ val renameFrom = mn.name + suffix
+ val renameTo = mn.name
+
+ if (renameFrom == renameTo) {
+ errors.onErrorFound("@SubstituteWith have a different name")
+ return@let
+ }
+
+ // This mn has "SubstituteWith". This means,
+ // 1. Re move the "rename-to" method, so add it to substitutedMethods.
+ policiesFromSubstitution[MethodKey(renameTo, mn.desc)] =
+ FilterPolicy.Remove.withReason("substitute-to")
+
+ // 2. We also keep the from-to in the map.
+ policiesFromSubstitution[MethodKey(renameFrom, mn.desc)] =
+ policy.withReason("substitute-from")
+ substituteToMethods[MethodKey(renameFrom, mn.desc)] = renameTo
+
+ log.v("Substitution found: %s%s -> %s", renameFrom, mn.desc, renameTo)
+ }
+ }
+ }
+ }
+
+ /**
+ * Return the (String) value of 'value' parameter from an annotation.
+ */
+ private fun getAnnotationField(an: AnnotationNode, name: String): String? {
+ try {
+ val suffix = findAnnotationValueAsString(an, name)
+ if (suffix == null) {
+ errors.onErrorFound("Annotation \"${an.desc}\" must have field $name")
+ }
+ return suffix
+ } catch (e: ClassParseException) {
+ errors.onErrorFound(e.message!!)
+ return null
+ }
+ }
+
+ companion object {
+ /**
+ * Convert from human-readable type names (e.g. "com.android.TypeName") to the internal type
+ * names (e.g. "Lcom/android/TypeName).
+ */
+ private fun convertToInternalNames(input: Set<String>): Set<String> {
+ val ret = mutableSetOf<String>()
+ input.forEach { ret.add("L" + it.toJvmClassName() + ";") }
+ return ret
+ }
+
+ /**
+ * Convert from human-readable type names to JVM type names.
+ */
+ private fun convertToJvmNames(input: Set<String>): Set<String> {
+ val ret = mutableSetOf<String>()
+ input.forEach { ret.add(it.toJvmClassName()) }
+ return ret
+ }
+ }
+}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt
new file mode 100644
index 0000000..6aac3d8
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.filters
+
+import com.android.hoststubgen.asm.getDirectOuterClassName
+
+/**
+ * This is used as the second last fallback filter. This filter propagates the class-wide policy
+ * (obtained from [outermostFilter]) to the fields and methods.
+ */
+class ClassWidePolicyPropagatingFilter(
+ fallback: OutputFilter,
+ ) : DelegatingFilter(fallback) {
+
+ private fun getClassWidePolicy(className: String, resolve: Boolean): FilterPolicyWithReason? {
+ var currentClass = className
+
+ while (true) {
+ outermostFilter.getPolicyForClass(className).let { policy ->
+ if (policy.policy.isClassWidePolicy) {
+ val p = if (resolve) policy.policy.resolveClassWidePolicy() else policy.policy
+
+ return p.withReason(policy.reason).wrapReason("class-wide in $currentClass")
+ }
+ // If the class's policy is remove, then remove it.
+ if (policy.policy == FilterPolicy.Remove) {
+ return FilterPolicy.Remove.withReason("class-wide in $currentClass")
+ }
+ }
+
+ // Next, look at the outer class...
+ val outer = getDirectOuterClassName(currentClass)
+ if (outer == null) {
+ return null
+ }
+ currentClass = outer
+ }
+ }
+
+ override fun getPolicyForClass(className: String): FilterPolicyWithReason {
+ // If it's a nested class, use the outer class's policy.
+ getDirectOuterClassName(className)?.let { outerName ->
+ getClassWidePolicy(outerName, resolve = false)?.let { policy ->
+ return policy
+ }
+ }
+
+ return super.getPolicyForClass(className)
+ }
+
+ override fun getPolicyForField(
+ className: String,
+ fieldName: String
+ ): FilterPolicyWithReason {
+ return getClassWidePolicy(className, resolve = true)
+ ?: super.getPolicyForField(className, fieldName)
+ }
+
+ override fun getPolicyForMethod(
+ className: String,
+ methodName: String,
+ descriptor: String
+ ): FilterPolicyWithReason {
+ return getClassWidePolicy(className, resolve = true)
+ ?: super.getPolicyForMethod(className, methodName, descriptor)
+ }
+}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt
new file mode 100644
index 0000000..33010ba
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.filters
+
+import com.android.hoststubgen.HostStubGenInternalException
+
+
+/**
+ * [OutputFilter] with a given policy. Used to represent the default policy.
+ *
+ * This is used as the last fallback filter.
+ *
+ * @param policy the policy. Cannot be a "substitute" policy.
+ */
+class ConstantFilter(
+ policy: FilterPolicy,
+ val reason: String
+) : OutputFilter() {
+ val classPolicy: FilterPolicy
+ val fieldPolicy: FilterPolicy
+ val methodPolicy: FilterPolicy
+
+ init {
+ if (policy.isSubstitute) {
+ throw HostStubGenInternalException(
+ "ConstantFilter doesn't allow substitution policies.")
+ }
+ if (policy.isClassWidePolicy) {
+ // We prevent it, because there's no point in using class-wide policies because
+ // all members get othe same policy too anyway.
+ throw HostStubGenInternalException(
+ "ConstantFilter doesn't allow class-wide policies.")
+ }
+ methodPolicy = policy
+
+ // TODO: Need to think about the realistic default behavior.
+ classPolicy = if (policy != FilterPolicy.Throw) policy else FilterPolicy.Remove
+ fieldPolicy = classPolicy
+ }
+
+ override fun getPolicyForClass(className: String): FilterPolicyWithReason {
+ return classPolicy.withReason(reason)
+ }
+
+ override fun getPolicyForField(className: String, fieldName: String): FilterPolicyWithReason {
+ return fieldPolicy.withReason(reason)
+ }
+
+ override fun getPolicyForMethod(
+ className: String,
+ methodName: String,
+ descriptor: String,
+ ): FilterPolicyWithReason {
+ return methodPolicy.withReason(reason)
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
new file mode 100644
index 0000000..f0763c4
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.filters
+
+/**
+ * Base class for an [OutputFilter] that uses another filter as a fallback.
+ */
+abstract class DelegatingFilter(
+ // fallback shouldn't be used by subclasses, so make it private.
+ // They should instead be calling into `super` or `outermostFilter`.
+ private val fallback: OutputFilter
+) : OutputFilter() {
+ init {
+ fallback.outermostFilter = this
+ }
+
+ override var outermostFilter: OutputFilter = this
+ get() = field
+ set(value) {
+ field = value
+ // Propagate the inner filters.
+ fallback.outermostFilter = value
+ }
+
+ override fun getPolicyForClass(className: String): FilterPolicyWithReason {
+ return fallback.getPolicyForClass(className)
+ }
+
+ override fun getPolicyForField(
+ className: String,
+ fieldName: String
+ ): FilterPolicyWithReason {
+ return fallback.getPolicyForField(className, fieldName)
+ }
+
+ override fun getPolicyForMethod(
+ className: String,
+ methodName: String,
+ descriptor: String
+ ): FilterPolicyWithReason {
+ return fallback.getPolicyForMethod(className, methodName, descriptor)
+ }
+
+ override fun getRenameTo(
+ className: String,
+ methodName: String,
+ descriptor: String
+ ): String? {
+ return fallback.getRenameTo(className, methodName, descriptor)
+ }
+
+ override fun getNativeSubstitutionClass(className: String): String? {
+ return fallback.getNativeSubstitutionClass(className)
+ }
+
+ override fun getClassLoadHook(className: String): String? {
+ return fallback.getClassLoadHook(className)
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt
new file mode 100644
index 0000000..f11ac2f
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.filters
+
+enum class FilterPolicy {
+ /**
+ * Keep the item in the stub jar file, so tests can use it.
+ */
+ Stub,
+
+ /**
+ * Keep the item in the impl jar file, but not in the stub file. Tests cannot use it directly,
+ * but indirectly.
+ */
+ Keep,
+
+ /**
+ * Only used for types. Keep the class in the stub, and also all its members.
+ * But each member can have another annotations to override it.
+ */
+ StubClass,
+
+ /**
+ * Only used for types. Keep the class in the impl, not in the stub, and also all its members.
+ * But each member can have another annotations to override it.
+ */
+ KeepClass,
+
+ /**
+ * Same as [Stub], but replace it with a "substitution" method. Only usable with methods.
+ */
+ SubstituteAndStub,
+
+ /**
+ * Same as [Keep], but replace it with a "substitution" method. Only usable with methods.
+ */
+ SubstituteAndKeep,
+
+ /**
+ * Only usable with methods. The item will be kept in the impl jar file, but when called,
+ * it'll throw.
+ */
+ Throw,
+
+ /**
+ * Remove the item completely.
+ */
+ Remove;
+
+ val isSubstitute: Boolean
+ get() = this == SubstituteAndStub || this == SubstituteAndKeep
+
+ val needsInStub: Boolean
+ get() = this == Stub || this == StubClass || this == SubstituteAndStub
+
+ val needsInImpl: Boolean
+ get() = this != Remove
+
+ /** Returns whether a policy can be used with classes */
+ val isUsableWithClasses: Boolean
+ get() {
+ return when (this) {
+ Stub, StubClass, Keep, KeepClass, Remove -> true
+ else -> false
+ }
+ }
+
+ /** Returns whether a policy can be used with fields. */
+ val isUsableWithFields: Boolean
+ get() {
+ return when (this) {
+ Stub, Keep, Remove -> true
+ else -> false
+ }
+ }
+
+ /** Returns whether a policy can be used with methods */
+ val isUsableWithMethods: Boolean
+ get() {
+ return when (this) {
+ StubClass, KeepClass -> false
+ else -> true
+ }
+ }
+
+ /** Returns whether a policy is a class-wide one. */
+ val isClassWidePolicy: Boolean
+ get() {
+ return when (this) {
+ StubClass, KeepClass -> true
+ else -> false
+ }
+ }
+
+ fun getSubstitutionBasePolicy(): FilterPolicy {
+ return when (this) {
+ SubstituteAndKeep -> Keep
+ SubstituteAndStub -> Stub
+ else -> this
+ }
+ }
+
+ /**
+ * Convert {Stub,Keep}Class to the corresponding Stub or Keep.
+ */
+ fun resolveClassWidePolicy(): FilterPolicy {
+ return when (this) {
+ StubClass -> Stub
+ KeepClass -> Keep
+ else -> this
+ }
+ }
+
+ /**
+ * Create a [FilterPolicyWithReason] with a given reason.
+ */
+ fun withReason(reason: String): FilterPolicyWithReason {
+ return FilterPolicyWithReason(this, reason)
+ }
+}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt
new file mode 100644
index 0000000..b64a2f5
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.filters
+
+/**
+ * Captures a [FilterPolicy] with a human-readable reason.
+ */
+data class FilterPolicyWithReason (
+ val policy: FilterPolicy,
+ val reason: String = "",
+) {
+ /**
+ * Return a new [FilterPolicy] with an updated reason, while keeping the original reason
+ * as an "inner-reason".
+ */
+ fun wrapReason(reason: String): FilterPolicyWithReason {
+ return FilterPolicyWithReason(policy, "$reason [inner-reason: ${this.reason}]")
+ }
+
+ /**
+ * If the visibility is lower than "Keep" (meaning if it's "remove"),
+ * then return a new [FilterPolicy] with "Keep".
+ * Otherwise, return itself
+ */
+ fun promoteToKeep(promotionReason: String): FilterPolicyWithReason {
+ if (policy.needsInImpl) {
+ return this
+ }
+ val newPolicy = if (policy.isClassWidePolicy) FilterPolicy.KeepClass else FilterPolicy.Keep
+
+ return FilterPolicyWithReason(newPolicy,
+ "$promotionReason [original remove reason: ${this.reason}]")
+ }
+
+ /**
+ * If the visibility is above "Keep" (meaning if it's "stub"),
+ * then return a new [FilterPolicy] with "Keep".
+ * Otherwise, return itself
+ */
+ fun demoteToKeep(promotionReason: String): FilterPolicyWithReason {
+ if (!policy.needsInStub) {
+ return this
+ }
+ val newPolicy = if (policy.isClassWidePolicy) FilterPolicy.KeepClass else FilterPolicy.Keep
+
+ return FilterPolicyWithReason(newPolicy,
+ "$promotionReason [original stub reason: ${this.reason}]")
+ }
+
+ override fun toString(): String {
+ return "[$policy - reason: $reason]"
+ }
+}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
new file mode 100644
index 0000000..9c372ff
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.filters
+
+import com.android.hoststubgen.HostStubGenErrors
+import com.android.hoststubgen.HostStubGenInternalException
+import com.android.hoststubgen.asm.isAnonymousInnerClass
+import com.android.hoststubgen.log
+import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.isVisibilityPrivateOrPackagePrivate
+
+/**
+ * Filter implementing "implicit" rules, such as:
+ * - "keep all anonymous inner classes if the outer class is keep".
+ * (But anonymous inner classes should never be in "stub")
+ * - For classes in stub, make sure private parameterless constructors are also in stub, if any.
+ */
+class ImplicitOutputFilter(
+ private val errors: HostStubGenErrors,
+ private val classes: ClassNodes,
+ fallback: OutputFilter
+) : DelegatingFilter(fallback) {
+ private fun getClassImplicitPolicy(className: String): FilterPolicyWithReason? {
+ // TODO: This check should be cached.
+ val cn = classes.getClass(className)
+
+ if (isAnonymousInnerClass(cn)) {
+ log.forDebug {
+// log.d(" anon-inner class: ${className} outer: ${cn.outerClass} ")
+ }
+ if (cn.outerClass == null) {
+ throw HostStubGenInternalException(
+ "outerClass is null for anonymous inner class")
+ }
+ // If the outer class needs to be in impl, it should be in impl too.
+ val outerPolicy = outermostFilter.getPolicyForClass(cn.outerClass)
+ if (outerPolicy.policy.needsInImpl) {
+ return FilterPolicy.KeepClass.withReason("anonymous-inner-class")
+ }
+ }
+ return null
+ }
+
+ override fun getPolicyForClass(className: String): FilterPolicyWithReason {
+ // Use the implicit policy, if any.
+ getClassImplicitPolicy(className)?.let { return it }
+
+ return super.getPolicyForClass(className)
+ }
+
+ override fun getPolicyForMethod(
+ className: String,
+ methodName: String,
+ descriptor: String
+ ): FilterPolicyWithReason {
+ val fallback = super.getPolicyForMethod(className, methodName, descriptor)
+
+ // If the class is in the stub, then we need to put the private constructor in the stub too,
+ // to prevent the class from getting instantiated.
+ if (outermostFilter.getPolicyForClass(className).policy.needsInStub &&
+ !fallback.policy.needsInStub &&
+ (methodName == "<init>") && // Constructor?
+ (descriptor == "()V")) { // Has zero parameters?
+ classes.findMethod(className, methodName, descriptor)?.let { mn ->
+ if (isVisibilityPrivateOrPackagePrivate(mn.access)) {
+ return FilterPolicy.Stub.withReason("private constructor in stub class")
+ }
+ }
+ }
+
+ return fallback
+ }
+}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
new file mode 100644
index 0000000..f3551d4
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.filters
+
+import com.android.hoststubgen.UnknownApiException
+import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.toHumanReadableClassName
+import com.android.hoststubgen.asm.toHumanReadableMethodName
+
+// TODO: Validate all input names.
+
+class InMemoryOutputFilter(
+ private val classes: ClassNodes,
+ fallback: OutputFilter,
+) : DelegatingFilter(fallback) {
+ private val mPolicies: MutableMap<String, FilterPolicyWithReason> = mutableMapOf()
+ private val mRenames: MutableMap<String, String> = mutableMapOf()
+ private val mNativeSubstitutionClasses: MutableMap<String, String> = mutableMapOf()
+ private val mClassLoadHooks: MutableMap<String, String> = mutableMapOf()
+
+ private fun getClassKey(className: String): String {
+ return className.toHumanReadableClassName()
+ }
+
+ private fun getFieldKey(className: String, fieldName: String): String {
+ return getClassKey(className) + "." + fieldName
+ }
+
+ private fun getMethodKey(className: String, methodName: String, signature: String): String {
+ return getClassKey(className) + "." + methodName + ";" + signature
+ }
+
+ override fun getPolicyForClass(className: String): FilterPolicyWithReason {
+ return mPolicies[getClassKey(className)] ?: super.getPolicyForClass(className)
+ }
+
+ private fun ensureClassExists(className: String) {
+ if (classes.findClass(className) == null) {
+ throw UnknownApiException("Unknown class $className")
+ }
+ }
+
+ private fun ensureFieldExists(className: String, fieldName: String) {
+ if (classes.findField(className, fieldName) == null) {
+ throw UnknownApiException("Unknown field $className.$fieldName")
+ }
+ }
+
+ private fun ensureMethodExists(
+ className: String,
+ methodName: String,
+ descriptor: String
+ ) {
+ if (classes.findMethod(className, methodName, descriptor) == null) {
+ throw UnknownApiException("Unknown method $className.$methodName$descriptor")
+ }
+ }
+
+ fun setPolicyForClass(className: String, policy: FilterPolicyWithReason) {
+ ensureClassExists(className)
+ mPolicies[getClassKey(className)] = policy
+ }
+
+ override fun getPolicyForField(className: String, fieldName: String): FilterPolicyWithReason {
+ return mPolicies[getFieldKey(className, fieldName)]
+ ?: super.getPolicyForField(className, fieldName)
+ }
+
+ fun setPolicyForField(className: String, fieldName: String, policy: FilterPolicyWithReason) {
+ ensureFieldExists(className, fieldName)
+ mPolicies[getFieldKey(className, fieldName)] = policy
+ }
+
+ override fun getPolicyForMethod(
+ className: String,
+ methodName: String,
+ descriptor: String,
+ ): FilterPolicyWithReason {
+ return mPolicies[getMethodKey(className, methodName, descriptor)]
+ ?: super.getPolicyForMethod(className, methodName, descriptor)
+ }
+
+ fun setPolicyForMethod(
+ className: String,
+ methodName: String,
+ descriptor: String,
+ policy: FilterPolicyWithReason,
+ ) {
+ ensureMethodExists(className, methodName, descriptor)
+ mPolicies[getMethodKey(className, methodName, descriptor)] = policy
+ }
+
+ override fun getRenameTo(className: String, methodName: String, descriptor: String): String? {
+ return mRenames[getMethodKey(className, methodName, descriptor)]
+ ?: super.getRenameTo(className, methodName, descriptor)
+ }
+
+ fun setRenameTo(className: String, methodName: String, descriptor: String, toName: String) {
+ ensureMethodExists(className, methodName, descriptor)
+ ensureMethodExists(className, toName, descriptor)
+ mRenames[getMethodKey(className, methodName, descriptor)] = toName
+ }
+
+ override fun getNativeSubstitutionClass(className: String): String? {
+ return mNativeSubstitutionClasses[getClassKey(className)]
+ ?: super.getNativeSubstitutionClass(className)
+ }
+
+ fun setNativeSubstitutionClass(from: String, to: String) {
+ ensureClassExists(from)
+
+ // Native substitute classes may be provided from other jars, so we can't do this check.
+ // ensureClassExists(to)
+ mNativeSubstitutionClasses[getClassKey(from)] = to.toHumanReadableClassName()
+ }
+
+ override fun getClassLoadHook(className: String): String? {
+ return mClassLoadHooks[getClassKey(className)]
+ ?: super.getClassLoadHook(className)
+ }
+
+ fun setClassLoadHook(className: String, methodName: String) {
+ mClassLoadHooks[getClassKey(className)] = methodName.toHumanReadableMethodName()
+ }
+}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepAllClassesFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepAllClassesFilter.kt
new file mode 100644
index 0000000..45dd38d1
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepAllClassesFilter.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.filters
+
+/**
+ * An [OutputFilter] that keeps all classes by default. (but none of its members)
+ *
+ * We're not currently using it, but using it *might* make certain things easier. For example, with
+ * this, all classes would at least be loadable.
+ */
+class KeepAllClassesFilter(fallback: OutputFilter) : DelegatingFilter(fallback) {
+ override fun getPolicyForClass(className: String): FilterPolicyWithReason {
+ // If the default visibility wouldn't keep it, change it to "keep".
+ val f = super.getPolicyForClass(className)
+ return f.promoteToKeep("keep-all-classes")
+ }
+}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt
new file mode 100644
index 0000000..392ee4b
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.filters
+
+/**
+ * Base class for "filters", which decides what APIs should go to the stub / impl jars.
+ */
+abstract class OutputFilter {
+ /**
+ * Filters are stacked over one another. This fields contains the "outermost" filter in a
+ * filter stack chain.
+ *
+ * Subclasses must use this filter to get a policy, when they need to infer a policy
+ * from the policy of another API.
+ *
+ * For example, [ClassWidePolicyPropagatingFilter] needs to check the policy of the enclosing
+ * class to propagate "class-wide" policies, but when it does so, it can't just use
+ * `this.getPolicyForClass()` because that wouldn't return policies decided by "outer"
+ * filters. Instead, it uses [outermostFilter.getPolicyForClass()].
+ *
+ * Note, [outermostFilter] can be itself, so make sure not to cause infinity recursions when
+ * using it.
+ */
+ open var outermostFilter: OutputFilter = this
+ get() = field
+ set(value) {
+ field = value
+ }
+
+ abstract fun getPolicyForClass(className: String): FilterPolicyWithReason
+
+ abstract fun getPolicyForField(className: String, fieldName: String): FilterPolicyWithReason
+
+ abstract fun getPolicyForMethod(
+ className: String,
+ methodName: String,
+ descriptor: String,
+ ): FilterPolicyWithReason
+
+ /**
+ * If a given method is a substitute-from method, return the substitute-to method name.
+ *
+ * The substitute-to and from methods must have the same signature, in the same class.
+ */
+ open fun getRenameTo(className: String, methodName: String, descriptor: String): String? {
+ return null
+ }
+
+ /**
+ * Return a "native substitution class" name for a given class.
+ *
+ * The result will be in a "human readable" form. (e.g. uses '.'s instead of '/'s)
+ *
+ * (which corresponds to @HostSideTestNativeSubstitutionClass of the standard annotations.)
+ */
+ open fun getNativeSubstitutionClass(className: String): String? {
+ return null
+ }
+
+ /**
+ * Return a "class load hook" method name for a given class.
+ *
+ * (which corresponds to @HostSideTestClassLoadHook of the standard annotations.)
+ */
+ open fun getClassLoadHook(className: String): String? {
+ return null
+ }
+}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/StubIntersectingFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/StubIntersectingFilter.kt
new file mode 100644
index 0000000..f92a027
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/StubIntersectingFilter.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.filters
+
+import com.android.hoststubgen.HostStubGenErrors
+import com.android.hoststubgen.asm.ClassNodes
+
+private const val REASON = "demoted, not in intersect jars"
+
+/**
+ * An [OutputFilter] that will restrict what to put in stub to only what shows up in "intersecting
+ * jar" files.
+ *
+ * For example, if the Android public API stub jar is provided, then the HostStubGen's output
+ * stub will be restricted to public APIs.
+ */
+class StubIntersectingFilter(
+ private val errors: HostStubGenErrors,
+ /**
+ * If a class / field / method is not in any of these jars, then we will not put it in
+ * stub.
+ */
+ private val intersectingJars: Map<String, ClassNodes>,
+ fallback: OutputFilter,
+) : DelegatingFilter(fallback) {
+ private inline fun exists(predicate: (ClassNodes) -> Boolean): Boolean {
+ intersectingJars.forEach { entry ->
+ if (predicate(entry.value)) {
+ return true
+ }
+ }
+ return false
+ }
+
+ /**
+ * If [origPolicy] is less than "Stub", then return it as-is.
+ *
+ * Otherwise, call [inStubChecker] to see if the API is in any of [intersectingJars].
+ * If yes, then return [origPolicy] as-is. Otherwise, demote to "Keep".
+ */
+ private fun intersectWithStub(
+ origPolicy: FilterPolicyWithReason,
+ inStubChecker: () -> Boolean,
+ ): FilterPolicyWithReason {
+ if (origPolicy.policy.needsInStub) {
+ // Only check the stub jars, when the class is supposed to be in stub otherwise.
+ if (!inStubChecker()) {
+ return origPolicy.demoteToKeep(REASON)
+ }
+ }
+ return origPolicy
+ }
+
+ override fun getPolicyForClass(className: String): FilterPolicyWithReason {
+ return intersectWithStub(super.getPolicyForClass(className)) {
+ exists { classes -> classes.findClass(className) != null }
+ }
+ }
+
+ override fun getPolicyForField(
+ className: String,
+ fieldName: String
+ ): FilterPolicyWithReason {
+ return intersectWithStub(super.getPolicyForField(className, fieldName)) {
+ exists { classes -> classes.findField(className, fieldName) != null }
+ }
+ }
+
+ override fun getPolicyForMethod(
+ className: String,
+ methodName: String,
+ descriptor: String
+ ): FilterPolicyWithReason {
+ return intersectWithStub(super.getPolicyForMethod(className, methodName, descriptor)) {
+ exists { classes -> classes.findMethod(className, methodName, descriptor) != null }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
new file mode 100644
index 0000000..46546e8
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.filters
+
+import com.android.hoststubgen.UserErrorException
+import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.log
+import com.android.hoststubgen.normalizeTextLine
+import com.android.hoststubgen.whitespaceRegex
+import org.objectweb.asm.Opcodes
+import org.objectweb.asm.tree.ClassNode
+import java.io.BufferedReader
+import java.io.FileReader
+import java.io.PrintWriter
+import java.util.Objects
+
+/**
+ * Print a class node as a "keep" policy.
+ */
+fun printAsTextPolicy(pw: PrintWriter, cn: ClassNode) {
+ pw.printf("class %s\t%s\n", cn.name, "keep")
+
+ for (f in cn.fields ?: emptyList()) {
+ pw.printf(" field %s\t%s\n", f.name, "keep")
+ }
+ for (m in cn.methods ?: emptyList()) {
+ pw.printf(" method %s\t%s\t%s\n", m.name, m.desc, "keep")
+ }
+}
+
+/** Return true if [access] is either public or protected. */
+private fun isVisible(access: Int): Boolean {
+ return (access and (Opcodes.ACC_PUBLIC or Opcodes.ACC_PROTECTED)) != 0
+}
+
+/**
+ * Exception for a parse error.
+ */
+private class ParseException : Exception, UserErrorException {
+ val hasSourceInfo: Boolean
+
+ constructor(message: String) : super(message) {
+ hasSourceInfo = false
+ }
+
+ constructor(message: String, file: String, line: Int) :
+ super("$message in file $file line $line") {
+ hasSourceInfo = true
+ }
+
+ fun withSourceInfo(filename: String, lineNo: Int): ParseException {
+ if (hasSourceInfo) {
+ return this // Already has source information.
+ } else {
+ return ParseException(this.message ?: "", filename, lineNo)
+ }
+ }
+}
+
+private const val FILTER_REASON = "file-override"
+
+/**
+ * Read a given "policy" file and return as an [OutputFilter]
+ */
+fun createFilterFromTextPolicyFile(
+ filename: String,
+ classes: ClassNodes,
+ fallback: OutputFilter,
+ ): OutputFilter {
+ log.i("Loading offloaded annotations from $filename ...")
+ log.withIndent {
+ val ret = InMemoryOutputFilter(classes, fallback)
+
+ var lineNo = 0
+
+ try {
+ BufferedReader(FileReader(filename)).use { reader ->
+ var className = ""
+
+ while (true) {
+ var line = reader.readLine()
+ if (line == null) {
+ break
+ }
+ lineNo++
+
+ line = normalizeTextLine(line)
+
+ if (line.isEmpty()) {
+ continue // skip empty lines.
+ }
+
+ val fields = line.split(whitespaceRegex).toTypedArray()
+ when (fields[0].lowercase()) {
+ "c", "class" -> {
+ if (fields.size < 3) {
+ throw ParseException("Class ('c') expects 2 fields.")
+ }
+ className = fields[1]
+ if (fields[2].startsWith("!")) {
+ // It's a native-substitution.
+ val toClass = fields[2].substring(1)
+ ret.setNativeSubstitutionClass(className, toClass)
+ } else if (fields[2].startsWith("~")) {
+ // It's a class-load hook
+ val callback = fields[2].substring(1)
+ ret.setClassLoadHook(className, callback)
+ } else {
+ val policy = parsePolicy(fields[2])
+ if (!policy.isUsableWithClasses) {
+ throw ParseException("Class can't have policy '$policy'")
+ }
+ Objects.requireNonNull(className)
+
+ // TODO: Duplicate check, etc
+ ret.setPolicyForClass(className, policy.withReason(FILTER_REASON))
+ }
+ }
+
+ "f", "field" -> {
+ if (fields.size < 3) {
+ throw ParseException("Field ('f') expects 2 fields.")
+ }
+ val name = fields[1]
+ val policy = parsePolicy(fields[2])
+ if (!policy.isUsableWithFields) {
+ throw ParseException("Field can't have policy '$policy'")
+ }
+ Objects.requireNonNull(className)
+
+ // TODO: Duplicate check, etc
+ ret.setPolicyForField(className, name, policy.withReason(FILTER_REASON))
+ }
+
+ "m", "method" -> {
+ if (fields.size < 4) {
+ throw ParseException("Method ('m') expects 3 fields.")
+ }
+ val name = fields[1]
+ val signature = fields[2]
+ val policy = parsePolicy(fields[3])
+
+ if (!policy.isUsableWithMethods) {
+ throw ParseException("Method can't have policy '$policy'")
+ }
+
+ Objects.requireNonNull(className)
+
+ ret.setPolicyForMethod(className, name, signature,
+ policy.withReason(FILTER_REASON))
+ if (policy.isSubstitute) {
+ val fromName = fields[3].substring(1)
+
+ if (fromName == name) {
+ throw ParseException(
+ "Substitution must have a different name")
+ }
+
+ // Set the policy for the "from" method.
+ ret.setPolicyForMethod(className, fromName, signature,
+ policy.getSubstitutionBasePolicy()
+ .withReason(FILTER_REASON))
+
+ // Keep "from" -> "to" mapping.
+ ret.setRenameTo(className, fromName, signature, name)
+ }
+ }
+
+ else -> {
+ throw ParseException("Unknown directive \"${fields[0]}\"")
+ }
+ }
+ }
+ }
+ } catch (e: ParseException) {
+ throw e.withSourceInfo(filename, lineNo)
+ }
+ return ret
+ }
+}
+
+private fun parsePolicy(s: String): FilterPolicy {
+ return when (s.lowercase()) {
+ "s", "stub" -> FilterPolicy.Stub
+ "k", "keep" -> FilterPolicy.Keep
+ "t", "throw" -> FilterPolicy.Throw
+ "r", "remove" -> FilterPolicy.Remove
+ "sc", "stubclass" -> FilterPolicy.StubClass
+ "kc", "keepclass" -> FilterPolicy.KeepClass
+ else -> {
+ if (s.startsWith("@")) {
+ FilterPolicy.SubstituteAndStub
+ } else if (s.startsWith("%")) {
+ FilterPolicy.SubstituteAndKeep
+ } else {
+ throw ParseException("Invalid policy \"$s\"")
+ }
+ }
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
new file mode 100644
index 0000000..3cf9a1d
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.visitors
+
+import com.android.hoststubgen.HostStubGenErrors
+import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.getPackageNameFromClassName
+import com.android.hoststubgen.asm.resolveClassName
+import com.android.hoststubgen.asm.toJvmClassName
+import com.android.hoststubgen.filters.FilterPolicy
+import com.android.hoststubgen.filters.FilterPolicyWithReason
+import com.android.hoststubgen.filters.OutputFilter
+import com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+import com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+import com.android.hoststubgen.log
+import org.objectweb.asm.ClassVisitor
+import org.objectweb.asm.FieldVisitor
+import org.objectweb.asm.MethodVisitor
+import org.objectweb.asm.Opcodes
+import org.objectweb.asm.util.TraceClassVisitor
+import java.io.PrintWriter
+
+val OPCODE_VERSION = Opcodes.ASM9
+
+abstract class BaseAdapter (
+ protected val classes: ClassNodes,
+ nextVisitor: ClassVisitor,
+ protected val filter: OutputFilter,
+ protected val options: Options,
+) : ClassVisitor(OPCODE_VERSION, nextVisitor) {
+
+ /**
+ * Options to control the behavior.
+ */
+ data class Options (
+ val errors: HostStubGenErrors,
+ val enablePreTrace: Boolean,
+ val enablePostTrace: Boolean,
+ val enableMethodLogging: Boolean,
+ val enableNonStubMethodCallDetection: Boolean,
+ )
+
+ protected lateinit var currentPackageName: String
+ protected lateinit var currentClassName: String
+ protected var nativeSubstitutionClass: String? = null
+ protected lateinit var classPolicy: FilterPolicyWithReason
+
+ /**
+ * Return whether an item with a given policy should be included in the output.
+ */
+ protected abstract fun shouldEmit(policy: FilterPolicy): Boolean
+
+ override fun visit(
+ version: Int,
+ access: Int,
+ name: String,
+ signature: String?,
+ superName: String?,
+ interfaces: Array<String>,
+ ) {
+ super.visit(version, access, name, signature, superName, interfaces)
+ currentClassName = name
+ currentPackageName = getPackageNameFromClassName(name)
+ classPolicy = filter.getPolicyForClass(currentClassName)
+
+ log.d("[%s] visit: %s (package: %s)", this.javaClass.simpleName, name, currentPackageName)
+ log.indent()
+ log.v("Emitting class: %s", name)
+ log.indent()
+
+ filter.getNativeSubstitutionClass(currentClassName)?.let { className ->
+ val fullClassName = resolveClassName(className, currentPackageName).toJvmClassName()
+ log.d(" NativeSubstitutionClass: $fullClassName")
+ if (classes.findClass(fullClassName) == null) {
+ log.w("Native substitution class $fullClassName not found. Class must be " +
+ "available at runtime.")
+ } else {
+ // If the class exists, it must have a KeepClass policy.
+ if (filter.getPolicyForClass(fullClassName).policy != FilterPolicy.KeepClass) {
+ // TODO: Use real annotation name.
+ options.errors.onErrorFound(
+ "Native substitution class $fullClassName should have @Keep.")
+ }
+ }
+
+ nativeSubstitutionClass = fullClassName
+ }
+ // Inject annotations to generated classes.
+ if (classPolicy.policy.needsInStub) {
+ visitAnnotation(HostStubGenProcessedStubClass.CLASS_DESCRIPTOR, true)
+ }
+ if (classPolicy.policy.needsInImpl) {
+ visitAnnotation(HostStubGenProcessedKeepClass.CLASS_DESCRIPTOR, true)
+ }
+ }
+
+ override fun visitEnd() {
+ log.unindent()
+ log.unindent()
+ super.visitEnd()
+ }
+
+ var skipMemberModificationNestCount = 0
+
+ /**
+ * This method allows writing class members without any modifications.
+ */
+ protected inline fun writeRawMembers(callback: () -> Unit) {
+ skipMemberModificationNestCount++
+ try {
+ callback()
+ } finally {
+ skipMemberModificationNestCount--
+ }
+ }
+
+ override fun visitField(
+ access: Int,
+ name: String,
+ descriptor: String,
+ signature: String?,
+ value: Any?,
+ ): FieldVisitor? {
+ if (skipMemberModificationNestCount > 0) {
+ return super.visitField(access, name, descriptor, signature, value)
+ }
+ val policy = filter.getPolicyForField(currentClassName, name)
+ log.d("visitField: %s %s [%x] Policy: %s", name, descriptor, access, policy)
+
+ log.withIndent {
+ if (!shouldEmit(policy.policy)) {
+ log.d("Removing %s %s", name, policy)
+ return null
+ }
+
+ log.v("Emitting field: %s %s %s", name, descriptor, policy)
+ return super.visitField(access, name, descriptor, signature, value)
+ }
+ }
+
+ override fun visitMethod(
+ access: Int,
+ name: String,
+ descriptor: String,
+ signature: String?,
+ exceptions: Array<String>?,
+ ): MethodVisitor? {
+ if (skipMemberModificationNestCount > 0) {
+ return super.visitMethod(access, name, descriptor, signature, exceptions)
+ }
+ val p = filter.getPolicyForMethod(currentClassName, name, descriptor)
+ log.d("visitMethod: %s%s [%x] [%s] Policy: %s", name, descriptor, access, signature, p)
+
+ log.withIndent {
+ // If it's a substitute-to method, then skip.
+ val policy = filter.getPolicyForMethod(currentClassName, name, descriptor)
+ if (policy.policy.isSubstitute) {
+ log.d("Skipping %s%s %s", name, descriptor, policy)
+ return null
+ }
+ if (!shouldEmit(p.policy)) {
+ log.d("Removing %s%s %s", name, descriptor, policy)
+ return null
+ }
+
+ // Maybe rename the method.
+ val newName: String
+ val substituteTo = filter.getRenameTo(currentClassName, name, descriptor)
+ if (substituteTo != null) {
+ newName = substituteTo
+ log.v("Emitting %s.%s%s as %s %s", currentClassName, name, descriptor,
+ newName, policy)
+ } else {
+ log.v("Emitting method: %s%s %s", name, descriptor, policy)
+ newName = name
+ }
+
+ // Let subclass update the flag.
+ // But note, we only use it when calling the super's method,
+ // but not for visitMethodInner(), beucase when subclass wants to change access,
+ // it can do so inside visitMethodInner().
+ val newAccess = updateAccessFlags(access, name, descriptor)
+
+ return visitMethodInner(access, newName, descriptor, signature, exceptions, policy,
+ super.visitMethod(newAccess, newName, descriptor, signature, exceptions))
+ }
+ }
+
+ open fun updateAccessFlags(
+ access: Int,
+ name: String,
+ descriptor: String,
+ ): Int {
+ return access
+ }
+
+ abstract fun visitMethodInner(
+ access: Int,
+ name: String,
+ descriptor: String,
+ signature: String?,
+ exceptions: Array<String>?,
+ policy: FilterPolicyWithReason,
+ superVisitor: MethodVisitor?,
+ ): MethodVisitor?
+
+ companion object {
+ fun getVisitor(
+ classes: ClassNodes,
+ nextVisitor: ClassVisitor,
+ filter: OutputFilter,
+ forImpl: Boolean,
+ options: Options,
+ ): ClassVisitor {
+ var next = nextVisitor
+
+ val verbosePrinter = PrintWriter(log.getVerbosePrintStream())
+
+ // TODO: This doesn't work yet.
+
+ // Inject TraceClassVisitor for debugging.
+ if (options.enablePostTrace) {
+ next = TraceClassVisitor(next, verbosePrinter)
+ }
+ var ret: ClassVisitor
+ if (forImpl) {
+ ret = ImplGeneratingAdapter(classes, next, filter, options)
+ } else {
+ ret = StubGeneratingAdapter(classes, next, filter, options)
+ }
+
+ // Inject TraceClassVisitor for debugging.
+ if (options.enablePreTrace) {
+ ret = TraceClassVisitor(ret, verbosePrinter)
+ }
+ return ret
+ }
+ }
+}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt
new file mode 100644
index 0000000..8250412
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.visitors
+
+import org.objectweb.asm.AnnotationVisitor
+import org.objectweb.asm.Attribute
+import org.objectweb.asm.Handle
+import org.objectweb.asm.Label
+import org.objectweb.asm.MethodVisitor
+import org.objectweb.asm.Opcodes
+import org.objectweb.asm.TypePath
+
+/**
+ * A method visitor that removes everything from method body.
+ *
+ * To inject a method body, override [visitCode] and create the opcodes there.
+ */
+abstract class BodyReplacingMethodVisitor(
+ access: Int,
+ name: String,
+ descriptor: String,
+ signature: String?,
+ exceptions: Array<String>?,
+ next: MethodVisitor?,
+) : MethodVisitor(OPCODE_VERSION, next) {
+ val isVoid: Boolean
+ val isStatic: Boolean
+
+ init {
+ isVoid = descriptor.endsWith(")V")
+ isStatic = access and Opcodes.ACC_STATIC != 0
+ }
+
+ // Following methods are for things that we need to keep.
+ // Since they're all calling the super method, we can just remove them, but we keep them
+ // just to clarify what we're keeping.
+
+ final override fun visitParameter(
+ name: String?,
+ access: Int
+ ) {
+ super.visitParameter(name, access)
+ }
+
+ final override fun visitAnnotationDefault(): AnnotationVisitor? {
+ return super.visitAnnotationDefault()
+ }
+
+ final override fun visitAnnotation(
+ descriptor: String?,
+ visible: Boolean
+ ): AnnotationVisitor? {
+ return super.visitAnnotation(descriptor, visible)
+ }
+
+ final override fun visitTypeAnnotation(
+ typeRef: Int,
+ typePath: TypePath?,
+ descriptor: String?,
+ visible: Boolean
+ ): AnnotationVisitor? {
+ return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible)
+ }
+
+ final override fun visitAnnotableParameterCount(
+ parameterCount: Int,
+ visible: Boolean
+ ) {
+ super.visitAnnotableParameterCount(parameterCount, visible)
+ }
+
+ final override fun visitParameterAnnotation(
+ parameter: Int,
+ descriptor: String?,
+ visible: Boolean
+ ): AnnotationVisitor? {
+ return super.visitParameterAnnotation(parameter, descriptor, visible)
+ }
+
+ final override fun visitAttribute(attribute: Attribute?) {
+ super.visitAttribute(attribute)
+ }
+
+ override fun visitEnd() {
+ super.visitEnd()
+ }
+
+ /**
+ * Control when to emit the code. We use this to ignore all visitXxx method calls caused by
+ * the original method, so we'll remove all the original code.
+ *
+ * Only when visitXxx methods are called from [emitNewCode], we pass-through to the base class,
+ * so the body will be generated.
+ *
+ * (See also https://asm.ow2.io/asm4-guide.pdf section 3.2.1 about the MethovVisitor
+ * call order.)
+ */
+ var emitCode = false
+
+ final override fun visitCode() {
+ super.visitCode()
+
+ try {
+ emitCode = true
+
+ emitNewCode()
+ } finally {
+ emitCode = false
+ }
+ }
+
+ /**
+ * Subclass must implement it and emit code, and call [visitMaxs] at the end.
+ */
+ abstract fun emitNewCode()
+
+ final override fun visitMaxs(
+ maxStack: Int,
+ maxLocals: Int
+ ) {
+ if (emitCode) {
+ super.visitMaxs(maxStack, maxLocals)
+ }
+ }
+
+ // Following methods are called inside a method body, and we don't want to
+ // emit any of them, so they are all no-op.
+
+ final override fun visitFrame(
+ type: Int,
+ numLocal: Int,
+ local: Array<out Any>?,
+ numStack: Int,
+ stack: Array<out Any>?
+ ) {
+ if (emitCode) {
+ super.visitFrame(type, numLocal, local, numStack, stack)
+ }
+ }
+
+ final override fun visitInsn(opcode: Int) {
+ if (emitCode) {
+ super.visitInsn(opcode)
+ }
+ }
+
+ final override fun visitIntInsn(
+ opcode: Int,
+ operand: Int
+ ) {
+ if (emitCode) {
+ super.visitIntInsn(opcode, operand)
+ }
+ }
+
+ final override fun visitVarInsn(
+ opcode: Int,
+ varIndex: Int
+ ) {
+ if (emitCode) {
+ super.visitVarInsn(opcode, varIndex)
+ }
+ }
+
+ final override fun visitTypeInsn(
+ opcode: Int,
+ type: String?
+ ) {
+ if (emitCode) {
+ super.visitTypeInsn(opcode, type)
+ }
+ }
+
+ final override fun visitFieldInsn(
+ opcode: Int,
+ owner: String?,
+ name: String?,
+ descriptor: String?
+ ) {
+ if (emitCode) {
+ super.visitFieldInsn(opcode, owner, name, descriptor)
+ }
+ }
+
+ final override fun visitMethodInsn(
+ opcode: Int,
+ owner: String?,
+ name: String?,
+ descriptor: String?,
+ isInterface: Boolean
+ ) {
+ if (emitCode) {
+ super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
+ }
+ }
+
+ final override fun visitInvokeDynamicInsn(
+ name: String?,
+ descriptor: String?,
+ bootstrapMethodHandle: Handle?,
+ vararg bootstrapMethodArguments: Any?
+ ) {
+ if (emitCode) {
+ super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle,
+ *bootstrapMethodArguments)
+ }
+ }
+
+ final override fun visitJumpInsn(
+ opcode: Int,
+ label: Label?
+ ) {
+ if (emitCode) {
+ super.visitJumpInsn(opcode, label)
+ }
+ }
+
+ final override fun visitLabel(label: Label?) {
+ if (emitCode) {
+ super.visitLabel(label)
+ }
+ }
+
+ final override fun visitLdcInsn(value: Any?) {
+ if (emitCode) {
+ super.visitLdcInsn(value)
+ }
+ }
+
+ final override fun visitIincInsn(
+ varIndex: Int,
+ increment: Int
+ ) {
+ if (emitCode) {
+ super.visitIincInsn(varIndex, increment)
+ }
+ }
+
+ final override fun visitTableSwitchInsn(
+ min: Int,
+ max: Int,
+ dflt: Label?,
+ vararg labels: Label?
+ ) {
+ if (emitCode) {
+ super.visitTableSwitchInsn(min, max, dflt, *labels)
+ }
+ }
+
+ final override fun visitLookupSwitchInsn(
+ dflt: Label?,
+ keys: IntArray?,
+ labels: Array<out Label>?
+ ) {
+ if (emitCode) {
+ super.visitLookupSwitchInsn(dflt, keys, labels)
+ }
+ }
+
+ final override fun visitMultiANewArrayInsn(
+ descriptor: String?,
+ numDimensions: Int
+ ) {
+ if (emitCode) {
+ super.visitMultiANewArrayInsn(descriptor, numDimensions)
+ }
+ }
+
+ final override fun visitInsnAnnotation(
+ typeRef: Int,
+ typePath: TypePath?,
+ descriptor: String?,
+ visible: Boolean
+ ): AnnotationVisitor? {
+ if (emitCode) {
+ return super.visitInsnAnnotation(typeRef, typePath, descriptor, visible)
+ }
+ return null
+ }
+
+ final override fun visitTryCatchBlock(
+ start: Label?,
+ end: Label?,
+ handler: Label?,
+ type: String?
+ ) {
+ if (emitCode) {
+ super.visitTryCatchBlock(start, end, handler, type)
+ }
+ }
+
+ final override fun visitTryCatchAnnotation(
+ typeRef: Int,
+ typePath: TypePath?,
+ descriptor: String?,
+ visible: Boolean
+ ): AnnotationVisitor? {
+ if (emitCode) {
+ return super.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible)
+ }
+ return null
+ }
+
+ final override fun visitLocalVariable(
+ name: String?,
+ descriptor: String?,
+ signature: String?,
+ start: Label?,
+ end: Label?,
+ index: Int
+ ) {
+ if (emitCode) {
+ super.visitLocalVariable(name, descriptor, signature, start, end, index)
+ }
+ }
+
+ final override fun visitLocalVariableAnnotation(
+ typeRef: Int,
+ typePath: TypePath?,
+ start: Array<out Label>?,
+ end: Array<out Label>?,
+ index: IntArray?,
+ descriptor: String?,
+ visible: Boolean
+ ): AnnotationVisitor? {
+ if (emitCode) {
+ return super.visitLocalVariableAnnotation(
+ typeRef, typePath, start, end, index, descriptor, visible)
+ }
+ return null
+ }
+
+ final override fun visitLineNumber(
+ line: Int,
+ start: Label?
+ ) {
+ if (emitCode) {
+ super.visitLineNumber(line, start)
+ }
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
new file mode 100644
index 0000000..ac06886
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.visitors
+
+import com.android.hoststubgen.asm.CLASS_INITIALIZER_DESC
+import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME
+import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.isVisibilityPrivateOrPackagePrivate
+import com.android.hoststubgen.asm.writeByteCodeToPushArguments
+import com.android.hoststubgen.asm.writeByteCodeToReturn
+import com.android.hoststubgen.filters.FilterPolicy
+import com.android.hoststubgen.filters.FilterPolicyWithReason
+import com.android.hoststubgen.filters.OutputFilter
+import com.android.hoststubgen.hosthelper.HostTestUtils
+import com.android.hoststubgen.log
+import org.objectweb.asm.ClassVisitor
+import org.objectweb.asm.MethodVisitor
+import org.objectweb.asm.Opcodes
+import org.objectweb.asm.Type
+
+/**
+ * An adapter that generates the "impl" class file from an input class file.
+ */
+class ImplGeneratingAdapter(
+ classes: ClassNodes,
+ nextVisitor: ClassVisitor,
+ filter: OutputFilter,
+ options: Options,
+) : BaseAdapter(classes, nextVisitor, filter, options) {
+
+ override fun shouldEmit(policy: FilterPolicy): Boolean {
+ return policy.needsInImpl
+ }
+
+ private var classLoadHookMethod: String? = null
+
+ override fun visit(
+ version: Int,
+ access: Int,
+ name: String,
+ signature: String?,
+ superName: String?,
+ interfaces: Array<String>
+ ) {
+ super.visit(version, access, name, signature, superName, interfaces)
+
+ classLoadHookMethod = filter.getClassLoadHook(currentClassName)
+
+ // classLoadHookMethod is non-null, then we need to inject code to call it
+ // in the class initializer.
+ // If the target class already has a class initializer, then we need to inject code to it.
+ // Otherwise, we need to create one.
+
+ classLoadHookMethod?.let { callback ->
+ log.d(" ClassLoadHook: $callback")
+ if (!classes.hasClassInitializer(currentClassName)) {
+ injectClassLoadHook(callback)
+ }
+ }
+ }
+
+ private fun injectClassLoadHook(callback: String) {
+ writeRawMembers {
+ // Create a class initializer to call onClassLoaded().
+ // Each class can only have at most one class initializer, but the base class
+ // StaticInitMerger will merge it with the existing one, if any.
+ visitMethod(
+ Opcodes.ACC_PRIVATE or Opcodes.ACC_STATIC,
+ "<clinit>",
+ "()V",
+ null,
+ null
+ )!!.let { mv ->
+ // Method prologue
+ mv.visitCode()
+
+ writeClassLoadHookCall(mv)
+ mv.visitInsn(Opcodes.RETURN)
+
+ // Method epilogue
+ mv.visitMaxs(0, 0)
+ mv.visitEnd()
+ }
+ }
+ }
+
+ private fun writeClassLoadHookCall(mv: MethodVisitor) {
+ // First argument: the class type.
+ mv.visitLdcInsn(Type.getType("L" + currentClassName + ";"))
+
+ // Second argument: method name
+ mv.visitLdcInsn(classLoadHookMethod)
+
+ // Call HostTestUtils.onClassLoaded().
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC,
+ HostTestUtils.CLASS_INTERNAL_NAME,
+ "onClassLoaded",
+ "(Ljava/lang/Class;Ljava/lang/String;)V",
+ false
+ )
+ }
+
+ override fun updateAccessFlags(
+ access: Int,
+ name: String,
+ descriptor: String,
+ ): Int {
+ if ((access and Opcodes.ACC_NATIVE) != 0 && nativeSubstitutionClass != null) {
+ return access and Opcodes.ACC_NATIVE.inv()
+ }
+ return access
+ }
+
+ override fun visitMethodInner(
+ access: Int,
+ name: String,
+ descriptor: String,
+ signature: String?,
+ exceptions: Array<String>?,
+ policy: FilterPolicyWithReason,
+ superVisitor: MethodVisitor?,
+ ): MethodVisitor? {
+ // Inject method log, if needed.
+ var innerVisitor = superVisitor
+
+ // If method logging is enabled, inject call to the logging method.
+ if (options.enableMethodLogging) {
+ innerVisitor = LogInjectingMethodAdapter(
+ access,
+ name,
+ descriptor,
+ signature,
+ exceptions,
+ innerVisitor,
+ )
+ }
+
+ // If this class already has a class initializer and a class load hook is needed, then
+ // we inject code.
+ if (classLoadHookMethod != null &&
+ name == CLASS_INITIALIZER_NAME &&
+ descriptor == CLASS_INITIALIZER_DESC) {
+ innerVisitor = ClassLoadHookInjectingMethodAdapter(
+ access,
+ name,
+ descriptor,
+ signature,
+ exceptions,
+ innerVisitor,
+ )
+ }
+
+ // If non-stub method call detection is enabled, then inject a call to the checker.
+ if (options.enableNonStubMethodCallDetection && doesMethodNeedNonStubCallCheck(
+ access, name, descriptor, policy) ) {
+ innerVisitor = NonStubMethodCallDetectingAdapter(
+ access,
+ name,
+ descriptor,
+ signature,
+ exceptions,
+ innerVisitor,
+ )
+ }
+
+ log.withIndent {
+ if ((access and Opcodes.ACC_NATIVE) != 0 && nativeSubstitutionClass != null) {
+ log.v("Rewriting native method...")
+ return NativeSubstitutingMethodAdapter(
+ access, name, descriptor, signature, exceptions, innerVisitor)
+ }
+ if (policy.policy == FilterPolicy.Throw) {
+ log.v("Making method throw...")
+ return ThrowingMethodAdapter(
+ access, name, descriptor, signature, exceptions, innerVisitor)
+ }
+ }
+
+ return innerVisitor
+ }
+
+ fun doesMethodNeedNonStubCallCheck(
+ access: Int,
+ name: String,
+ descriptor: String,
+ policy: FilterPolicyWithReason,
+ ): Boolean {
+ // If a method is in the stub, then no need to check.
+ if (policy.policy.needsInStub) {
+ return false
+ }
+ // If a method is private or package-private, no need to check.
+ // Technically test code can use framework package name, so it's a bit too lenient.
+ if (isVisibilityPrivateOrPackagePrivate(access)) {
+ return false
+ }
+ // TODO: If the method overrides a method that's accessible by tests, then we shouldn't
+ // do the check. (e.g. overrides a stub method or java standard method.)
+
+ return true
+ }
+
+ /**
+ * A method adapter that replaces the method body with a HostTestUtils.onThrowMethodCalled()
+ * call.
+ */
+ private inner class ThrowingMethodAdapter(
+ access: Int,
+ val name: String,
+ descriptor: String,
+ signature: String?,
+ exceptions: Array<String>?,
+ next: MethodVisitor?
+ ) : BodyReplacingMethodVisitor(access, name, descriptor, signature, exceptions, next) {
+ override fun emitNewCode() {
+ visitMethodInsn(Opcodes.INVOKESTATIC,
+ HostTestUtils.CLASS_INTERNAL_NAME,
+ "onThrowMethodCalled",
+ "()V",
+ false)
+
+ // We still need a RETURN opcode for the return type.
+ // For now, let's just inject a `throw`.
+ visitTypeInsn(Opcodes.NEW, "java/lang/RuntimeException")
+ visitInsn(Opcodes.DUP)
+ visitLdcInsn("Unreachable")
+ visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/RuntimeException",
+ "<init>", "(Ljava/lang/String;)V", false)
+ visitInsn(Opcodes.ATHROW)
+
+ // visitMaxs(3, if (isStatic) 0 else 1)
+ visitMaxs(0, 0) // We let ASM figure them out.
+ }
+ }
+
+ /**
+ * A method adapter that replaces a native method call with a call to the "native substitution"
+ * class.
+ */
+ private inner class NativeSubstitutingMethodAdapter(
+ access: Int,
+ private val name: String,
+ private val descriptor: String,
+ signature: String?,
+ exceptions: Array<String>?,
+ next: MethodVisitor?
+ ) : MethodVisitor(OPCODE_VERSION, next) {
+ override fun visitCode() {
+ super.visitCode()
+
+ throw RuntimeException("NativeSubstitutingMethodVisitor should be called on " +
+ " native method, where visitCode() shouldn't be called.")
+ }
+
+ override fun visitEnd() {
+ writeByteCodeToPushArguments(descriptor, this)
+
+ visitMethodInsn(Opcodes.INVOKESTATIC,
+ nativeSubstitutionClass,
+ name,
+ descriptor,
+ false)
+
+ writeByteCodeToReturn(descriptor, this)
+
+ visitMaxs(99, 0) // We let ASM figure them out.
+ super.visitEnd()
+ }
+ }
+
+ /**
+ * A method adapter that injects a call to HostTestUtils.logMethodCall() to every method.
+ *
+ * Note, when the target method is a constructor, it may contain calls to `super(...)` or
+ * `this(...)`. The logging code will be injected *before* such calls.
+ */
+ private inner class LogInjectingMethodAdapter(
+ access: Int,
+ val name: String,
+ val descriptor: String,
+ signature: String?,
+ exceptions: Array<String>?,
+ next: MethodVisitor?
+ ) : MethodVisitor(OPCODE_VERSION, next) {
+ override fun visitCode() {
+ super.visitCode()
+ visitLdcInsn(currentClassName)
+ visitLdcInsn(name)
+ visitLdcInsn(descriptor)
+ visitMethodInsn(Opcodes.INVOKESTATIC,
+ HostTestUtils.CLASS_INTERNAL_NAME,
+ "logMethodCall",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
+ false)
+ }
+ }
+
+ /**
+ * Inject a class load hook call.
+ */
+ private inner class ClassLoadHookInjectingMethodAdapter(
+ access: Int,
+ val name: String,
+ val descriptor: String,
+ signature: String?,
+ exceptions: Array<String>?,
+ next: MethodVisitor?
+ ) : MethodVisitor(OPCODE_VERSION, next) {
+ override fun visitCode() {
+ super.visitCode()
+
+ writeClassLoadHookCall(this)
+ }
+ }
+
+ /**
+ * A method adapter that detects calls to non-stub methods.
+ */
+ private inner class NonStubMethodCallDetectingAdapter(
+ access: Int,
+ val name: String,
+ val descriptor: String,
+ signature: String?,
+ exceptions: Array<String>?,
+ next: MethodVisitor?
+ ) : MethodVisitor(OPCODE_VERSION, next) {
+ override fun visitCode() {
+ super.visitCode()
+
+ // First three arguments to HostTestUtils.onNonStubMethodCalled().
+ visitLdcInsn(currentClassName)
+ visitLdcInsn(name)
+ visitLdcInsn(descriptor)
+
+ // Call: HostTestUtils.getStackWalker().getCallerClass().
+ // This push the caller Class in the stack.
+ visitMethodInsn(Opcodes.INVOKESTATIC,
+ HostTestUtils.CLASS_INTERNAL_NAME,
+ "getStackWalker",
+ "()Ljava/lang/StackWalker;",
+ false)
+ visitMethodInsn(Opcodes.INVOKEVIRTUAL,
+ "java/lang/StackWalker",
+ "getCallerClass",
+ "()Ljava/lang/Class;",
+ false)
+
+ // Then call onNonStubMethodCalled().
+ visitMethodInsn(Opcodes.INVOKESTATIC,
+ HostTestUtils.CLASS_INTERNAL_NAME,
+ "onNonStubMethodCalled",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V",
+ false)
+ }
+ }
+}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/StubGeneratingAdapter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/StubGeneratingAdapter.kt
new file mode 100644
index 0000000..37e2a88
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/StubGeneratingAdapter.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.visitors
+
+import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.filters.FilterPolicy
+import com.android.hoststubgen.filters.FilterPolicyWithReason
+import com.android.hoststubgen.filters.OutputFilter
+import com.android.hoststubgen.log
+import org.objectweb.asm.ClassVisitor
+import org.objectweb.asm.MethodVisitor
+import org.objectweb.asm.Opcodes
+
+/**
+ * An adapter that generates the "impl" class file from an input class file.
+ */
+class StubGeneratingAdapter(
+ classes: ClassNodes,
+ nextVisitor: ClassVisitor,
+ filter: OutputFilter,
+ options: Options,
+) : BaseAdapter(classes, nextVisitor, filter, options) {
+
+ override fun shouldEmit(policy: FilterPolicy): Boolean {
+ return policy.needsInStub
+ }
+
+ override fun visitMethodInner(
+ access: Int,
+ name: String,
+ descriptor: String,
+ signature: String?,
+ exceptions: Array<String>?,
+ policy: FilterPolicyWithReason,
+ superVisitor: MethodVisitor?,
+ ): MethodVisitor? {
+ return StubMethodVisitor(access, name, descriptor, signature, exceptions, superVisitor)
+ }
+
+ private inner class StubMethodVisitor(
+ access: Int,
+ val name: String,
+ descriptor: String,
+ signature: String?,
+ exceptions: Array<String>?,
+ next: MethodVisitor?
+ ) : BodyReplacingMethodVisitor(access, name, descriptor, signature, exceptions, next) {
+ override fun emitNewCode() {
+ log.d(" Generating stub method for $currentClassName.$name")
+
+ // Inject the following code:
+ // throw new RuntimeException("Stub!");
+
+ /*
+ NEW java/lang/RuntimeException
+ DUP
+ LDC "not supported on host side"
+ INVOKESPECIAL java/lang/RuntimeException.<init> (Ljava/lang/String;)V
+ ATHROW
+ MAXSTACK = 3
+ MAXLOCALS = 2 <- 1 for this, 1 for return value.
+ */
+ visitTypeInsn(Opcodes.NEW, "java/lang/RuntimeException")
+ visitInsn(Opcodes.DUP)
+ visitLdcInsn("Stub!")
+ visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/RuntimeException",
+ "<init>", "(Ljava/lang/String;)V", false)
+ visitInsn(Opcodes.ATHROW)
+ visitMaxs(0, 0) // We let ASM figure them out.
+ }
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-framework/Android.bp b/tools/hoststubgen/hoststubgen/test-framework/Android.bp
new file mode 100644
index 0000000..2b91cc1
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-framework/Android.bp
@@ -0,0 +1,19 @@
+// Copyright (C) 2023 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+build = ["AndroidHostTest.bp"]
diff --git a/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp b/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp
new file mode 100644
index 0000000..e7fb2de
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-framework/AndroidHostTest.bp
@@ -0,0 +1,44 @@
+// Copyright (C) 2023 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.
+
+// Add `build = ["AndroidHostTest.bp"]` to Android.bp to include this file.
+
+// Compile the test jar, using 2 rules.
+// 1. Build the test against the stub.
+java_library_host {
+ name: "HostStubGenTest-framework-test-host-test-lib",
+ defaults: ["hosttest-with-framework-all-hidden-api-test-lib-defaults"],
+ srcs: [
+ "src/**/*.java",
+ ],
+ static_libs: [
+ "junit",
+ "truth-prebuilt",
+ "mockito",
+
+ // http://cs/h/googleplex-android/platform/superproject/main/+/main:platform_testing/libraries/annotations/src/android/platform/test/annotations/
+ "platform-test-annotations",
+ "hoststubgen-annotations",
+ ],
+}
+
+// 2. Link the above module with necessary runtime dependencies, so it can be executed stand-alone.
+java_test_host {
+ name: "HostStubGenTest-framework-all-test-host-test",
+ defaults: ["hosttest-with-framework-all-hidden-api-test-defaults"],
+ static_libs: [
+ "HostStubGenTest-framework-test-host-test-lib",
+ ],
+ test_suites: ["general-tests"],
+}
diff --git a/tools/hoststubgen/hoststubgen/test-framework/AndroidTest-host.xml b/tools/hoststubgen/hoststubgen/test-framework/AndroidTest-host.xml
new file mode 100644
index 0000000..f35dcf6
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-framework/AndroidTest-host.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 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.
+-->
+
+<!-- [Ravenwood] Copied from $ANDROID_BUILD_TOP/cts/hostsidetests/devicepolicy/AndroidTest.xml -->
+<configuration description="CtsContentTestCases host-side test">
+ <option name="test-suite-tag" value="ravenwood" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
+
+ <test class="com.android.tradefed.testtype.IsolatedHostTest" >
+ <option name="jar" value="HostStubGenTest-framework-all-test-host-test.jar" />
+ </test>
+</configuration>
diff --git a/tools/hoststubgen/hoststubgen/test-framework/README.md b/tools/hoststubgen/hoststubgen/test-framework/README.md
new file mode 100644
index 0000000..20e2f87
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-framework/README.md
@@ -0,0 +1,27 @@
+# HostStubGen: real framework test
+
+This directory contains tests against the actual framework.jar code. The tests were
+copied from somewhere else in the android tree. We use this directory to quickly run existing
+tests.
+
+## How to run
+
+- With `atest`. This is the proper way to run it, but it may fail due to atest's known problems.
+
+ See the top level README.md on why `--no-bazel-mode` is needed (for now).
+
+```
+$ atest --no-bazel-mode HostStubGenTest-framework-test-host-test
+```
+
+- With `run-ravenwood-test`
+
+```
+$ run-ravenwood-test HostStubGenTest-framework-test-host-test
+```
+
+- Advanced option: `run-test-without-atest.sh` runs the test without using `atest` or `run-ravenwood-test`
+
+```
+$ ./run-test-without-atest.sh
+```
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/test-framework/run-test-without-atest.sh b/tools/hoststubgen/hoststubgen/test-framework/run-test-without-atest.sh
new file mode 100755
index 0000000..cfc06a1
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-framework/run-test-without-atest.sh
@@ -0,0 +1,85 @@
+#!/bin/bash
+# Copyright (C) 2023 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 HostStubGenTest-framework-test-host-test directly with JUnit.
+# (without using atest.)
+
+source "${0%/*}"/../../common.sh
+
+
+# Options:
+# -v enable verbose log
+# -d enable debugger
+
+verbose=0
+debug=0
+while getopts "vd" opt; do
+ case "$opt" in
+ v) verbose=1 ;;
+ d) debug=1 ;;
+ esac
+done
+shift $(($OPTIND - 1))
+
+
+if (( $verbose )) ; then
+ JAVA_OPTS="$JAVA_OPTS -verbose:class"
+fi
+
+if (( $debug )) ; then
+ JAVA_OPTS="$JAVA_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8700"
+fi
+
+#=======================================
+module=HostStubGenTest-framework-all-test-host-test
+module_jar=$ANDROID_BUILD_TOP/out/host/linux-x86/testcases/$module/$module.jar
+run m $module
+
+out=out
+
+rm -fr $out
+mkdir -p $out
+
+
+# Copy and extract the relevant jar files so we can look into them.
+run cp \
+ $module_jar \
+ $SOONG_INT/frameworks/base/tools/hoststubgen/hoststubgen/framework-all-hidden-api-host/linux_glibc_common/gen/*.jar \
+ $out
+
+run extract $out/*.jar
+
+# Result is the number of failed tests.
+result=0
+
+
+# This suite runs all tests in the JAR.
+tests=(com.android.hoststubgen.hosthelper.HostTestSuite)
+
+# Uncomment this to run a specific test.
+# tests=(com.android.hoststubgen.frameworktest.LogTest)
+
+
+for class in ${tests[@]} ; do
+ echo "Running $class ..."
+
+ run cd "${module_jar%/*}"
+ run $JAVA $JAVA_OPTS \
+ -cp $module_jar \
+ org.junit.runner.JUnitCore \
+ $class || result=$(( $result + 1 ))
+done
+
+exit $result
diff --git a/tools/hoststubgen/hoststubgen/test-framework/src/com/android/hoststubgen/frameworktest/ArrayMapTest.java b/tools/hoststubgen/hoststubgen/test-framework/src/com/android/hoststubgen/frameworktest/ArrayMapTest.java
new file mode 100644
index 0000000..62bbf48
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-framework/src/com/android/hoststubgen/frameworktest/ArrayMapTest.java
@@ -0,0 +1,737 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.frameworktest;
+
+// [ravewnwood] Copied from cts/, and commented out unsupported stuff.
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.AbstractMap;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.function.BiFunction;
+
+/**
+ * Some basic tests for {@link android.util.ArrayMap}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class ArrayMapTest {
+ static final boolean DEBUG = false;
+
+ static final int OP_ADD = 1;
+ static final int OP_REM = 2;
+
+ static int[] OPS = new int[]{
+ OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
+ OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
+ OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
+ OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
+
+ OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
+ OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
+
+ OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
+ OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
+
+ OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
+ OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
+
+ OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
+ OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
+ OP_ADD, OP_ADD, OP_ADD,
+ OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
+ OP_REM, OP_REM, OP_REM,
+ OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
+ };
+
+ static int[] KEYS = new int[]{
+ // General adding and removing.
+ -1, 1900, 600, 200, 1200, 1500, 1800, 100, 1900,
+ 2100, 300, 800, 600, 1100, 1300, 2000, 1000, 1400,
+ 600, -1, 1900, 600, 300, 2100, 200, 800, 800,
+ 1800, 1500, 1300, 1100, 2000, 1400, 1000, 1200, 1900,
+
+ // Shrink when removing item from end.
+ 100, 200, 300, 400, 500, 600, 700, 800, 900,
+ 900, 800, 700, 600, 500, 400, 300, 200, 100,
+
+ // Shrink when removing item from middle.
+ 100, 200, 300, 400, 500, 600, 700, 800, 900,
+ 900, 800, 700, 600, 500, 400, 200, 300, 100,
+
+ // Shrink when removing item from front.
+ 100, 200, 300, 400, 500, 600, 700, 800, 900,
+ 900, 800, 700, 600, 500, 400, 100, 200, 300,
+
+ // Test hash collisions.
+ 105, 106, 108, 104, 102, 102, 107, 5, 205,
+ 4, 202, 203, 3, 5, 101, 109, 200, 201,
+ 0, -1, 100,
+ 106, 108, 104, 102, 103, 105, 107, 101, 109,
+ -1, 100, 0,
+ 4, 5, 3, 5, 200, 203, 202, 201, 205,
+ };
+
+ public static class ControlledHash implements Parcelable {
+ final int mValue;
+
+ ControlledHash(int value) {
+ mValue = value;
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (o == null) {
+ return false;
+ }
+ return mValue == ((ControlledHash)o).mValue;
+ }
+
+ @Override
+ public final int hashCode() {
+ return mValue/100;
+ }
+
+ @Override
+ public final String toString() {
+ return Integer.toString(mValue);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mValue);
+ }
+
+ public static final Parcelable.Creator<ControlledHash> CREATOR
+ = new Parcelable.Creator<ControlledHash>() {
+ public ControlledHash createFromParcel(Parcel in) {
+ return new ControlledHash(in.readInt());
+ }
+
+ public ControlledHash[] newArray(int size) {
+ return new ControlledHash[size];
+ }
+ };
+ }
+
+ private static boolean compare(Object v1, Object v2) {
+ if (v1 == null) {
+ return v2 == null;
+ }
+ if (v2 == null) {
+ return false;
+ }
+ return v1.equals(v2);
+ }
+
+ private static void compareMaps(HashMap map, ArrayMap array) {
+ if (map.size() != array.size()) {
+ fail("Bad size: expected " + map.size() + ", got " + array.size());
+ }
+
+ Set<Entry> mapSet = map.entrySet();
+ for (Map.Entry entry : mapSet) {
+ Object expValue = entry.getValue();
+ Object gotValue = array.get(entry.getKey());
+ if (!compare(expValue, gotValue)) {
+ fail("Bad value: expected " + expValue + ", got " + gotValue
+ + " at key " + entry.getKey());
+ }
+ }
+
+ for (int i = 0; i < array.size(); i++) {
+ Object gotValue = array.valueAt(i);
+ Object key = array.keyAt(i);
+ Object expValue = map.get(key);
+ if (!compare(expValue, gotValue)) {
+ fail("Bad value: expected " + expValue + ", got " + gotValue
+ + " at key " + key);
+ }
+ }
+
+ if (map.entrySet().hashCode() != array.entrySet().hashCode()) {
+ fail("Entry set hash codes differ: map=0x"
+ + Integer.toHexString(map.entrySet().hashCode()) + " array=0x"
+ + Integer.toHexString(array.entrySet().hashCode()));
+ }
+
+ if (!map.entrySet().equals(array.entrySet())) {
+ fail("Failed calling equals on map entry set against array set");
+ }
+
+ if (!array.entrySet().equals(map.entrySet())) {
+ fail("Failed calling equals on array entry set against map set");
+ }
+
+ if (map.keySet().hashCode() != array.keySet().hashCode()) {
+ fail("Key set hash codes differ: map=0x"
+ + Integer.toHexString(map.keySet().hashCode()) + " array=0x"
+ + Integer.toHexString(array.keySet().hashCode()));
+ }
+
+ if (!map.keySet().equals(array.keySet())) {
+ fail("Failed calling equals on map key set against array set");
+ }
+
+ if (!array.keySet().equals(map.keySet())) {
+ fail("Failed calling equals on array key set against map set");
+ }
+
+ if (!map.keySet().containsAll(array.keySet())) {
+ fail("Failed map key set contains all of array key set");
+ }
+
+ if (!array.keySet().containsAll(map.keySet())) {
+ fail("Failed array key set contains all of map key set");
+ }
+
+ if (!array.containsAll(map.keySet())) {
+ fail("Failed array contains all of map key set");
+ }
+
+ if (!map.entrySet().containsAll(array.entrySet())) {
+ fail("Failed map entry set contains all of array entry set");
+ }
+
+ if (!array.entrySet().containsAll(map.entrySet())) {
+ fail("Failed array entry set contains all of map entry set");
+ }
+ }
+
+ private static void validateArrayMap(ArrayMap array) {
+ Set<Map.Entry> entrySet = array.entrySet();
+ int index = 0;
+ Iterator<Entry> entryIt = entrySet.iterator();
+ while (entryIt.hasNext()) {
+ Map.Entry entry = entryIt.next();
+ Object value = entry.getKey();
+ Object realValue = array.keyAt(index);
+ if (!compare(realValue, value)) {
+ fail("Bad array map entry set: expected key " + realValue
+ + ", got " + value + " at index " + index);
+ }
+ value = entry.getValue();
+ realValue = array.valueAt(index);
+ if (!compare(realValue, value)) {
+ fail("Bad array map entry set: expected value " + realValue
+ + ", got " + value + " at index " + index);
+ }
+ index++;
+ }
+
+ index = 0;
+ Set keySet = array.keySet();
+ Iterator keyIt = keySet.iterator();
+ while (keyIt.hasNext()) {
+ Object value = keyIt.next();
+ Object realValue = array.keyAt(index);
+ if (!compare(realValue, value)) {
+ fail("Bad array map key set: expected key " + realValue
+ + ", got " + value + " at index " + index);
+ }
+ index++;
+ }
+
+ index = 0;
+ Collection valueCol = array.values();
+ Iterator valueIt = valueCol.iterator();
+ while (valueIt.hasNext()) {
+ Object value = valueIt.next();
+ Object realValue = array.valueAt(index);
+ if (!compare(realValue, value)) {
+ fail("Bad array map value col: expected value " + realValue
+ + ", got " + value + " at index " + index);
+ }
+ index++;
+ }
+ }
+
+ private static void compareBundles(Bundle bundle1, Bundle bundle2) {
+ Set<String> keySet1 = bundle1.keySet();
+ Iterator<String> iterator1 = keySet1.iterator();
+ while (iterator1.hasNext()) {
+ String key = iterator1.next();
+ int value1 = bundle1.getInt(key);
+ if (bundle2.get(key) == null) {
+ fail("Bad Bundle: bundle2 didn't have expected key " + key);
+ }
+ int value2 = bundle2.getInt(key);
+ if (value1 != value2) {
+ fail("Bad Bundle: at key key " + key + " expected " + value1 + ", got " + value2);
+ }
+ }
+ Set<String> keySet2 = bundle2.keySet();
+ Iterator<String> iterator2 = keySet2.iterator();
+ while (iterator2.hasNext()) {
+ String key = iterator2.next();
+ if (bundle1.get(key) == null) {
+ fail("Bad Bundle: bundle1 didn't have expected key " + key);
+ }
+ int value1 = bundle1.getInt(key);
+ int value2 = bundle2.getInt(key);
+ if (value1 != value2) {
+ fail("Bad Bundle: at key key " + key + " expected " + value1 + ", got " + value2);
+ }
+ }
+ }
+
+ private static void dump(Map map, ArrayMap array) {
+ Log.e("test", "HashMap of " + map.size() + " entries:");
+ Set<Map.Entry> mapSet = map.entrySet();
+ for (Map.Entry entry : mapSet) {
+ Log.e("test", " " + entry.getKey() + " -> " + entry.getValue());
+ }
+ Log.e("test", "ArrayMap of " + array.size() + " entries:");
+ for (int i = 0; i < array.size(); i++) {
+ Log.e("test", " " + array.keyAt(i) + " -> " + array.valueAt(i));
+ }
+ }
+
+ private static void dump(ArrayMap map1, ArrayMap map2) {
+ Log.e("test", "ArrayMap of " + map1.size() + " entries:");
+ for (int i = 0; i < map1.size(); i++) {
+ Log.e("test", " " + map1.keyAt(i) + " -> " + map1.valueAt(i));
+ }
+ Log.e("test", "ArrayMap of " + map2.size() + " entries:");
+ for (int i = 0; i < map2.size(); i++) {
+ Log.e("test", " " + map2.keyAt(i) + " -> " + map2.valueAt(i));
+ }
+ }
+
+ private static void dump(Bundle bundle1, Bundle bundle2) {
+ Log.e("test", "First Bundle of " + bundle1.size() + " entries:");
+ Set<String> keys1 = bundle1.keySet();
+ for (String key : keys1) {
+ Log.e("test", " " + key + " -> " + bundle1.get(key));
+ }
+ Log.e("test", "Second Bundle of " + bundle2.size() + " entries:");
+ Set<String> keys2 = bundle2.keySet();
+ for (String key : keys2) {
+ Log.e("test", " " + key + " -> " + bundle2.get(key));
+ }
+ }
+
+ @Test
+ public void testBasicArrayMap() {
+ HashMap<ControlledHash, Integer> hashMap = new HashMap<>();
+ ArrayMap<ControlledHash, Integer> arrayMap = new ArrayMap<>();
+ Bundle bundle = new Bundle();
+
+ for (int i = 0; i < OPS.length; i++) {
+ Integer oldHash;
+ Integer oldArray;
+ ControlledHash key = KEYS[i] < 0 ? null : new ControlledHash(KEYS[i]);
+ String strKey = KEYS[i] < 0 ? null : Integer.toString(KEYS[i]);
+ switch (OPS[i]) {
+ case OP_ADD:
+ if (DEBUG) Log.i("test", "Adding key: " + key);
+ oldHash = hashMap.put(key, i);
+ oldArray = arrayMap.put(key, i);
+ bundle.putInt(strKey, i);
+ break;
+ case OP_REM:
+ if (DEBUG) Log.i("test", "Removing key: " + key);
+ oldHash = hashMap.remove(key);
+ oldArray = arrayMap.remove(key);
+ bundle.remove(strKey);
+ break;
+ default:
+ fail("Bad operation " + OPS[i] + " @ " + i);
+ return;
+ }
+ if (!compare(oldHash, oldArray)) {
+ String msg = "Bad result: expected " + oldHash + ", got " + oldArray;
+ Log.e("test", msg);
+ dump(hashMap, arrayMap);
+ fail(msg);
+ }
+ try {
+ validateArrayMap(arrayMap);
+ } catch (Throwable e) {
+ Log.e("test", e.getMessage());
+ dump(hashMap, arrayMap);
+ throw e;
+ }
+ try {
+ compareMaps(hashMap, arrayMap);
+ } catch (Throwable e) {
+ Log.e("test", e.getMessage());
+ dump(hashMap, arrayMap);
+ throw e;
+ }
+ Parcel parcel = Parcel.obtain();
+ bundle.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ Bundle bundle2 = parcel.readBundle();
+ try {
+ compareBundles(bundle, bundle2);
+ } catch (Throwable e) {
+ Log.e("test", e.getMessage());
+ dump(bundle, bundle2);
+ throw e;
+ }
+ }
+
+ arrayMap.put(new ControlledHash(50000), 100);
+ ControlledHash lookup = new ControlledHash(50000);
+ Iterator<ControlledHash> it = arrayMap.keySet().iterator();
+ while (it.hasNext()) {
+ if (it.next().equals(lookup)) {
+ it.remove();
+ }
+ }
+ if (arrayMap.containsKey(lookup)) {
+ String msg = "Bad map iterator: didn't remove test key";
+ Log.e("test", msg);
+ dump(hashMap, arrayMap);
+ fail(msg);
+ }
+ }
+
+ @Test
+ public void testCopyArrayMap() {
+ // map copy constructor test
+ ArrayMap newMap = new ArrayMap<Integer, String>();
+ for (int i = 0; i < 10; ++i) {
+ newMap.put(i, String.valueOf(i));
+ }
+ ArrayMap mapCopy = new ArrayMap(newMap);
+ if (!compare(mapCopy, newMap)) {
+ String msg = "ArrayMap copy constructor failure: expected " +
+ newMap + ", got " + mapCopy;
+ Log.e("test", msg);
+ dump(newMap, mapCopy);
+ fail(msg);
+ return;
+ }
+ }
+
+ @Test
+ public void testEqualsArrayMap() {
+ ArrayMap<Integer, String> map1 = new ArrayMap<>();
+ ArrayMap<Integer, String> map2 = new ArrayMap<>();
+ HashMap<Integer, String> map3 = new HashMap<>();
+ if (!compare(map1, map2) || !compare(map1, map3) || !compare(map3, map2)) {
+ fail("ArrayMap equals failure for empty maps " + map1 + ", " +
+ map2 + ", " + map3);
+ }
+
+ for (int i = 0; i < 10; ++i) {
+ String value = String.valueOf(i);
+ map1.put(i, value);
+ map2.put(i, value);
+ map3.put(i, value);
+ }
+ if (!compare(map1, map2) || !compare(map1, map3) || !compare(map3, map2)) {
+ fail("ArrayMap equals failure for populated maps " + map1 + ", " +
+ map2 + ", " + map3);
+ }
+
+ map1.remove(0);
+ if (compare(map1, map2) || compare(map1, map3) || compare(map3, map1)) {
+ fail("ArrayMap equals failure for map size " + map1 + ", " +
+ map2 + ", " + map3);
+ }
+
+ map1.put(0, "-1");
+ if (compare(map1, map2) || compare(map1, map3) || compare(map3, map1)) {
+ fail("ArrayMap equals failure for map contents " + map1 + ", " +
+ map2 + ", " + map3);
+ }
+ }
+
+// /**
+// * Test creating a malformed array map with duplicated keys and that we will catch this when
+// * unparcelling.
+// */
+// @Test
+// public void testDuplicateKeys() throws NoSuchMethodException,
+// InvocationTargetException, IllegalAccessException, NoSuchFieldException {
+// ArrayMap<String, Object> map1 = new ArrayMap(2);
+//
+// Method appendMethod = ArrayMap.class.getMethod("append", Object.class, Object.class);
+// appendMethod.invoke(map1, Integer.toString(100000), "foo");
+// appendMethod.invoke(map1, Integer.toString(100000), "bar");
+//
+// // Now parcel/unparcel, and verify we get the expected error.
+// Parcel parcel = Parcel.obtain();
+// Method writeArrayMapMethod = Parcel.class.getMethod("writeArrayMap", ArrayMap.class);
+// writeArrayMapMethod.invoke(parcel, map1);
+// parcel.setDataPosition(0);
+// ArrayMap<String, Object> map2 = new ArrayMap(2);
+//
+// try {
+// Parcel.class.getMethod("readArrayMap", ArrayMap.class, ClassLoader.class).invoke(
+// parcel, map2, null);
+// } catch (InvocationTargetException e) {
+// Throwable cause = e.getCause();
+// if (cause instanceof IllegalArgumentException) {
+// // Good!
+// return;
+// }
+// throw e;
+// }
+//
+// String msg = "Didn't throw expected IllegalArgumentException";
+// Log.e("test", msg);
+// dump(map1, map2);
+// fail(msg);
+// }
+
+ private static void checkEntrySetToArray(ArrayMap<?, ?> testMap) {
+ try {
+ testMap.entrySet().toArray();
+ fail();
+ } catch (UnsupportedOperationException expected) {
+ }
+
+ try {
+ Map.Entry<?, ?>[] entries = new Map.Entry[20];
+ testMap.entrySet().toArray(entries);
+ fail();
+ } catch (UnsupportedOperationException expected) {
+ }
+ }
+
+ // http://b/32294038, Test ArrayMap.entrySet().toArray()
+ @Test
+ public void testEntrySetArray() {
+ // Create
+ ArrayMap<Integer, String> testMap = new ArrayMap<>();
+
+ // Test empty
+ checkEntrySetToArray(testMap);
+
+ // Test non-empty
+ for (int i = 0; i < 10; ++i) {
+ testMap.put(i, String.valueOf(i));
+ }
+ checkEntrySetToArray(testMap);
+ }
+
+ @Test
+ public void testCanNotIteratePastEnd_entrySetIterator() {
+ Map<String, String> map = new ArrayMap<>();
+ map.put("key 1", "value 1");
+ map.put("key 2", "value 2");
+ Set<Map.Entry<String, String>> expectedEntriesToIterate = new HashSet<>(Arrays.asList(
+ entryOf("key 1", "value 1"),
+ entryOf("key 2", "value 2")
+ ));
+ Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
+
+ // Assert iteration over the expected two entries in any order
+ assertTrue(iterator.hasNext());
+ Map.Entry<String, String> firstEntry = copyOf(iterator.next());
+ assertTrue(expectedEntriesToIterate.remove(firstEntry));
+
+ assertTrue(iterator.hasNext());
+ Map.Entry<String, String> secondEntry = copyOf(iterator.next());
+ assertTrue(expectedEntriesToIterate.remove(secondEntry));
+
+ assertFalse(iterator.hasNext());
+
+ try {
+ iterator.next();
+ fail();
+ } catch (NoSuchElementException expected) {
+ }
+ }
+
+ private static <K, V> Map.Entry<K, V> entryOf(K key, V value) {
+ return new AbstractMap.SimpleEntry<>(key, value);
+ }
+
+ private static <K, V> Map.Entry<K, V> copyOf(Map.Entry<K, V> entry) {
+ return entryOf(entry.getKey(), entry.getValue());
+ }
+
+ @Test
+ public void testCanNotIteratePastEnd_keySetIterator() {
+ Map<String, String> map = new ArrayMap<>();
+ map.put("key 1", "value 1");
+ map.put("key 2", "value 2");
+ Set<String> expectedKeysToIterate = new HashSet<>(Arrays.asList("key 1", "key 2"));
+ Iterator<String> iterator = map.keySet().iterator();
+
+ // Assert iteration over the expected two keys in any order
+ assertTrue(iterator.hasNext());
+ String firstKey = iterator.next();
+ assertTrue(expectedKeysToIterate.remove(firstKey));
+
+ assertTrue(iterator.hasNext());
+ String secondKey = iterator.next();
+ assertTrue(expectedKeysToIterate.remove(secondKey));
+
+ assertFalse(iterator.hasNext());
+
+ try {
+ iterator.next();
+ fail();
+ } catch (NoSuchElementException expected) {
+ }
+ }
+
+ @Test
+ public void testCanNotIteratePastEnd_valuesIterator() {
+ Map<String, String> map = new ArrayMap<>();
+ map.put("key 1", "value 1");
+ map.put("key 2", "value 2");
+ Set<String> expectedValuesToIterate = new HashSet<>(Arrays.asList("value 1", "value 2"));
+ Iterator<String> iterator = map.values().iterator();
+
+ // Assert iteration over the expected two values in any order
+ assertTrue(iterator.hasNext());
+ String firstValue = iterator.next();
+ assertTrue(expectedValuesToIterate.remove(firstValue));
+
+ assertTrue(iterator.hasNext());
+ String secondValue = iterator.next();
+ assertTrue(expectedValuesToIterate.remove(secondValue));
+
+ assertFalse(iterator.hasNext());
+
+ try {
+ iterator.next();
+ fail();
+ } catch (NoSuchElementException expected) {
+ }
+ }
+
+ @Test
+ public void testForEach() {
+ ArrayMap<String, Integer> map = new ArrayMap<>();
+
+ for (int i = 0; i < 50; ++i) {
+ map.put(Integer.toString(i), i * 10);
+ }
+
+ // Make sure forEach goes through all of the elements.
+ HashMap<String, Integer> seen = new HashMap<>();
+ map.forEach(seen::put);
+ compareMaps(seen, map);
+ }
+
+ /**
+ * The entrySet Iterator returns itself from each call to {@code next()}. This is unusual
+ * behavior for {@link Iterator#next()}; this test ensures that any future change to this
+ * behavior is deliberate.
+ */
+ @Test
+ public void testUnusualBehavior_eachEntryIsSameAsIterator_entrySetIterator() {
+ Map<String, String> map = new ArrayMap<>();
+ map.put("key 1", "value 1");
+ map.put("key 2", "value 2");
+ Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
+
+ assertSame(iterator, iterator.next());
+ assertSame(iterator, iterator.next());
+ }
+
+ @SuppressWarnings("SelfEquals")
+ @Test
+ public void testUnusualBehavior_equalsThrowsAfterRemove_entrySetIterator() {
+ Map<String, String> map = new ArrayMap<>();
+ map.put("key 1", "value 1");
+ map.put("key 2", "value 2");
+ Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
+ iterator.next();
+ iterator.remove();
+ try {
+ iterator.equals(iterator);
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ private static <T> void assertEqualsBothWays(T a, T b) {
+ assertEquals(a, b);
+ assertEquals(b, a);
+ assertEquals(a.hashCode(), b.hashCode());
+ }
+
+ @Test
+ public void testRemoveAll() {
+ final ArrayMap<Integer, String> map = new ArrayMap<>();
+ for (Integer i : Arrays.asList(0, 1, 2, 3, 4, 5)) {
+ map.put(i, i.toString());
+ }
+
+ final ArrayMap<Integer, String> expectedMap = new ArrayMap<>();
+ for (Integer i : Arrays.asList(2, 4)) {
+ expectedMap.put(i, String.valueOf(i));
+ }
+ map.removeAll(Arrays.asList(0, 1, 3, 5, 6));
+ if (!compare(map, expectedMap)) {
+ fail("ArrayMap removeAll failure, expect " + expectedMap + ", but " + map);
+ }
+
+ map.removeAll(Collections.emptyList());
+ if (!compare(map, expectedMap)) {
+ fail("ArrayMap removeAll failure for empty maps, expect " + expectedMap + ", but " +
+ map);
+ }
+
+ map.removeAll(Arrays.asList(2, 4));
+ if (!map.isEmpty()) {
+ fail("ArrayMap removeAll failure, expect empty, but " + map);
+ }
+ }
+
+ @Test
+ public void testReplaceAll() {
+ final ArrayMap<Integer, Integer> map = new ArrayMap<>();
+ final ArrayMap<Integer, Integer> expectedMap = new ArrayMap<>();
+ final BiFunction<Integer, Integer, Integer> function = (k, v) -> 2 * v;
+ for (Integer i : Arrays.asList(0, 1, 2, 3, 4, 5)) {
+ map.put(i, i);
+ expectedMap.put(i, 2 * i);
+ }
+
+ map.replaceAll(function);
+ if (!compare(map, expectedMap)) {
+ fail("ArrayMap replaceAll failure, expect " + expectedMap + ", but " + map);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/test-framework/src/com/android/hoststubgen/frameworktest/LogTest.java b/tools/hoststubgen/hoststubgen/test-framework/src/com/android/hoststubgen/frameworktest/LogTest.java
new file mode 100644
index 0000000..56544b4
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-framework/src/com/android/hoststubgen/frameworktest/LogTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.frameworktest;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.util.Log;
+import android.util.Slog;
+
+import org.junit.Test;
+
+/**
+ * Some basic tests for {@link android.util.Log}.
+ */
+public class LogTest {
+ @Test
+ public void testBasicLogging() {
+ Log.v("TAG", "Test v log");
+ Log.d("TAG", "Test d log");
+ Log.i("TAG", "Test i log");
+ Log.w("TAG", "Test w log");
+ Log.e("TAG", "Test e log");
+
+ Slog.v("TAG", "Test v slog");
+ Slog.d("TAG", "Test d slog");
+ Slog.i("TAG", "Test i slog");
+ Slog.w("TAG", "Test w slog");
+ Slog.e("TAG", "Test e slog");
+ }
+
+ @Test
+ public void testNativeMethods() {
+ assertThat(Log.isLoggable("mytag", Log.INFO)).isTrue();
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
new file mode 100644
index 0000000..8c76a61
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
@@ -0,0 +1,141 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+// A library that simulates framework-all.jar
+java_library {
+ name: "hoststubgen-test-tiny-framework",
+ installable: true,
+ host_supported: true,
+ srcs: ["tiny-framework/src/**/*.java"],
+ static_libs: [
+ "hoststubgen-annotations",
+ ],
+}
+
+// Create stub/impl jars from "hoststubgen-test-tiny-framework", using the following 3 rules.
+java_genrule_host {
+ name: "hoststubgen-test-tiny-framework-host",
+ defaults: ["hoststubgen-command-defaults"],
+ cmd: hoststubgen_common_options +
+ "--in-jar $(location :hoststubgen-test-tiny-framework) " +
+ "--policy-override-file $(location policy-override-tiny-framework.txt) ",
+ srcs: [
+ ":hoststubgen-test-tiny-framework",
+ "policy-override-tiny-framework.txt",
+ ],
+}
+
+java_genrule_host {
+ name: "hoststubgen-test-tiny-framework-host-stub",
+ cmd: "cp $(in) $(out)",
+ srcs: [
+ ":hoststubgen-test-tiny-framework-host{host_stub.jar}",
+ ],
+ out: [
+ "host_stub.jar",
+ ],
+}
+
+java_genrule_host {
+ name: "hoststubgen-test-tiny-framework-host-impl",
+ cmd: "cp $(in) $(out)",
+ srcs: [
+ ":hoststubgen-test-tiny-framework-host{host_impl.jar}",
+ ],
+ out: [
+ "host_impl.jar",
+ ],
+}
+
+// Compile the test jar, using 2 rules.
+// 1. Build the test against the stub.
+java_library_host {
+ name: "hoststubgen-test-tiny-test-lib",
+ srcs: ["tiny-test/src/**/*.java"],
+
+ libs: [
+ "hoststubgen-test-tiny-framework-host-stub",
+ ],
+ static_libs: [
+ "junit",
+ "truth-prebuilt",
+
+ // http://cs/h/googleplex-android/platform/superproject/main/+/main:platform_testing/libraries/annotations/src/android/platform/test/annotations/
+ "platform-test-annotations",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+// 2. Link "hoststubgen-test-tiny-test-lib" with necessary runtime dependencies, so it can be
+// executed stand-alone.
+java_test_host {
+ name: "hoststubgen-test-tiny-test",
+ test_config: "AndroidTest-host.xml",
+ static_libs: [
+ "hoststubgen-test-tiny-test-lib",
+ "hoststubgen-helper-runtime",
+ "hoststubgen-test-tiny-framework-host-impl",
+ ],
+ test_suites: ["general-tests"],
+}
+
+// Dump the original, stub and impl jars as text files.
+// We use them in test-and-update-golden.sh.
+java_genrule_host {
+ name: "hoststubgen-test-tiny-framework-orig-dump",
+ defaults: ["hoststubgen-jar-dump-defaults"],
+ srcs: [
+ ":hoststubgen-test-tiny-framework",
+ ],
+ out: [
+ "01-hoststubgen-test-tiny-framework-orig-dump.txt",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+java_genrule_host {
+ name: "hoststubgen-test-tiny-framework-host-stub-dump",
+ defaults: ["hoststubgen-jar-dump-defaults"],
+ srcs: [
+ ":hoststubgen-test-tiny-framework-host-stub",
+ ],
+ out: [
+ "02-hoststubgen-test-tiny-framework-host-stub-dump.txt",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+java_genrule_host {
+ name: "hoststubgen-test-tiny-framework-host-impl-dump",
+ defaults: ["hoststubgen-jar-dump-defaults"],
+ srcs: [
+ ":hoststubgen-test-tiny-framework-host-impl",
+ ],
+ out: [
+ "03-hoststubgen-test-tiny-framework-host-impl-dump.txt",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+// Run it with `atest`. Compare the dump of the jar files to the golden output.
+python_test_host {
+ name: "tiny-framework-dump-test",
+ srcs: [
+ "tiny-framework-dump-test.py",
+ ],
+ data: [
+ "golden-output/*.txt",
+ ],
+ java_data: [
+ "hoststubgen-test-tiny-framework-host-stub-dump",
+ "hoststubgen-test-tiny-framework-host-impl-dump",
+ "hoststubgen-test-tiny-framework-orig-dump",
+ ],
+ test_suites: ["general-tests"],
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/AndroidTest-host.xml b/tools/hoststubgen/hoststubgen/test-tiny-framework/AndroidTest-host.xml
new file mode 100644
index 0000000..84aad69
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/AndroidTest-host.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 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.
+-->
+
+<!-- [Ravenwood] Copied from $ANDROID_BUILD_TOP/cts/hostsidetests/devicepolicy/AndroidTest.xml -->
+<configuration description="HostStubGen sample test">
+ <option name="test-suite-tag" value="ravenwood" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
+
+ <test class="com.android.tradefed.testtype.IsolatedHostTest" >
+ <option name="jar" value="hoststubgen-test-tiny-test.jar" />
+ </test>
+</configuration>
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md b/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md
new file mode 100644
index 0000000..f3c0450
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md
@@ -0,0 +1,27 @@
+# HostStubGen: tiny-framework test
+
+This directory contains a small classes that "simulates" framework.jar, and tests against it.
+
+This test doesn't use the actual android framework code.
+
+## How to run
+
+- With `atest`. This is the proper way to run it, but `atest` has known problems that may
+ affect the result. If you see weird problems, try the next `run-ravenwood-test` command.
+
+```
+$ atest hoststubgen-test-tiny-test
+```
+
+- With `run-ravenwood-test` should work too. This is the proper way to run it.
+
+```
+$ run-ravenwood-test hoststubgen-test-tiny-test
+```
+
+- `run-test-manually.sh` also run the test, but it builds the stub/impl jars and the test without
+ using the build system. This is useful for debugging the tool.
+
+```
+$ ./run-test-manually.sh
+```
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh b/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
new file mode 100755
index 0000000..4d58869
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
@@ -0,0 +1,134 @@
+#!/bin/bash
+# Copyright (C) 2023 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.
+
+help() {
+ cat <<'EOF'
+
+ diff-and-update-golden.sh [OPTIONS]
+
+ Compare the generated jar files from tiny-framework to the "golden" files.
+
+ OPTIONS:
+ -u: Update the golden files.
+
+ -3: Run `meld` to compare original, stub and impl jar files in 3-way diff.
+ This is useful to visualize the exact differences between 3 jar files.
+
+ -2: Run `meld` to compare original <-> impl, and impl <-> stub as two different diffs.
+EOF
+}
+
+source "${0%/*}"/../../common.sh
+
+SCRIPT_NAME="${0##*/}"
+
+GOLDEN_DIR=golden-output
+mkdir -p $GOLDEN_DIR
+
+DIFF_CMD=${DIFF:-diff -u --ignore-blank-lines --ignore-space-change}
+
+update=0
+three_way=0
+two_way=0
+while getopts "u32" opt; do
+case "$opt" in
+ u)
+ update=1
+ ;;
+ 3)
+ three_way=1
+ ;;
+ 2)
+ two_way=1
+ ;;
+ '?')
+ help
+ exit 1
+ ;;
+esac
+done
+shift $(($OPTIND - 1))
+
+
+# Build the dump files, which are the input of this test.
+run m tiny-framework-dump-test
+
+
+# Get the path to the generate text files. (not the golden files.)
+# We get them from $OUT/module-info.json
+
+files=(
+$(python3 -c '
+import sys
+import os
+import json
+
+with open(sys.argv[1], "r") as f:
+ data = json.load(f)
+
+ # Equivalent to: jq -r '.["tiny-framework-dump-test"]["installed"][]'
+ for path in data["tiny-framework-dump-test"]["installed"]:
+
+ if "golden-output" in path:
+ continue
+ if path.endswith(".txt"):
+ print(os.getenv("ANDROID_BUILD_TOP") + "/" + path)
+' $OUT/module-info.json)
+)
+
+# Next, compare each file and update them in $GOLDEN_DIR
+
+any_file_changed=0
+
+for file in ${files[*]} ; do
+ name=$(basename $file)
+ echo "# Checking $name ..."
+
+ file_changed=0
+ if run $DIFF_CMD $GOLDEN_DIR/$name $file; then
+ : # No diff
+ else
+ file_changed=1
+ any_file_changed=1
+ fi
+
+ if (( $update && $file_changed )) ; then
+ echo "# Updating $name ..."
+ run cp $file $GOLDEN_DIR/$name
+ fi
+done
+
+if (( $three_way )) ; then
+ echo "# Running 3-way diff with meld..."
+ run meld ${files[*]} &
+fi
+
+if (( $two_way )) ; then
+ echo "# Running meld..."
+ run meld --diff ${files[0]} ${files[1]} --diff ${files[1]} ${files[2]}
+fi
+
+if (( $any_file_changed == 0 )) ; then
+ echo "$SCRIPT_NAME: Success: no changes detected."
+ exit 0
+else
+ if (( $update )) ; then
+ echo "$SCRIPT_NAME: Warning: golden files have been updated."
+ exit 2
+ else
+ echo "$SCRIPT_NAME: Failure: changes detected. See above diff for the details."
+ exit 3
+ fi
+fi
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
new file mode 100644
index 0000000..1aa4859
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -0,0 +1,1671 @@
+## Class: android/hosttest/annotation/HostSideTestClassLoadHook.class
+ Compiled from "HostSideTestClassLoadHook.java"
+public interface android.hosttest.annotation.HostSideTestClassLoadHook extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestClassLoadHook
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ public abstract java.lang.String value();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
+}
+SourceFile: "HostSideTestClassLoadHook.java"
+RuntimeVisibleAnnotations:
+ 0: #x(#x=[e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE]
+ )
+ 1: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestKeep.class
+ Compiled from "HostSideTestKeep.java"
+public interface android.hosttest.annotation.HostSideTestKeep extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestKeep
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "HostSideTestKeep.java"
+RuntimeVisibleAnnotations:
+ 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
+ )
+ 1: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestNativeSubstitutionClass.class
+ Compiled from "HostSideTestNativeSubstitutionClass.java"
+public interface android.hosttest.annotation.HostSideTestNativeSubstitutionClass extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestNativeSubstitutionClass
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ public abstract java.lang.String value();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
+}
+SourceFile: "HostSideTestNativeSubstitutionClass.java"
+RuntimeVisibleAnnotations:
+ 0: #x(#x=[e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE]
+ )
+ 1: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestRemove.class
+ Compiled from "HostSideTestRemove.java"
+public interface android.hosttest.annotation.HostSideTestRemove extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestRemove
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "HostSideTestRemove.java"
+RuntimeVisibleAnnotations:
+ 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
+ )
+ 1: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestStub.class
+ Compiled from "HostSideTestStub.java"
+public interface android.hosttest.annotation.HostSideTestStub extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestStub
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "HostSideTestStub.java"
+RuntimeVisibleAnnotations:
+ 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
+ )
+ 1: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestSubstitute.class
+ Compiled from "HostSideTestSubstitute.java"
+public interface android.hosttest.annotation.HostSideTestSubstitute extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestSubstitute
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ public abstract java.lang.String suffix();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
+}
+SourceFile: "HostSideTestSubstitute.java"
+RuntimeVisibleAnnotations:
+ 0: #x(#x=[e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.METHOD]
+ )
+ 1: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestThrow.class
+ Compiled from "HostSideTestThrow.java"
+public interface android.hosttest.annotation.HostSideTestThrow extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestThrow
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "HostSideTestThrow.java"
+RuntimeVisibleAnnotations:
+ 0: #x(#x=[e#x.#x,e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
+ )
+ 1: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestWholeClassKeep.class
+ Compiled from "HostSideTestWholeClassKeep.java"
+public interface android.hosttest.annotation.HostSideTestWholeClassKeep extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestWholeClassKeep
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "HostSideTestWholeClassKeep.java"
+RuntimeVisibleAnnotations:
+ 0: #x(#x=[e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE]
+ )
+ 1: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestWholeClassStub.class
+ Compiled from "HostSideTestWholeClassStub.java"
+public interface android.hosttest.annotation.HostSideTestWholeClassStub extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestWholeClassStub
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "HostSideTestWholeClassStub.java"
+RuntimeVisibleAnnotations:
+ 0: #x(#x=[e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE]
+ )
+ 1: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/tests/HostSideTestSuppress.class
+ Compiled from "HostSideTestSuppress.java"
+public interface android.hosttest.annotation.tests.HostSideTestSuppress extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/tests/HostSideTestSuppress
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "HostSideTestSuppress.java"
+RuntimeVisibleAnnotations:
+ 0: #x(#x=[e#x.#x,e#x.#x,e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD]
+ )
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
+ Compiled from "TinyFrameworkCallerCheck.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 3, attributes: 3
+ private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl();
+ descriptor: ()V
+ flags: (0x0002) ACC_PRIVATE
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl;
+
+ public static int getOneKeep();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ 0: iconst_1
+ 1: ireturn
+ LineNumberTable:
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestKeep
+
+ public static int getOneStub();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ 0: iconst_1
+ 1: ireturn
+ LineNumberTable:
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestStub
+}
+SourceFile: "TinyFrameworkCallerCheck.java"
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+InnerClasses:
+ private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class
+ Compiled from "TinyFrameworkCallerCheck.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 3, attributes: 4
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck;
+
+ public static int getOne_withCheck();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
+ 3: ireturn
+ LineNumberTable:
+
+ public static int getOne_noCheck();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
+ 3: ireturn
+ LineNumberTable:
+}
+SourceFile: "TinyFrameworkCallerCheck.java"
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+InnerClasses:
+ private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class
+ Compiled from "TinyFrameworkClassAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 3, methods: 10, attributes: 2
+ public int stub;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public int keep;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestKeep
+
+ public int remove;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: aload_0
+ 5: iconst_1
+ 6: putfield #x // Field stub:I
+ 9: aload_0
+ 10: iconst_2
+ 11: putfield #x // Field keep:I
+ 14: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public int addOne(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: aload_0
+ 1: iload_1
+ 2: invokevirtual #x // Method addOneInner:(I)I
+ 5: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ 0 6 1 value I
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public int addOneInner(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: iload_1
+ 1: iconst_1
+ 2: iadd
+ 3: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ 0 4 1 value I
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestKeep
+
+ public void toBeRemoved(java.lang.String);
+ descriptor: (Ljava/lang/String;)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
+ 7: athrow
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ 0 8 1 foo Ljava/lang/String;
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestRemove
+
+ public int addTwo(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String not supported on host side
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ 0 10 1 value I
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestStub
+ 1: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestSubstitute(
+ suffix="_host"
+ )
+
+ public int addTwo_host(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: iload_1
+ 1: iconst_2
+ 2: iadd
+ 3: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ 0 4 1 value I
+
+ public static native int nativeAddThree(int);
+ descriptor: (I)I
+ flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestStub
+ 1: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestSubstitute(
+ suffix="_host"
+ )
+
+ public static int nativeAddThree_host(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=1, args_size=1
+ 0: iload_0
+ 1: iconst_3
+ 2: iadd
+ 3: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 4 0 value I
+
+ public java.lang.String unsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: ldc #x // String This value shouldn\'t be seen on the host side.
+ 2: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestThrow
+
+ public java.lang.String visibleButUsesUnsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ 4: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestStub
+}
+SourceFile: "TinyFrameworkClassAnnotations.java"
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestStub
+ 1: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestClassLoadHook(
+ value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
+ )
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class
+ Compiled from "TinyFrameworkClassClassWideAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 3, methods: 10, attributes: 2
+ public int stub;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public int keep;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public int remove;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: aload_0
+ 5: iconst_1
+ 6: putfield #x // Field stub:I
+ 9: aload_0
+ 10: iconst_2
+ 11: putfield #x // Field keep:I
+ 14: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+
+ public int addOne(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: aload_0
+ 1: iload_1
+ 2: invokevirtual #x // Method addOneInner:(I)I
+ 5: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+ 0 6 1 value I
+
+ public int addOneInner(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: iload_1
+ 1: iconst_1
+ 2: iadd
+ 3: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+ 0 4 1 value I
+
+ public void toBeRemoved(java.lang.String);
+ descriptor: (Ljava/lang/String;)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
+ 7: athrow
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+ 0 8 1 foo Ljava/lang/String;
+
+ public int addTwo(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String not supported on host side
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+ 0 10 1 value I
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestStub
+ 1: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestSubstitute(
+ suffix="_host"
+ )
+
+ public int addTwo_host(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: iload_1
+ 1: iconst_2
+ 2: iadd
+ 3: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+ 0 4 1 value I
+
+ public static native int nativeAddThree(int);
+ descriptor: (I)I
+ flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestStub
+ 1: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestSubstitute(
+ suffix="_host"
+ )
+
+ public static int nativeAddThree_host(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=1, args_size=1
+ 0: iload_0
+ 1: iconst_3
+ 2: iadd
+ 3: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 4 0 value I
+
+ public java.lang.String unsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: ldc #x // String This value shouldn\'t be seen on the host side.
+ 2: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+
+ public java.lang.String visibleButUsesUnsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ 4: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+}
+SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
+ Compiled from "TinyFrameworkClassLoadHook.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 3, attributes: 2
+ public static final java.util.Set<java.lang.Class<?>> sLoadedClasses;
+ descriptor: Ljava/util/Set;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/Set<Ljava/lang/Class<*>;>;
+
+ private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook();
+ descriptor: ()V
+ flags: (0x0002) ACC_PRIVATE
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook;
+
+ public static void onClassLoaded(java.lang.Class<?>);
+ descriptor: (Ljava/lang/Class;)V
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=1, args_size=1
+ 0: getstatic #x // Field sLoadedClasses:Ljava/util/Set;
+ 3: aload_0
+ 4: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
+ 9: pop
+ 10: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 11 0 clazz Ljava/lang/Class;
+ LocalVariableTypeTable:
+ Start Length Slot Name Signature
+ 0 11 0 clazz Ljava/lang/Class<*>;
+ Signature: #x // (Ljava/lang/Class<*>;)V
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ 0: new #x // class java/util/HashSet
+ 3: dup
+ 4: invokespecial #x // Method java/util/HashSet."<init>":()V
+ 7: putstatic #x // Field sLoadedClasses:Ljava/util/Set;
+ 10: return
+ LineNumberTable:
+}
+SourceFile: "TinyFrameworkClassLoadHook.java"
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class
+ Compiled from "TinyFrameworkClassWithInitializer.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 2
+ public static boolean sInitialized;
+ descriptor: Z
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer;
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ 0: iconst_1
+ 1: putstatic #x // Field sInitialized:Z
+ 4: return
+ LineNumberTable:
+}
+SourceFile: "TinyFrameworkClassWithInitializer.java"
+RuntimeInvisibleAnnotations:
+ 0: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestClassLoadHook(
+ value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
+ )
+ 1: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
+ Compiled from "TinyFrameworkExceptionTester.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 2, attributes: 2
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester;
+
+ public static int testException();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=0
+ 0: new #x // class java/lang/IllegalStateException
+ 3: dup
+ 4: ldc #x // String Inner exception
+ 6: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+ 10: astore_0
+ 11: new #x // class java/lang/RuntimeException
+ 14: dup
+ 15: ldc #x // String Outer exception
+ 17: aload_0
+ 18: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V
+ 21: athrow
+ Exception table:
+ from to target type
+ 0 10 10 Class java/lang/Exception
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 11 0 e Ljava/lang/Exception;
+ StackMapTable: number_of_entries = 1
+ frame_type = 74 /* same_locals_1_stack_item */
+ stack = [ class java/lang/Exception ]
+}
+SourceFile: "TinyFrameworkExceptionTester.java"
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
+ Compiled from "TinyFrameworkForTextPolicy.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 3, methods: 10, attributes: 1
+ public int stub;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public int keep;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public int remove;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: aload_0
+ 5: iconst_1
+ 6: putfield #x // Field stub:I
+ 9: aload_0
+ 10: iconst_2
+ 11: putfield #x // Field keep:I
+ 14: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+
+ public int addOne(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: aload_0
+ 1: iload_1
+ 2: invokevirtual #x // Method addOneInner:(I)I
+ 5: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+ 0 6 1 value I
+
+ public int addOneInner(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: iload_1
+ 1: iconst_1
+ 2: iadd
+ 3: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+ 0 4 1 value I
+
+ public void toBeRemoved(java.lang.String);
+ descriptor: (Ljava/lang/String;)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
+ 7: athrow
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+ 0 8 1 foo Ljava/lang/String;
+
+ public int addTwo(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String not supported on host side
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+ 0 10 1 value I
+
+ public int addTwo_host(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: iload_1
+ 1: iconst_2
+ 2: iadd
+ 3: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+ 0 4 1 value I
+
+ public static native int nativeAddThree(int);
+ descriptor: (I)I
+ flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+
+ public static int addThree_host(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=1, args_size=1
+ 0: iload_0
+ 1: iconst_3
+ 2: iadd
+ 3: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 4 0 value I
+
+ public java.lang.String unsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: ldc #x // String This value shouldn\'t be seen on the host side.
+ 2: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+
+ public java.lang.String visibleButUsesUnsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ 4: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+}
+SourceFile: "TinyFrameworkForTextPolicy.java"
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
+ Compiled from "TinyFrameworkNative.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 5, attributes: 2
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+
+ public static native int nativeAddTwo(int);
+ descriptor: (I)I
+ flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+
+ public static int nativeAddTwo_should_be_like_this(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: iload_0
+ 1: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
+ 4: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 arg I
+
+ public static native long nativeLongPlus(long, long);
+ descriptor: (JJ)J
+ flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+
+ public static long nativeLongPlus_should_be_like_this(long, long);
+ descriptor: (JJ)J
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=4, args_size=2
+ 0: lload_0
+ 1: lload_2
+ 2: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
+ 5: lreturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 arg1 J
+ 0 6 2 arg2 J
+}
+SourceFile: "TinyFrameworkNative.java"
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+ 1: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
+ value="TinyFrameworkNative_host"
+ )
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.class
+ Compiled from "TinyFrameworkNative_host.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 3, attributes: 2
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host;
+
+ public static int nativeAddTwo(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=1, args_size=1
+ 0: iload_0
+ 1: iconst_2
+ 2: iadd
+ 3: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 4 0 arg I
+
+ public static long nativeLongPlus(long, long);
+ descriptor: (JJ)J
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=4, args_size=2
+ 0: lload_0
+ 1: lload_2
+ 2: ladd
+ 3: lreturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 4 0 arg1 J
+ 0 4 2 arg2 J
+}
+SourceFile: "TinyFrameworkNative_host.java"
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 1, methods: 3, attributes: 5
+ final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
+
+ com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
+ descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ flags: (0x0000)
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: aload_0
+ 1: aload_1
+ 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ 5: aload_0
+ 6: invokespecial #x // Method java/lang/Object."<init>":()V
+ 9: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
+ 0 10 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+
+ public java.lang.Integer get();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: iconst_1
+ 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ 4: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
+
+ public java.lang.Object get();
+ descriptor: ()Ljava/lang/Object;
+ flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ 4: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
+}
+Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+InnerClasses:
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 3, attributes: 5
+ com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2();
+ descriptor: ()V
+ flags: (0x0000)
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
+
+ public java.lang.Integer get();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: iconst_2
+ 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ 4: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
+
+ public java.lang.Object get();
+ descriptor: ()Ljava/lang/Object;
+ flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ 4: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
+}
+Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+InnerClasses:
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 1, methods: 3, attributes: 5
+ final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
+
+ com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
+ descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ flags: (0x0000)
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: aload_0
+ 1: aload_1
+ 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ 5: aload_0
+ 6: invokespecial #x // Method java/lang/Object."<init>":()V
+ 9: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
+ 0 10 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+
+ public java.lang.Integer get();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: iconst_3
+ 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ 4: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
+
+ public java.lang.Object get();
+ descriptor: ()Ljava/lang/Object;
+ flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ 4: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
+}
+Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+InnerClasses:
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 3, attributes: 5
+ com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4();
+ descriptor: ()V
+ flags: (0x0000)
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
+
+ public java.lang.Integer get();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: iconst_4
+ 1: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ 4: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
+
+ public java.lang.Object get();
+ descriptor: ()Ljava/lang/Object;
+ flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ 4: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
+}
+Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier_static
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+InnerClasses:
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 1, attributes: 3
+ public int value;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: aload_0
+ 5: iload_1
+ 6: putfield #x // Field value:I
+ 9: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass;
+ 0 10 1 x I
+}
+SourceFile: "TinyFrameworkNestedClasses.java"
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+InnerClasses:
+ public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 1, attributes: 4
+ public int value;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
+ descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: aload_0
+ 1: aload_1
+ 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ 5: aload_0
+ 6: invokespecial #x // Method java/lang/Object."<init>":()V
+ 9: aload_0
+ 10: iconst_5
+ 11: putfield #x // Field value:I
+ 14: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass;
+ 0 15 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+}
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+InnerClasses:
+ public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 3, attributes: 5
+ com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1();
+ descriptor: ()V
+ flags: (0x0000)
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
+
+ public java.lang.Integer get();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: bipush 7
+ 2: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ 5: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
+
+ public java.lang.Object get();
+ descriptor: ()Ljava/lang/Object;
+ flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ 4: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
+}
+Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass.getSupplier_static
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+InnerClasses:
+ public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 4
+ public int value;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: aload_0
+ 5: bipush 6
+ 7: putfield #x // Field value:I
+ 10: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 11 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass;
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ 3: dup
+ 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V
+ 7: areturn
+ LineNumberTable:
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+}
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+InnerClasses:
+ public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass extends com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
+ super_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+ interfaces: 0, fields: 0, methods: 1, attributes: 3
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: aload_0
+ 1: iload_1
+ 2: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V
+ 5: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass;
+ 0 6 1 x I
+}
+SourceFile: "TinyFrameworkNestedClasses.java"
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+InnerClasses:
+ public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 4, attributes: 4
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: aload_0
+ 5: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ 8: dup
+ 9: aload_0
+ 10: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ 13: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
+ 16: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 17 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ 3: dup
+ 4: aload_0
+ 5: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ 8: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 9 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ 3: dup
+ 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V
+ 7: areturn
+ LineNumberTable:
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ 3: dup
+ 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V
+ 7: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
+ 10: return
+ LineNumberTable:
+}
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+InnerClasses:
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
new file mode 100644
index 0000000..6e1528a
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
@@ -0,0 +1,837 @@
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
+ Compiled from "TinyFrameworkCallerCheck.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 2, attributes: 4
+ private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl();
+ descriptor: ()V
+ flags: (0x0002) ACC_PRIVATE
+ Code:
+ stack=3, locals=1, args_size=1
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public static int getOneStub();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestStub
+}
+InnerClasses:
+ private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+SourceFile: "TinyFrameworkCallerCheck.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class
+ Compiled from "TinyFrameworkCallerCheck.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 3, attributes: 5
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public static int getOne_withCheck();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public static int getOne_noCheck();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+}
+InnerClasses:
+ private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+SourceFile: "TinyFrameworkCallerCheck.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class
+ Compiled from "TinyFrameworkClassAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 5, attributes: 3
+ public int stub;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public int addOne(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public int addTwo(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public static int nativeAddThree(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public java.lang.String visibleButUsesUnsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestStub
+}
+SourceFile: "TinyFrameworkClassAnnotations.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestStub
+ 1: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestClassLoadHook(
+ value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
+ )
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class
+ Compiled from "TinyFrameworkClassClassWideAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 3, methods: 8, attributes: 3
+ public int stub;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public int keep;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public int remove;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public int addOne(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public int addOneInner(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public void toBeRemoved(java.lang.String);
+ descriptor: (Ljava/lang/String;)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public int addTwo(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public static int nativeAddThree(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public java.lang.String unsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public java.lang.String visibleButUsesUnsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+}
+SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
+ Compiled from "TinyFrameworkClassLoadHook.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 3, attributes: 3
+ public static final java.util.Set<java.lang.Class<?>> sLoadedClasses;
+ descriptor: Ljava/util/Set;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/Set<Ljava/lang/Class<*>;>;
+
+ private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook();
+ descriptor: ()V
+ flags: (0x0002) ACC_PRIVATE
+ Code:
+ stack=3, locals=1, args_size=1
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public static void onClassLoaded(java.lang.Class<?>);
+ descriptor: (Ljava/lang/Class;)V
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+ Signature: #x // (Ljava/lang/Class<*>;)V
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+}
+SourceFile: "TinyFrameworkClassLoadHook.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class
+ Compiled from "TinyFrameworkClassWithInitializer.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 3
+ public static boolean sInitialized;
+ descriptor: Z
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+}
+SourceFile: "TinyFrameworkClassWithInitializer.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ 0: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestClassLoadHook(
+ value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
+ )
+ 1: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
+ Compiled from "TinyFrameworkExceptionTester.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 2, attributes: 3
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public static int testException();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+}
+SourceFile: "TinyFrameworkExceptionTester.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
+ Compiled from "TinyFrameworkForTextPolicy.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 5, attributes: 2
+ public int stub;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public int addOne(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public int addTwo(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public static int nativeAddThree(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public java.lang.String visibleButUsesUnsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+}
+SourceFile: "TinyFrameworkForTextPolicy.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
+ Compiled from "TinyFrameworkNative.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 5, attributes: 3
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public static native int nativeAddTwo(int);
+ descriptor: (I)I
+ flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+
+ public static int nativeAddTwo_should_be_like_this(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public static native long nativeLongPlus(long, long);
+ descriptor: (JJ)J
+ flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+
+ public static long nativeLongPlus_should_be_like_this(long, long);
+ descriptor: (JJ)J
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=4, args_size=2
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+}
+SourceFile: "TinyFrameworkNative.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+ 1: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
+ value="TinyFrameworkNative_host"
+ )
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 1, attributes: 4
+ public int value;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+}
+InnerClasses:
+ public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 1, attributes: 5
+ public int value;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
+ descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+}
+InnerClasses:
+ public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 5
+ public int value;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+}
+InnerClasses:
+ public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass extends com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
+ super_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+ interfaces: 0, fields: 0, methods: 1, attributes: 4
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=2, args_size=2
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+}
+InnerClasses:
+ public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 4, attributes: 5
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: ldc #x // String Stub!
+ 6: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+}
+InnerClasses:
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
new file mode 100644
index 0000000..5672e9c
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
@@ -0,0 +1,1774 @@
+## Class: android/hosttest/annotation/HostSideTestClassLoadHook.class
+ Compiled from "HostSideTestClassLoadHook.java"
+public interface android.hosttest.annotation.HostSideTestClassLoadHook extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestClassLoadHook
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ public abstract java.lang.String value();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
+}
+SourceFile: "HostSideTestClassLoadHook.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ 1: #x(#x=[e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE]
+ )
+ 2: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestKeep.class
+ Compiled from "HostSideTestKeep.java"
+public interface android.hosttest.annotation.HostSideTestKeep extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestKeep
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "HostSideTestKeep.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ 1: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
+ )
+ 2: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestNativeSubstitutionClass.class
+ Compiled from "HostSideTestNativeSubstitutionClass.java"
+public interface android.hosttest.annotation.HostSideTestNativeSubstitutionClass extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestNativeSubstitutionClass
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ public abstract java.lang.String value();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
+}
+SourceFile: "HostSideTestNativeSubstitutionClass.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ 1: #x(#x=[e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE]
+ )
+ 2: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestRemove.class
+ Compiled from "HostSideTestRemove.java"
+public interface android.hosttest.annotation.HostSideTestRemove extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestRemove
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "HostSideTestRemove.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ 1: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
+ )
+ 2: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestStub.class
+ Compiled from "HostSideTestStub.java"
+public interface android.hosttest.annotation.HostSideTestStub extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestStub
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "HostSideTestStub.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ 1: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
+ )
+ 2: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestSubstitute.class
+ Compiled from "HostSideTestSubstitute.java"
+public interface android.hosttest.annotation.HostSideTestSubstitute extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestSubstitute
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ public abstract java.lang.String suffix();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
+}
+SourceFile: "HostSideTestSubstitute.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ 1: #x(#x=[e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.METHOD]
+ )
+ 2: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestThrow.class
+ Compiled from "HostSideTestThrow.java"
+public interface android.hosttest.annotation.HostSideTestThrow extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestThrow
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "HostSideTestThrow.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ 1: #x(#x=[e#x.#x,e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
+ )
+ 2: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestWholeClassKeep.class
+ Compiled from "HostSideTestWholeClassKeep.java"
+public interface android.hosttest.annotation.HostSideTestWholeClassKeep extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestWholeClassKeep
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "HostSideTestWholeClassKeep.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ 1: #x(#x=[e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE]
+ )
+ 2: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: android/hosttest/annotation/HostSideTestWholeClassStub.class
+ Compiled from "HostSideTestWholeClassStub.java"
+public interface android.hosttest.annotation.HostSideTestWholeClassStub extends java.lang.annotation.Annotation
+ minor version: 0
+ major version: 61
+ flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+ this_class: #x // android/hosttest/annotation/HostSideTestWholeClassStub
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "HostSideTestWholeClassStub.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+ 1: #x(#x=[e#x.#x])
+ java.lang.annotation.Target(
+ value=[Ljava/lang/annotation/ElementType;.TYPE]
+ )
+ 2: #x(#x=e#x.#x)
+ java.lang.annotation.Retention(
+ value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+ )
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
+ Compiled from "TinyFrameworkCallerCheck.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 3, attributes: 4
+ private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl();
+ descriptor: ()V
+ flags: (0x0002) ACC_PRIVATE
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl;
+
+ public static int getOneKeep();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+ 2: ldc #x // String getOneKeep
+ 4: ldc #x // String ()I
+ 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ 15: iconst_1
+ 16: ireturn
+ LineNumberTable:
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestKeep
+
+ public static int getOneStub();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ 0: iconst_1
+ 1: ireturn
+ LineNumberTable:
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestStub
+}
+InnerClasses:
+ private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+SourceFile: "TinyFrameworkCallerCheck.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class
+ Compiled from "TinyFrameworkCallerCheck.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 3, attributes: 5
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck;
+
+ public static int getOne_withCheck();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
+ 3: ireturn
+ LineNumberTable:
+
+ public static int getOne_noCheck();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ 0: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
+ 3: ireturn
+ LineNumberTable:
+}
+InnerClasses:
+ private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+SourceFile: "TinyFrameworkCallerCheck.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class
+ Compiled from "TinyFrameworkClassAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 8, attributes: 3
+ public int stub;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public int keep;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestKeep
+
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ 0: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ 2: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+ 4: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ 7: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: aload_0
+ 5: iconst_1
+ 6: putfield #x // Field stub:I
+ 9: aload_0
+ 10: iconst_2
+ 11: putfield #x // Field keep:I
+ 14: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public int addOne(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: aload_0
+ 1: iload_1
+ 2: invokevirtual #x // Method addOneInner:(I)I
+ 5: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ 0 6 1 value I
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public int addOneInner(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ 2: ldc #x // String addOneInner
+ 4: ldc #x // String (I)I
+ 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ 15: iload_1
+ 16: iconst_1
+ 17: iadd
+ 18: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 15 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ 15 4 1 value I
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestKeep
+
+ public int addTwo(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: iload_1
+ 1: iconst_2
+ 2: iadd
+ 3: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ 0 4 1 value I
+
+ public static int nativeAddThree(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=1, args_size=1
+ 0: iload_0
+ 1: iconst_3
+ 2: iadd
+ 3: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 4 0 value I
+
+ public java.lang.String unsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+ 2: ldc #x // String unsupportedMethod
+ 4: ldc #x // String ()Ljava/lang/String;
+ 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ 15: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+ 18: new #x // class java/lang/RuntimeException
+ 21: dup
+ 22: ldc #x // String Unreachable
+ 24: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 27: athrow
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestThrow
+
+ public java.lang.String visibleButUsesUnsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ 4: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+ RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestStub
+}
+SourceFile: "TinyFrameworkClassAnnotations.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestStub
+ 1: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestClassLoadHook(
+ value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
+ )
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class
+ Compiled from "TinyFrameworkClassClassWideAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 3, methods: 8, attributes: 3
+ public int stub;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public int keep;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public int remove;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: aload_0
+ 5: iconst_1
+ 6: putfield #x // Field stub:I
+ 9: aload_0
+ 10: iconst_2
+ 11: putfield #x // Field keep:I
+ 14: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+
+ public int addOne(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: aload_0
+ 1: iload_1
+ 2: invokevirtual #x // Method addOneInner:(I)I
+ 5: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+ 0 6 1 value I
+
+ public int addOneInner(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: iload_1
+ 1: iconst_1
+ 2: iadd
+ 3: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+ 0 4 1 value I
+
+ public void toBeRemoved(java.lang.String);
+ descriptor: (Ljava/lang/String;)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: new #x // class java/lang/RuntimeException
+ 3: dup
+ 4: invokespecial #x // Method java/lang/RuntimeException."<init>":()V
+ 7: athrow
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+ 0 8 1 foo Ljava/lang/String;
+
+ public int addTwo(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: iload_1
+ 1: iconst_2
+ 2: iadd
+ 3: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+ 0 4 1 value I
+
+ public static int nativeAddThree(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=1, args_size=1
+ 0: iload_0
+ 1: iconst_3
+ 2: iadd
+ 3: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 4 0 value I
+
+ public java.lang.String unsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: ldc #x // String This value shouldn\'t be seen on the host side.
+ 2: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+
+ public java.lang.String visibleButUsesUnsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ 4: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
+}
+SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
+ Compiled from "TinyFrameworkClassLoadHook.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 3, attributes: 3
+ public static final java.util.Set<java.lang.Class<?>> sLoadedClasses;
+ descriptor: Ljava/util/Set;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/Set<Ljava/lang/Class<*>;>;
+
+ private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook();
+ descriptor: ()V
+ flags: (0x0002) ACC_PRIVATE
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook;
+
+ public static void onClassLoaded(java.lang.Class<?>);
+ descriptor: (Ljava/lang/Class;)V
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=1, args_size=1
+ 0: getstatic #x // Field sLoadedClasses:Ljava/util/Set;
+ 3: aload_0
+ 4: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
+ 9: pop
+ 10: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 11 0 clazz Ljava/lang/Class;
+ LocalVariableTypeTable:
+ Start Length Slot Name Signature
+ 0 11 0 clazz Ljava/lang/Class<*>;
+ Signature: #x // (Ljava/lang/Class<*>;)V
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ 0: new #x // class java/util/HashSet
+ 3: dup
+ 4: invokespecial #x // Method java/util/HashSet."<init>":()V
+ 7: putstatic #x // Field sLoadedClasses:Ljava/util/Set;
+ 10: return
+ LineNumberTable:
+}
+SourceFile: "TinyFrameworkClassLoadHook.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class
+ Compiled from "TinyFrameworkClassWithInitializer.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 3
+ public static boolean sInitialized;
+ descriptor: Z
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializer();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer;
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ 0: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
+ 2: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+ 4: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ 7: iconst_1
+ 8: putstatic #x // Field sInitialized:Z
+ 11: return
+ LineNumberTable:
+}
+SourceFile: "TinyFrameworkClassWithInitializer.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ 0: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestClassLoadHook(
+ value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
+ )
+ 1: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
+ Compiled from "TinyFrameworkExceptionTester.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 2, attributes: 3
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester;
+
+ public static int testException();
+ descriptor: ()I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=0
+ 0: new #x // class java/lang/IllegalStateException
+ 3: dup
+ 4: ldc #x // String Inner exception
+ 6: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
+ 9: athrow
+ 10: astore_0
+ 11: new #x // class java/lang/RuntimeException
+ 14: dup
+ 15: ldc #x // String Outer exception
+ 17: aload_0
+ 18: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V
+ 21: athrow
+ Exception table:
+ from to target type
+ 0 10 10 Class java/lang/Exception
+ StackMapTable: number_of_entries = 1
+ frame_type = 74 /* same_locals_1_stack_item */
+ stack = [ class java/lang/Exception ]
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 11 0 e Ljava/lang/Exception;
+}
+SourceFile: "TinyFrameworkExceptionTester.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
+ Compiled from "TinyFrameworkForTextPolicy.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 8, attributes: 2
+ public int stub;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public int keep;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ 0: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ 2: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+ 4: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ 7: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: aload_0
+ 5: iconst_1
+ 6: putfield #x // Field stub:I
+ 9: aload_0
+ 10: iconst_2
+ 11: putfield #x // Field keep:I
+ 14: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+
+ public int addOne(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: aload_0
+ 1: iload_1
+ 2: invokevirtual #x // Method addOneInner:(I)I
+ 5: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+ 0 6 1 value I
+
+ public int addOneInner(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ 2: ldc #x // String addOneInner
+ 4: ldc #x // String (I)I
+ 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ 15: iload_1
+ 16: iconst_1
+ 17: iadd
+ 18: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 15 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+ 15 4 1 value I
+
+ public int addTwo(int);
+ descriptor: (I)I
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: iload_1
+ 1: iconst_2
+ 2: iadd
+ 3: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+ 0 4 1 value I
+
+ public static int nativeAddThree(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=1, args_size=1
+ 0: iload_0
+ 1: iconst_3
+ 2: iadd
+ 3: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 4 0 value I
+
+ public java.lang.String unsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+ 2: ldc #x // String unsupportedMethod
+ 4: ldc #x // String ()Ljava/lang/String;
+ 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ 15: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+ 18: new #x // class java/lang/RuntimeException
+ 21: dup
+ 22: ldc #x // String Unreachable
+ 24: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ 27: athrow
+
+ public java.lang.String visibleButUsesUnsupportedMethod();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String;
+ 4: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+}
+SourceFile: "TinyFrameworkForTextPolicy.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
+ Compiled from "TinyFrameworkNative.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 5, attributes: 3
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+
+ public static int nativeAddTwo(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: iload_0
+ 1: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
+ 4: ireturn
+
+ public static int nativeAddTwo_should_be_like_this(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: iload_0
+ 1: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
+ 4: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 arg I
+
+ public static long nativeLongPlus(long, long);
+ descriptor: (JJ)J
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=4, args_size=2
+ 0: lload_0
+ 1: lload_2
+ 2: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
+ 5: lreturn
+
+ public static long nativeLongPlus_should_be_like_this(long, long);
+ descriptor: (JJ)J
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=4, args_size=2
+ 0: lload_0
+ 1: lload_2
+ 2: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
+ 5: lreturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 arg1 J
+ 0 6 2 arg2 J
+}
+SourceFile: "TinyFrameworkNative.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+ 1: #x(#x=s#x)
+ android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
+ value="TinyFrameworkNative_host"
+ )
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.class
+ Compiled from "TinyFrameworkNative_host.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 3, attributes: 3
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ 2: ldc #x // String <init>
+ 4: ldc #x // String ()V
+ 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ 15: aload_0
+ 16: invokespecial #x // Method java/lang/Object."<init>":()V
+ 19: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host;
+
+ public static int nativeAddTwo(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ 2: ldc #x // String nativeAddTwo
+ 4: ldc #x // String (I)I
+ 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ 15: iload_0
+ 16: iconst_2
+ 17: iadd
+ 18: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 15 4 0 arg I
+
+ public static long nativeLongPlus(long, long);
+ descriptor: (JJ)J
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=4, args_size=2
+ 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+ 2: ldc #x // String nativeLongPlus
+ 4: ldc #x // String (JJ)J
+ 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ 15: lload_0
+ 16: lload_2
+ 17: ladd
+ 18: lreturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 15 4 0 arg1 J
+ 15 4 2 arg2 J
+}
+SourceFile: "TinyFrameworkNative_host.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 1, methods: 3, attributes: 6
+ final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
+
+ com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
+ descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ flags: (0x0000)
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: aload_0
+ 1: aload_1
+ 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ 5: aload_0
+ 6: invokespecial #x // Method java/lang/Object."<init>":()V
+ 9: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
+ 0 10 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+
+ public java.lang.Integer get();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ 2: ldc #x // String get
+ 4: ldc #x // String ()Ljava/lang/Integer;
+ 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ 15: iconst_1
+ 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ 19: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
+
+ public java.lang.Object get();
+ descriptor: ()Ljava/lang/Object;
+ flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=1, args_size=1
+ 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ 2: ldc #x // String get
+ 4: ldc #x // String ()Ljava/lang/Object;
+ 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ 15: aload_0
+ 16: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ 19: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
+}
+InnerClasses:
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
+Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 3, attributes: 6
+ com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2();
+ descriptor: ()V
+ flags: (0x0000)
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
+
+ public java.lang.Integer get();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ 2: ldc #x // String get
+ 4: ldc #x // String ()Ljava/lang/Integer;
+ 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ 15: iconst_2
+ 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ 19: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
+
+ public java.lang.Object get();
+ descriptor: ()Ljava/lang/Object;
+ flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=1, args_size=1
+ 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ 2: ldc #x // String get
+ 4: ldc #x // String ()Ljava/lang/Object;
+ 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ 15: aload_0
+ 16: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ 19: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
+}
+InnerClasses:
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
+Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 1, methods: 3, attributes: 6
+ final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
+
+ com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
+ descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ flags: (0x0000)
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: aload_0
+ 1: aload_1
+ 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ 5: aload_0
+ 6: invokespecial #x // Method java/lang/Object."<init>":()V
+ 9: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
+ 0 10 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+
+ public java.lang.Integer get();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ 2: ldc #x // String get
+ 4: ldc #x // String ()Ljava/lang/Integer;
+ 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ 15: iconst_3
+ 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ 19: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
+
+ public java.lang.Object get();
+ descriptor: ()Ljava/lang/Object;
+ flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=1, args_size=1
+ 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ 2: ldc #x // String get
+ 4: ldc #x // String ()Ljava/lang/Object;
+ 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ 15: aload_0
+ 16: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ 19: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
+}
+InnerClasses:
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier
+Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 3, attributes: 6
+ com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4();
+ descriptor: ()V
+ flags: (0x0000)
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
+
+ public java.lang.Integer get();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ 2: ldc #x // String get
+ 4: ldc #x // String ()Ljava/lang/Integer;
+ 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ 15: iconst_4
+ 16: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ 19: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
+
+ public java.lang.Object get();
+ descriptor: ()Ljava/lang/Object;
+ flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=1, args_size=1
+ 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ 2: ldc #x // String get
+ 4: ldc #x // String ()Ljava/lang/Object;
+ 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ 15: aload_0
+ 16: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ 19: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
+}
+InnerClasses:
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier_static
+Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 1, attributes: 4
+ public int value;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: aload_0
+ 5: iload_1
+ 6: putfield #x // Field value:I
+ 9: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass;
+ 0 10 1 x I
+}
+InnerClasses:
+ public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 1, attributes: 5
+ public int value;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
+ descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: aload_0
+ 1: aload_1
+ 2: putfield #x // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ 5: aload_0
+ 6: invokespecial #x // Method java/lang/Object."<init>":()V
+ 9: aload_0
+ 10: iconst_5
+ 11: putfield #x // Field value:I
+ 14: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass;
+ 0 15 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+}
+InnerClasses:
+ public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+ minor version: 0
+ major version: 61
+ flags: (0x0020) ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 3, attributes: 6
+ com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1();
+ descriptor: ()V
+ flags: (0x0000)
+ Code:
+ stack=1, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
+
+ public java.lang.Integer get();
+ descriptor: ()Ljava/lang/Integer;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ 2: ldc #x // String get
+ 4: ldc #x // String ()Ljava/lang/Integer;
+ 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ 15: bipush 7
+ 17: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+ 20: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 15 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
+
+ public java.lang.Object get();
+ descriptor: ()Ljava/lang/Object;
+ flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=1, args_size=1
+ 0: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ 2: ldc #x // String get
+ 4: ldc #x // String ()Ljava/lang/Object;
+ 6: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+ 9: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+ 12: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+ 15: aload_0
+ 16: invokevirtual #x // Method get:()Ljava/lang/Integer;
+ 19: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 15 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
+}
+InnerClasses:
+ public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass.getSupplier_static
+Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 5
+ public int value;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: aload_0
+ 5: bipush 6
+ 7: putfield #x // Field value:I
+ 10: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 11 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass;
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ 3: dup
+ 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V
+ 7: areturn
+ LineNumberTable:
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+}
+InnerClasses:
+ public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass extends com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
+ super_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+ interfaces: 0, fields: 0, methods: 1, attributes: 4
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ 0: aload_0
+ 1: iload_1
+ 2: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V
+ 5: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass;
+ 0 6 1 x I
+}
+InnerClasses:
+ public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class
+ Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 2, methods: 4, attributes: 5
+ public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+ descriptor: Ljava/util/function/Supplier;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ 0: aload_0
+ 1: invokespecial #x // Method java/lang/Object."<init>":()V
+ 4: aload_0
+ 5: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ 8: dup
+ 9: aload_0
+ 10: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ 13: putfield #x // Field mSupplier:Ljava/util/function/Supplier;
+ 16: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 17 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+
+ public java.util.function.Supplier<java.lang.Integer> getSupplier();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ 3: dup
+ 4: aload_0
+ 5: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+ 8: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 9 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+ descriptor: ()Ljava/util/function/Supplier;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ 3: dup
+ 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V
+ 7: areturn
+ LineNumberTable:
+ Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ 0: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ 3: dup
+ 4: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V
+ 7: putstatic #x // Field sSupplier:Ljava/util/function/Supplier;
+ 10: return
+ LineNumberTable:
+}
+InnerClasses:
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+ #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+ 0: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ 1: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ 0: #x()
+ android.hosttest.annotation.HostSideTestWholeClassStub
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
new file mode 100644
index 0000000..079d2a8
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
@@ -0,0 +1,17 @@
+class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy stub
+ field stub stub
+ field keep keep
+ # field remove remove # Implicitly remove
+ method <init> ()V stub
+ method addOne (I)I stub
+ method addOneInner (I)I keep
+ method toBeRemoved (Ljava/lang/String;)V remove
+ method addTwo (I)I @addTwo_host
+ # method addTwo_host (I)I # used as a substitute
+ method nativeAddThree (I)I @addThree_host
+ # method addThree_host (I)I # used as a substitute
+ method unsupportedMethod ()Ljava/lang/String; throw
+ method visibleButUsesUnsupportedMethod ()Ljava/lang/String; stub
+
+# Class load hook
+class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy ~com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh b/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh
new file mode 100755
index 0000000..fd48646
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh
@@ -0,0 +1,132 @@
+#!/bin/bash
+# Copyright (C) 2023 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.
+
+
+source "${0%/*}"/../../common.sh
+
+# This scripts run the "tiny-framework" test, but does most stuff from the command line, using
+# the native java and javac commands.
+# This is useful to
+
+
+debug=0
+while getopts "d" opt; do
+case "$opt" in
+ d) debug=1 ;;
+esac
+done
+shift $(($OPTIND - 1))
+
+
+out=out
+
+rm -fr $out
+mkdir -p $out
+
+HOSTSTUBGEN=hoststubgen
+
+# Rebuild the tool and the dependencies. These are the only things we build with the build system.
+run m $HOSTSTUBGEN hoststubgen-annotations hoststubgen-helper-runtime truth-prebuilt junit
+
+
+# Build tiny-framework
+
+tiny_framework_classes=$out/tiny-framework/classes/
+tiny_framework_jar=$out/tiny-framework.jar
+tiny_framework_host_stub_jar=$out/tiny-framework_host_stub.jar
+tiny_framework_host_impl_jar=$out/tiny-framework_host_impl.jar
+
+tiny_test_classes=$out/tiny-test/classes/
+tiny_test_jar=$out/tiny-test.jar
+
+framework_compile_classpaths=(
+ $SOONG_INT/frameworks/base/tools/hoststubgen/hoststubgen/hoststubgen-annotations/android_common/javac/hoststubgen-annotations.jar
+)
+
+test_compile_classpaths=(
+ $SOONG_INT/external/junit/junit/android_common/combined/junit.jar
+ $SOONG_INT/prebuilts/tools/common/m2/truth-prebuilt/android_common/combined/truth-prebuilt.jar
+)
+
+test_runtime_classpaths=(
+ $SOONG_INT/frameworks/base/tools/hoststubgen/hoststubgen/hoststubgen-helper-runtime/linux_glibc_common/javac/hoststubgen-helper-runtime.jar
+)
+
+# This suite runs all tests in the JAR.
+test_classes=(com.android.hoststubgen.hosthelper.HostTestSuite)
+
+# Uncomment this to run a specific test.
+# tests=(com.android.hoststubgen.test.tinyframework.TinyFrameworkBenchmark)
+
+
+# Build tiny-framework.jar
+echo "# Building tiny-framework..."
+run $JAVAC \
+ -cp $( \
+ join : \
+ ${framework_compile_classpaths[@]} \
+ ) \
+ -d $tiny_framework_classes \
+ tiny-framework/src/**/*.java
+
+run $JAR cvf $tiny_framework_jar \
+ -C $tiny_framework_classes .
+
+# Build stub/impl jars
+echo "# Generating the stub and impl jars..."
+run $HOSTSTUBGEN \
+ @../hoststubgen-standard-options.txt \
+ --in-jar $tiny_framework_jar \
+ --out-stub-jar $tiny_framework_host_stub_jar \
+ --out-impl-jar $tiny_framework_host_impl_jar \
+ --policy-override-file policy-override-tiny-framework.txt \
+ --gen-keep-all-file out/tiny-framework_keep_all.txt \
+ --gen-input-dump-file out/tiny-framework_dump.txt \
+ $HOSTSTUBGEN_OPTS
+
+# Extract the jar files, so we can look into them.
+extract $tiny_framework_host_stub_jar $tiny_framework_host_impl_jar
+
+# Build the test
+echo "# Building tiny-test..."
+run $JAVAC \
+ -cp $( \
+ join : \
+ $tiny_framework_host_stub_jar \
+ "${test_compile_classpaths[@]}" \
+ ) \
+ -d $tiny_test_classes \
+ tiny-test/src/**/*.java
+
+run $JAR cvf $tiny_test_jar \
+ -C $tiny_test_classes .
+
+if (( $debug )) ; then
+ JAVA_OPTS="$JAVA_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8700"
+fi
+
+# Run the test
+echo "# Running tiny-test..."
+run $JAVA \
+ $JAVA_OPTS \
+ -cp $( \
+ join : \
+ $tiny_test_jar \
+ $tiny_framework_host_impl_jar \
+ "${test_compile_classpaths[@]}" \
+ "${test_runtime_classpaths[@]}" \
+ ) \
+ org.junit.runner.JUnitCore \
+ ${test_classes[@]}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
new file mode 100755
index 0000000..cee29dc
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
@@ -0,0 +1,64 @@
+#!/usr/bin/python3
+# Copyright (C) 2023 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.
+
+# Compare the tiny-framework JAR dumps to the golden files.
+
+import sys
+import os
+import unittest
+import subprocess
+
+GOLDEN_DIR = 'golden-output'
+
+# Run diff.
+def run_diff(file1, file2):
+ command = ['diff', '-u', '--ignore-blank-lines', '--ignore-space-change', file1, file2]
+ print(' '.join(command))
+ result = subprocess.run(command, stderr = sys.stdout)
+
+ success = result.returncode == 0
+
+ if success:
+ print(f'No diff found.')
+ else:
+ print(f'Fail: {file1} and {file2} are different.')
+
+ return success
+
+
+# Check one golden file.
+def check_one_file(filename):
+ print(f'= Checking file: {filename}')
+ return run_diff(os.path.join(GOLDEN_DIR, filename), filename)
+
+class TestWithGoldenOutput(unittest.TestCase):
+
+ # Test to check the generated jar files to the golden output.
+ def test_compare_to_golden(self):
+ files = os.listdir(GOLDEN_DIR)
+ files.sort()
+
+ print(f"Golden files: {files}")
+ success = True
+
+ for file in files:
+ if not check_one_file(file):
+ success = False
+
+ if not success:
+ self.fail('Some files are different. See stdout log for more details.')
+
+if __name__ == "__main__":
+ unittest.main(verbosity=2)
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.java
new file mode 100644
index 0000000..f530207
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.test.tinyframework;
+
+import android.hosttest.annotation.HostSideTestKeep;
+import android.hosttest.annotation.HostSideTestStub;
+import android.hosttest.annotation.HostSideTestWholeClassStub;
+
+/**
+ * Used by the benchmark.
+ */
+@HostSideTestWholeClassStub
+public class TinyFrameworkCallerCheck {
+
+ /**
+ * This method uses an inner method (which has the caller check).
+ *
+ * Benchmark result: 768ns
+ */
+ public static int getOne_withCheck() {
+ return Impl.getOneKeep();
+ }
+
+ /**
+ * This method doesn't have any caller check.
+ *
+ * Benchmark result: 2ns
+ */
+ public static int getOne_noCheck() {
+ return Impl.getOneStub();
+ }
+
+ private static class Impl {
+ @HostSideTestKeep
+ public static int getOneKeep() {
+ return 1;
+ }
+
+ @HostSideTestStub
+ public static int getOneStub() {
+ return 1;
+ }
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.java
new file mode 100644
index 0000000..ab387e0
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.test.tinyframework;
+
+import android.hosttest.annotation.HostSideTestClassLoadHook;
+import android.hosttest.annotation.HostSideTestKeep;
+import android.hosttest.annotation.HostSideTestRemove;
+import android.hosttest.annotation.HostSideTestStub;
+import android.hosttest.annotation.HostSideTestSubstitute;
+import android.hosttest.annotation.HostSideTestThrow;
+
+/**
+ * Test without class-wide annotations.
+ */
+@HostSideTestStub
+@HostSideTestClassLoadHook(
+ "com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded")
+public class TinyFrameworkClassAnnotations {
+ @HostSideTestStub
+ public TinyFrameworkClassAnnotations() {
+ }
+
+ @HostSideTestStub
+ public int stub = 1;
+
+ @HostSideTestKeep
+ public int keep = 2;
+
+ // Members will be deleted by default.
+ // Deleted fields cannot have an initial value, because otherwise .ctor will fail to set it at
+ // runtime.
+ public int remove;
+
+ @HostSideTestStub
+ public int addOne(int value) {
+ return addOneInner(value);
+ }
+
+ @HostSideTestKeep
+ public int addOneInner(int value) {
+ return value + 1;
+ }
+
+ @HostSideTestRemove // Explicitly remove
+ public void toBeRemoved(String foo) {
+ throw new RuntimeException();
+ }
+
+ @HostSideTestStub
+ @HostSideTestSubstitute(suffix = "_host")
+ public int addTwo(int value) {
+ throw new RuntimeException("not supported on host side");
+ }
+
+ public int addTwo_host(int value) {
+ return value + 2;
+ }
+
+ @HostSideTestStub
+ @HostSideTestSubstitute(suffix = "_host")
+ public static native int nativeAddThree(int value);
+
+ public static int nativeAddThree_host(int value) {
+ return value + 3;
+ }
+
+ @HostSideTestThrow
+ public String unsupportedMethod() {
+ return "This value shouldn't be seen on the host side.";
+ }
+
+ @HostSideTestStub
+ public String visibleButUsesUnsupportedMethod() {
+ return unsupportedMethod();
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.java
new file mode 100644
index 0000000..145b65a
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.test.tinyframework;
+
+import android.hosttest.annotation.HostSideTestStub;
+import android.hosttest.annotation.HostSideTestSubstitute;
+import android.hosttest.annotation.HostSideTestWholeClassStub;
+
+@HostSideTestWholeClassStub
+public class TinyFrameworkClassClassWideAnnotations {
+ public TinyFrameworkClassClassWideAnnotations() {
+ }
+
+ public int stub = 1;
+
+ public int keep = 2;
+
+ // Cannot have an initial value, because otherwise .ctor will fail to set it at runtime.
+ public int remove;
+
+ // @Stub
+ public int addOne(int value) {
+ return addOneInner(value);
+ }
+
+ // @Keep
+ public int addOneInner(int value) {
+ return value + 1;
+ }
+
+ // @Remove
+ public void toBeRemoved(String foo) {
+ throw new RuntimeException();
+ }
+
+ @HostSideTestStub
+ @HostSideTestSubstitute(suffix = "_host")
+ public int addTwo(int value) {
+ throw new RuntimeException("not supported on host side");
+ }
+
+ public int addTwo_host(int value) {
+ return value + 2;
+ }
+
+ @HostSideTestStub
+ @HostSideTestSubstitute(suffix = "_host")
+ public static native int nativeAddThree(int value);
+
+ public static int nativeAddThree_host(int value) {
+ return value + 3;
+ }
+
+ public String unsupportedMethod() {
+ return "This value shouldn't be seen on the host side.";
+ }
+
+ public String visibleButUsesUnsupportedMethod() {
+ return unsupportedMethod();
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java
new file mode 100644
index 0000000..98fc634
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.test.tinyframework;
+
+import android.hosttest.annotation.HostSideTestWholeClassStub;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@HostSideTestWholeClassStub
+public class TinyFrameworkClassLoadHook {
+ private TinyFrameworkClassLoadHook() {
+ }
+
+ public static final Set<Class<?>> sLoadedClasses = new HashSet<>();
+
+ public static void onClassLoaded(Class<?> clazz) {
+ sLoadedClasses.add(clazz);
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.java
new file mode 100644
index 0000000..53cfdf6
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.test.tinyframework;
+
+import android.hosttest.annotation.HostSideTestClassLoadHook;
+import android.hosttest.annotation.HostSideTestWholeClassStub;
+
+@HostSideTestClassLoadHook(
+ "com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded")
+@HostSideTestWholeClassStub
+public class TinyFrameworkClassWithInitializer {
+ static {
+ sInitialized = true;
+ }
+
+ public static boolean sInitialized;
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java
new file mode 100644
index 0000000..909d3b4
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.test.tinyframework;
+
+import android.hosttest.annotation.HostSideTestWholeClassStub;
+
+@HostSideTestWholeClassStub
+public class TinyFrameworkExceptionTester {
+ public static int testException() {
+ try {
+ throw new IllegalStateException("Inner exception");
+ } catch (Exception e) {
+ throw new RuntimeException("Outer exception", e);
+ }
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java
new file mode 100644
index 0000000..bde7c35
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.test.tinyframework;
+
+/**
+ * Class for testing the "text policy" file.
+ */
+public class TinyFrameworkForTextPolicy {
+ public TinyFrameworkForTextPolicy() {
+ }
+
+ public int stub = 1;
+
+ public int keep = 2;
+
+ // Removed fields cannot have an initial value, because otherwise .ctor will fail to set it at
+ // runtime.
+ public int remove;
+
+ public int addOne(int value) {
+ return addOneInner(value);
+ }
+
+ public int addOneInner(int value) {
+ return value + 1;
+ }
+
+ public void toBeRemoved(String foo) {
+ throw new RuntimeException();
+ }
+
+ public int addTwo(int value) {
+ throw new RuntimeException("not supported on host side");
+ }
+
+ public int addTwo_host(int value) {
+ return value + 2;
+ }
+
+ public static native int nativeAddThree(int value);
+
+ public static int addThree_host(int value) {
+ return value + 3;
+ }
+
+ public String unsupportedMethod() {
+ return "This value shouldn't be seen on the host side.";
+ }
+
+ public String visibleButUsesUnsupportedMethod() {
+ return unsupportedMethod();
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
new file mode 100644
index 0000000..c151dcc
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.test.tinyframework;
+
+import android.hosttest.annotation.HostSideTestNativeSubstitutionClass;
+import android.hosttest.annotation.HostSideTestWholeClassStub;
+
+@HostSideTestWholeClassStub
+@HostSideTestNativeSubstitutionClass("TinyFrameworkNative_host")
+public class TinyFrameworkNative {
+ public static native int nativeAddTwo(int arg);
+
+ public static int nativeAddTwo_should_be_like_this(int arg) {
+ return TinyFrameworkNative_host.nativeAddTwo(arg);
+ }
+
+ public static native long nativeLongPlus(long arg1, long arg2);
+
+ public static long nativeLongPlus_should_be_like_this(long arg1, long arg2) {
+ return TinyFrameworkNative_host.nativeLongPlus(arg1, arg2);
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
new file mode 100644
index 0000000..48f7dea
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.test.tinyframework;
+
+import android.hosttest.annotation.HostSideTestWholeClassKeep;
+
+// TODO: This annotation shouldn't be needed.
+// We should infer it from HostSideTestNativeSubstitutionClass.
+@HostSideTestWholeClassKeep
+public class TinyFrameworkNative_host {
+ public static int nativeAddTwo(int arg) {
+ return arg + 2;
+ }
+
+ public static long nativeLongPlus(long arg1, long arg2) {
+ return arg1 + arg2;
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java
new file mode 100644
index 0000000..e1c48bb
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.test.tinyframework;
+
+import android.hosttest.annotation.HostSideTestWholeClassStub;
+
+import java.util.function.Supplier;
+
+@HostSideTestWholeClassStub
+public class TinyFrameworkNestedClasses {
+ public final Supplier<Integer> mSupplier = new Supplier<Integer>() {
+ @Override
+ public Integer get() {
+ return 1;
+ }
+ };
+
+ public static final Supplier<Integer> sSupplier = new Supplier<Integer>() {
+ @Override
+ public Integer get() {
+ return 2;
+ }
+ };
+ public Supplier<Integer> getSupplier() {
+ return new Supplier<Integer>() {
+ @Override
+ public Integer get() {
+ return 3;
+ }
+ };
+ }
+
+ public static Supplier<Integer> getSupplier_static() {
+ return new Supplier<Integer>() {
+ @Override
+ public Integer get() {
+ return 4;
+ }
+ };
+ }
+
+ @HostSideTestWholeClassStub
+ public class InnerClass {
+ public int value = 5;
+ }
+
+ @HostSideTestWholeClassStub
+ public static class StaticNestedClass {
+ public int value = 6;
+
+ // Double-nest
+ public static Supplier<Integer> getSupplier_static() {
+ return new Supplier<Integer>() {
+ @Override
+ public Integer get() {
+ return 7;
+ }
+ };
+ }
+ }
+
+ public static class BaseClass {
+ public int value;
+ public BaseClass(int x) {
+ value = x;
+ }
+ }
+
+ public static class SubClass extends BaseClass {
+ public SubClass(int x) {
+ super(x);
+ }
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkBenchmark.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkBenchmark.java
new file mode 100644
index 0000000..6b5110e
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkBenchmark.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.test.tinyframework;
+
+import org.junit.Test;
+
+import java.text.DecimalFormat;
+
+/**
+ * Contains simple micro-benchmarks.
+ */
+public class TinyFrameworkBenchmark {
+ private static final int MINIMAL_ITERATION = 1000;
+ private static final int MEASURE_SECONDS = 3;
+
+ private static final DecimalFormat sFormatter = new DecimalFormat("#,###");
+
+ private void doBenchmark(String name, Runnable r) {
+ // Worm up
+ for (int i = 0; i < MINIMAL_ITERATION; i++) {
+ r.run();
+ }
+
+ // Start measuring.
+ final long start = System.nanoTime();
+ final long end = start + MEASURE_SECONDS * 1_000_000_000L;
+
+ double iteration = 0;
+ while (System.nanoTime() <= end) {
+ for (int i = 0; i < MINIMAL_ITERATION; i++) {
+ r.run();
+ }
+ iteration += MINIMAL_ITERATION;
+ }
+
+ final long realEnd = System.nanoTime();
+
+ System.out.println(String.format("%s\t%s", name,
+ sFormatter.format((((double) realEnd - start)) / iteration)));
+ }
+
+ /**
+ * Micro-benchmark for a method without a non-stub caller check.
+ */
+ @Test
+ public void benchNoCallerCheck() {
+ doBenchmark("No caller check", TinyFrameworkCallerCheck::getOne_noCheck);
+ }
+
+ /**
+ * Micro-benchmark for a method with a non-stub caller check.
+ */
+ @Test
+ public void benchWithCallerCheck() {
+ doBenchmark("With caller check", TinyFrameworkCallerCheck::getOne_withCheck);
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
new file mode 100644
index 0000000..246d065
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.test.tinyframework;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.SubClass;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class TinyFrameworkClassTest {
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void testSimple() {
+ TinyFrameworkForTextPolicy tfc = new TinyFrameworkForTextPolicy();
+ assertThat(tfc.addOne(1)).isEqualTo(2);
+ assertThat(tfc.stub).isEqualTo(1);
+ }
+
+// @Test
+// public void testDoesntCompile() {
+// TinyFrameworkClass tfc = new TinyFrameworkClass();
+//
+// tfc.addOneInner(1); // Shouldn't compile.
+// tfc.toBeRemoved("abc"); // Shouldn't compile.
+// tfc.unsupportedMethod(); // Shouldn't compile.
+// int a = tfc.keep; // Shouldn't compile
+// int b = tfc.remove; // Shouldn't compile
+// }
+
+ @Test
+ public void testSubstitute() {
+ TinyFrameworkForTextPolicy tfc = new TinyFrameworkForTextPolicy();
+ assertThat(tfc.addTwo(1)).isEqualTo(3);
+ }
+
+ @Test
+ public void testSubstituteNative() {
+ TinyFrameworkForTextPolicy tfc = new TinyFrameworkForTextPolicy();
+ assertThat(tfc.nativeAddThree(1)).isEqualTo(4);
+ }
+
+ @Test
+ public void testVisibleButUsesUnsupportedMethod() {
+ TinyFrameworkForTextPolicy tfc = new TinyFrameworkForTextPolicy();
+
+ thrown.expect(RuntimeException.class);
+ thrown.expectMessage("This method is not supported on the host side");
+ tfc.visibleButUsesUnsupportedMethod();
+ }
+
+ @Test
+ public void testNestedClass1() {
+ assertThat(new TinyFrameworkNestedClasses().mSupplier.get()).isEqualTo(1);
+ }
+
+ @Test
+ public void testNestedClass2() {
+ assertThat(TinyFrameworkNestedClasses.sSupplier.get()).isEqualTo(2);
+ }
+
+ @Test
+ public void testNestedClass3() {
+ assertThat(new TinyFrameworkNestedClasses().getSupplier().get()).isEqualTo(3);
+ }
+
+ @Test
+ public void testNestedClass4() {
+ assertThat(TinyFrameworkNestedClasses.getSupplier_static().get()).isEqualTo(4);
+ }
+
+ @Test
+ public void testNestedClass5() {
+ assertThat((new TinyFrameworkNestedClasses()).new InnerClass().value).isEqualTo(5);
+ }
+
+ @Test
+ public void testNestedClass6() {
+ assertThat(new TinyFrameworkNestedClasses.StaticNestedClass().value).isEqualTo(6);
+ }
+
+ @Test
+ public void testNestedClass7() {
+ assertThat(TinyFrameworkNestedClasses.StaticNestedClass.getSupplier_static().get())
+ .isEqualTo(7);
+ }
+
+ @Test
+ public void testNativeSubstitutionClass() {
+ assertThat(TinyFrameworkNative.nativeAddTwo(3)).isEqualTo(5);
+ }
+
+ @Test
+ public void testExitLog() {
+ thrown.expect(RuntimeException.class);
+ thrown.expectMessage("Outer exception");
+
+ TinyFrameworkExceptionTester.testException();
+
+ }
+
+ @Test
+ public void testMethodCallBeforeSuperCall() {
+ assertThat(new SubClass(3).value).isEqualTo(3);
+ }
+
+ @Test
+ public void testClassLoadHook() {
+ assertThat(TinyFrameworkClassWithInitializer.sInitialized).isTrue();
+
+ // Having this line before assertThat() will ensure these class are already loaded.
+ var classes = new Class[] {
+ TinyFrameworkClassWithInitializer.class,
+ TinyFrameworkClassAnnotations.class,
+ TinyFrameworkForTextPolicy.class,
+ };
+
+ // The following classes have a class load hook, so they should be registered.
+ assertThat(TinyFrameworkClassLoadHook.sLoadedClasses)
+ .containsAnyIn(classes);
+
+ // This class doesn't have a class load hook, so shouldn't be included.
+ assertThat(TinyFrameworkClassLoadHook.sLoadedClasses)
+ .doesNotContain(TinyFrameworkNestedClasses.class);
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithAnnotTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithAnnotTest.java
new file mode 100644
index 0000000..20cc2ec
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithAnnotTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2023 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 com.android.hoststubgen.test.tinyframework;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class TinyFrameworkClassWithAnnotTest {
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void testSimple() {
+ TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations();
+ assertThat(tfc.addOne(1)).isEqualTo(2);
+ assertThat(tfc.stub).isEqualTo(1);
+ }
+
+// @Test
+// public void testDoesntCompile() {
+// TinyFrameworkClassWithAnnot tfc = new TinyFrameworkClassWithAnnot();
+//
+// tfc.addOneInner(1); // Shouldn't compile.
+// tfc.toBeRemoved("abc"); // Shouldn't compile.
+// tfc.unsupportedMethod(); // Shouldn't compile.
+// int a = tfc.keep; // Shouldn't compile
+// int b = tfc.remove; // Shouldn't compile
+// }
+
+ @Test
+ public void testSubstitute() {
+ TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations();
+ assertThat(tfc.addTwo(1)).isEqualTo(3);
+ }
+
+ @Test
+ public void testSubstituteNative() {
+ TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations();
+ assertThat(tfc.nativeAddThree(1)).isEqualTo(4);
+ }
+
+ @Test
+ public void testVisibleButUsesUnsupportedMethod() {
+ TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations();
+
+ thrown.expect(RuntimeException.class);
+ thrown.expectMessage("This method is not supported on the host side");
+ tfc.visibleButUsesUnsupportedMethod();
+ }
+}
diff --git a/tools/hoststubgen/scripts/Android.bp b/tools/hoststubgen/scripts/Android.bp
new file mode 100644
index 0000000..5da805e
--- /dev/null
+++ b/tools/hoststubgen/scripts/Android.bp
@@ -0,0 +1,26 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+sh_binary_host {
+ name: "dump-jar",
+ src: "dump-jar",
+ visibility: ["//visibility:public"],
+}
+
+genrule_defaults {
+ name: "hoststubgen-jar-dump-defaults",
+ tools: ["dump-jar"],
+ cmd: "$(location dump-jar) -s -o $(out) $(in)",
+}
+
+sh_binary_host {
+ name: "run-ravenwood-test",
+ src: "run-ravenwood-test",
+ visibility: ["//visibility:public"],
+}
diff --git a/tools/hoststubgen/scripts/build-framework-hostside-jars-and-extract.sh b/tools/hoststubgen/scripts/build-framework-hostside-jars-and-extract.sh
new file mode 100755
index 0000000..7268123
--- /dev/null
+++ b/tools/hoststubgen/scripts/build-framework-hostside-jars-and-extract.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+# Copyright (C) 2023 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.
+
+
+# Script to build `framework-host-stub` and `framework-host-impl`, and copy the
+# generated jars to $out, and unzip them. Useful for looking into the generated files.
+
+source "${0%/*}"/../common.sh
+
+out=framework-all-stub-out
+
+rm -fr $out
+mkdir -p $out
+
+# Build the jars with `m`.
+run m framework-all-hidden-api-host
+
+# Copy the jar to out/ and extract them.
+run cp \
+ $SOONG_INT/frameworks/base/tools/hoststubgen/hoststubgen/framework-all-hidden-api-host/linux_glibc_common/gen/* \
+ $out
+
+extract $out/*.jar
diff --git a/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh b/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh
new file mode 100755
index 0000000..c3605a9
--- /dev/null
+++ b/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh
@@ -0,0 +1,76 @@
+#!/bin/bash
+# Copyright (C) 2023 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.
+
+
+# Script to build hoststubgen and run it directly (without using the build rules)
+# on framework-all.jar.
+
+
+echo "THIS SCRIPT IS BROKEN DUE TO CHANGES TO FILE PATHS TO DEPENDENT FILES. FIX IT WHEN YOU NEED TO USE IT." 1>&2
+
+exit 99
+
+
+source "${0%/*}"/../common.sh
+
+out=out
+
+mkdir -p $out
+
+# Build the tool and target jar.
+run m hoststubgen framework-all
+
+base_args=(
+ @../hoststubgen/hoststubgen-standard-options.txt
+
+ --in-jar $ANDROID_BUILD_TOP/out/soong/.intermediates/frameworks/base/framework-all/android_common/combined/framework-all.jar
+ --policy-override-file ../hoststubgen/framework-policy-override.txt "${@}"
+
+ # This file will contain all classes as an annotation file, with "keep all" policy.
+ --gen-keep-all-file $out/framework-all-keep-all-policy.txt
+
+ # This file will contains dump of all classes in the input jar.
+ --gen-input-dump-file $out/framework-all-dump.txt
+)
+
+do_it() {
+ local out_file_stem="$1"
+ shift
+ local extra_args=("${@}")
+
+ run hoststubgen \
+ "${base_args[@]}" \
+ "${extra_args[@]}" \
+ --out-stub-jar ${out_file_stem}_stub.jar \
+ --out-impl-jar ${out_file_stem}_impl.jar \
+ $HOSTSTUBGEN_OPTS
+
+ # Extract the jar files, so we can look into them.
+ run extract ${out_file_stem}_*.jar
+}
+
+#-----------------------------------------------------------------------------
+# framework-all, with all hidden APIs.
+#-----------------------------------------------------------------------------
+
+# No extra args.
+do_it $out/framework-all_host
+
+#-----------------------------------------------------------------------------
+# framework-test-api, only public/system/test-APIs in the stub.
+#-----------------------------------------------------------------------------
+
+do_it $out/framework-test-api_host \
+ --intersect-stub-jar $SOONG_INT/frameworks/base/api/android_test_stubs_current/android_common/combined/*.jar
diff --git a/tools/hoststubgen/scripts/dump-jar b/tools/hoststubgen/scripts/dump-jar
new file mode 100755
index 0000000..93729fb
--- /dev/null
+++ b/tools/hoststubgen/scripts/dump-jar
@@ -0,0 +1,163 @@
+#!/bin/bash
+# Copyright (C) 2023 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.
+
+set -e
+
+
+help() {
+ cat <<'EOF'
+
+ dump-jar: Dump java classes in jar files
+
+ Usage:
+ dump-jar [-v] CLASS-FILE [...]
+
+ Dump a *.class file
+
+ dump-jar [-v] [-s] [-o OUTPUT-FILENAME] JAR-FILE[: filename regex] [...]
+
+ Dump a jar file.
+
+ If a filename contains a ':', then the following part
+ will be used to filter files in the jar file.
+
+ For example, "file.jar:/MyClass$" will only dump "MyClass" in file.jar.
+
+ Options:
+ -v: Enable verbose output.
+
+ -s: Simple output mode, used to check HostStubGen output jars.
+
+ -o: Write the output to a specified file.
+EOF
+}
+
+# Parse the options.
+
+verbose=0
+simple=0
+output=""
+while getopts "hvso:" opt; do
+case "$opt" in
+ h)
+ help
+ exit 0
+ ;;
+ v)
+ verbose=1
+ ;;
+ s)
+ simple=1
+ ;;
+ o)
+ output="$OPTARG"
+ ;;
+ '?')
+ help
+ exit 1
+ ;;
+esac
+done
+shift $(($OPTIND - 1))
+
+JAVAP_OPTS="${JAVAP_OPTS:--v -p -s -sysinfo -constants}"
+
+if (( $simple )) ; then
+ JAVAP_OPTS="-p -c -v"
+fi
+
+
+# Normalize a java class name.
+# Convert '.' to '/'
+# Remove the *.class suffix.
+normalize() {
+ local name="$1"
+ name="${name%.class}" # Remove the .class suffix.
+ echo "$name" | tr '.' '/'
+}
+
+# Convert the output for `-s` as needed.
+filter_output() {
+ if (( $simple )) ; then
+ # For "simple output" mode,
+ # - Normalize the constant numbers (replace with "#x")
+ # - Remove the constant pool
+ # - Remove the line number table
+ # - Some other transient lines
+ #
+ # `/PATTERN-1/,/PATTERN-1/{//!d}` is a trick to delete lines between two patterns, without
+ # the start and the end lines.
+ sed -e 's/#[0-9][0-9]*/#x/g' \
+ -e '/^Constant pool:/,/^[^ ]/{//!d}' \
+ -e '/^ *line *[0-9][0-9]*: *[0-9][0-9]*$/d' \
+ -e '/SHA-256 checksum/d' \
+ -e '/Last modified/d' \
+ -e '/^Classfile jar/d'
+ else
+ cat # Print as-is.
+ fi
+}
+
+# Write to the output file (specified with -o) as needed.
+write_to_out() {
+ if [[ -n "$output" ]] ; then
+ cat >"$output"
+ echo "Wrote output to $output" 1>&2
+ else
+ cat # print to stdout
+ fi
+}
+
+for file in "${@}"; do
+
+ # *.class?
+ if echo "$file" | grep -qE '\.class$' ; then
+ echo "# Class: $file" 1>&2
+ javap $dump_code_opt $JAVAP_OPTS $file
+
+ # *.jar?
+ elif echo "$file" | grep -qE '\.jar(:.*)?$' ; then
+ # Take the regex. Remove everything up to : in $file
+ regex=""
+ if [[ "$file" =~ : ]] ; then
+ regex="$(normalize "${file##*:}")"
+ fi
+
+ # Remove everything after ':', inclusively, in $file.
+ file="${file%:*}"
+
+ # Print the filename and the regex.
+ if ! (( $simple )) ; then
+ echo -n "# Jar: $file"
+ if [[ "$regex" != "" ]] ;then
+ echo -n " (regex: $regex)"
+ fi
+ echo
+ fi
+
+ jar tf "$file" | grep '\.class$' | sort | while read -r class ; do
+ if normalize "$class" | grep -q -- "$regex" ; then
+ echo "## Class: $class"
+ javap $dump_code_opt $JAVAP_OPTS -cp $file ${class%.class}
+ else
+ (( $verbose )) && echo "## Skipping class: $class"
+ fi
+ done
+
+ else
+ echo "Unknown file type: $file" 1>&2
+ exit 1
+ fi
+done | filter_output | write_to_out
diff --git a/tools/hoststubgen/scripts/run-all-tests.sh b/tools/hoststubgen/scripts/run-all-tests.sh
new file mode 100755
index 0000000..6bc0ddb
--- /dev/null
+++ b/tools/hoststubgen/scripts/run-all-tests.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+# Copyright (C) 2023 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.
+
+source "${0%/*}"/../common.sh
+
+# Move to the top directory of hoststubgen
+cd ..
+
+# These tests are known to pass.
+READY_TEST_MODULES=(
+ HostStubGenTest-framework-all-test-host-test
+ hoststubgen-test-tiny-test
+)
+
+# First, build all the test modules. This shouldn't fail.
+run m run-ravenwood-test ${READY_TEST_MODULES[*]} ${NOT_READY_TEST_MODULES[*]}
+
+# Next, run the golden check. This should always pass too.
+# The following scripts _should_ pass too, but they depend on the internal paths to soong generated
+# files, and they may fail when something changes in the build system.
+run ./hoststubgen/test-tiny-framework/diff-and-update-golden.sh
+
+run ./hoststubgen/test-framework/run-test-without-atest.sh
+run ./hoststubgen/test-tiny-framework/run-test-manually.sh
+run ./scripts/build-framework-hostside-jars-and-extract.sh
+
+# This script is already broken on goog/master
+# run ./scripts/build-framework-hostside-jars-without-genrules.sh
+
+# These tests should all pass.
+run-ravenwood-test ${READY_TEST_MODULES[*]}
+
+echo ""${0##*/}" finished, with no unexpected failures. Ready to submit!"
\ No newline at end of file
diff --git a/tools/hoststubgen/scripts/run-ravenwood-test b/tools/hoststubgen/scripts/run-ravenwood-test
new file mode 100755
index 0000000..9bbb859
--- /dev/null
+++ b/tools/hoststubgen/scripts/run-ravenwood-test
@@ -0,0 +1,129 @@
+#!/bin/bash
+# Copyright (C) 2023 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.
+
+set -e
+
+# Script to run a "Ravenwood" host side test.
+#
+# A proper way to run these tests is to use `atest`, but `atest` has a known issue of loading
+# unrelated jar files as the class path, so for now we use this script to run host side tests.
+
+# Copy (with some changes) some functions from ../common.sh, so this script can be used without it.
+
+m() {
+ if (( $SKIP_BUILD )) ; then
+ echo "Skipping build: $*" 1>&2
+ return 0
+ fi
+ run ${ANDROID_BUILD_TOP}/build/soong/soong_ui.bash --make-mode "$@"
+}
+
+run() {
+ echo "Running: $*" 1>&2
+ "$@"
+}
+
+run_junit_test_jar() {
+ local jar="$1"
+ echo "Starting test: $jar ..."
+ run cd "${jar%/*}"
+
+ run ${JAVA:-java} $JAVA_OPTS \
+ -cp $jar \
+ org.junit.runner.JUnitCore \
+ com.android.hoststubgen.hosthelper.HostTestSuite || return 1
+ return 0
+}
+
+help() {
+ cat <<'EOF'
+
+ run-ravenwood-test -- Run Ravenwood host tests
+
+ Usage:
+ run-ravenwood-test [options] MODULE-NAME ...
+
+ Options:
+ -h: Help
+ -t: Run test only, without building
+ -b: Build only, without running
+
+ Example:
+ run-ravenwood-test HostStubGenTest-framework-test-host-test
+
+EOF
+}
+
+#-------------------------------------------------------------------------
+# Parse options
+#-------------------------------------------------------------------------
+build=0
+test=0
+
+while getopts "htb" opt; do
+ case "$opt" in
+ h) help; exit 0 ;;
+ t)
+ test=1
+ ;;
+ b)
+ build=1
+ ;;
+ esac
+done
+shift $(($OPTIND - 1))
+
+# If neither -t nor -b is provided, then build and run./
+if (( ( $build + $test ) == 0 )) ; then
+ build=1
+ test=1
+fi
+
+
+modules=("${@}")
+
+if (( "${#modules[@]}" == 0 )); then
+ help
+ exit 1
+fi
+
+#-------------------------------------------------------------------------
+# Build
+#-------------------------------------------------------------------------
+if (( $build )) ; then
+ run m "${modules[@]}"
+fi
+
+#-------------------------------------------------------------------------
+# Run
+#-------------------------------------------------------------------------
+
+failures=0
+if (( test )) ; then
+ for module in "${modules[@]}"; do
+ if run_junit_test_jar "$ANDROID_BUILD_TOP/out/host/linux-x86/testcases/${module}/${module}.jar"; then
+ : # passed.
+ else
+ failures=$(( failures + 1 ))
+ fi
+ done
+
+ if (( $failures > 0 )) ; then
+ echo "$failures test jar(s) failed." 1>&2
+ exit 2
+ fi
+fi
+
+exit 0
\ No newline at end of file