Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b638f56d authored by Makoto Onuki's avatar Makoto Onuki Committed by Android (Google) Code Review
Browse files

Merge "Add --default-method-call-hook and --default-class-load-hook" into main

parents dbc995e2 bf6dd094
Loading
Loading
Loading
Loading
+55 −10
Original line number Diff line number Diff line
@@ -38,6 +38,10 @@ public class HostTestUtils {
    private static final boolean SKIP_METHOD_LOG = "1".equals(System.getenv(
            "HOSTTEST_SKIP_METHOD_LOG"));

    /** If true, we won't print class load log. */
    private static final boolean SKIP_CLASS_LOG = "1".equals(System.getenv(
            "HOSTTEST_SKIP_CLASS_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"));
@@ -57,17 +61,34 @@ public class HostTestUtils {
    }

    /**
     * Called from methods with FilterPolicy.Log.
     * Trampoline method for method-call-hook.
     */
    public static void callMethodCallHook(
            Class<?> methodClass,
            String methodName,
            String methodDescriptor,
            String callbackMethod
    ) {
        callStaticMethodByName(callbackMethod, methodClass, methodName, methodDescriptor);
    }

    /**
     * I can be used as
     * {@code --default-method-call-hook
     * com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall}.
     *
     * It logs every single methods called.
     */
    public static void logMethodCall(
            String methodClass,
            Class<?> methodClass,
            String methodName,
            String methodDescriptor
    ) {
        if (SKIP_METHOD_LOG) {
            return;
        }
        logPrintStream.println("# " + methodClass + "." + methodName + methodDescriptor);
        logPrintStream.println("# method called: " + methodClass.getCanonicalName() + "."
                + methodName + methodDescriptor);
    }

    private static final StackWalker sStackWalker =
@@ -146,15 +167,19 @@ public class HostTestUtils {
        logPrintStream.println("! Class loaded: " + loadedClass.getCanonicalName()
                + " calling hook " + callbackMethod);

        callStaticMethodByName(callbackMethod, loadedClass);
    }

    private static void callStaticMethodByName(String classAndMethodName, Object... args) {
        // Forward the call to callbackMethod.
        final int lastPeriod = callbackMethod.lastIndexOf(".");
        final String className = callbackMethod.substring(0, lastPeriod);
        final String methodName = callbackMethod.substring(lastPeriod + 1);
        final int lastPeriod = classAndMethodName.lastIndexOf(".");
        final String className = classAndMethodName.substring(0, lastPeriod);
        final String methodName = classAndMethodName.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));
                    classAndMethodName));
        }

        Class<?> clazz = null;
@@ -169,13 +194,19 @@ public class HostTestUtils {
                    "Unable to find class load hook: Class %s must be public", className));
        }

        Class<?>[] argTypes = new Class[args.length];
        for (int i = 0; i < args.length; i++) {
            argTypes[i] = args[i].getClass();
        }

        Method method = null;
        try {
            method = clazz.getMethod(methodName, Class.class);
            method = clazz.getMethod(methodName, argTypes);
        } 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)",
                            + " (method must take exactly one parameter of type Class,"
                            + " and public static)",
                    className,
                    methodName), e);
        }
@@ -186,7 +217,7 @@ public class HostTestUtils {
                    methodName, className));
        }
        try {
            method.invoke(null, loadedClass);
            method.invoke(null, args);
        } catch (Exception e) {
            throw new HostTestException(String.format(
                    "Unable to invoke class load hook %s.%s",
@@ -194,4 +225,18 @@ public class HostTestUtils {
                    methodName), e);
        }
    }

    /**
     * I can be used as
     * {@code --default-class-load-hook
     * com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded}.
     *
     * It logs every loaded class.
     */
    public static void logClassLoaded(Class<?> clazz) {
        if (SKIP_CLASS_LOG) {
            return;
        }
        logPrintStream.println("# class loaded: " + clazz.getCanonicalName());
    }
}
+4 −2
Original line number Diff line number Diff line
@@ -6,8 +6,10 @@
--enable-non-stub-method-check
# --no-non-stub-method-check

# --enable-method-logging

#--default-method-call-hook
#    com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
#--default-class-load-hook
#    com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded

# Standard annotations.
# Note, each line is a single argument, so we need newlines after each `--xxx-annotation`.
+21 −13
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ package com.android.hoststubgen

import com.android.hoststubgen.asm.ClassNodes
import com.android.hoststubgen.filters.AnnotationBasedFilter
import com.android.hoststubgen.filters.DefaultHookInjectingFilter
import com.android.hoststubgen.filters.ClassWidePolicyPropagatingFilter
import com.android.hoststubgen.filters.ConstantFilter
import com.android.hoststubgen.filters.FilterPolicy
@@ -156,7 +157,14 @@ class HostStubGen(val options: HostStubGenOptions) {
        // 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)
        filter = ClassWidePolicyPropagatingFilter(filter)

        // Inject default hooks from options.
        filter = DefaultHookInjectingFilter(
            options.defaultClassLoadHook,
            options.defaultMethodCallHook,
            filter
        )

        // Next, Java annotation based filter.
        filter = AnnotationBasedFilter(
@@ -171,7 +179,7 @@ class HostStubGen(val options: HostStubGenOptions) {
            options.substituteAnnotations,
            options.nativeSubstituteAnnotations,
            options.classLoadHookAnnotations,
                classWidePropagator
            filter
        )

        // Next, "text based" filter, which allows to override polices without touching
+11 −6
Original line number Diff line number Diff line
@@ -48,6 +48,9 @@ class HostStubGenOptions(
        var nativeSubstituteAnnotations: MutableSet<String> = mutableSetOf(),
        var classLoadHookAnnotations: MutableSet<String> = mutableSetOf(),

        var defaultClassLoadHook: String? = null,
        var defaultMethodCallHook: String? = null,

        var intersectStubJars: MutableSet<String> = mutableSetOf(),

        var policyOverrideFile: String? = null,
@@ -63,8 +66,6 @@ class HostStubGenOptions(
        var enablePreTrace: Boolean = false,
        var enablePostTrace: Boolean = false,

        var enableMethodLogging: Boolean = false,

        var enableNonStubMethodCallDetection: Boolean = true,
) {
    companion object {
@@ -151,6 +152,12 @@ class HostStubGenOptions(
                        ret.classLoadHookAnnotations +=
                            ensureUniqueAnnotation(ai.nextArgRequired(arg))

                    "--default-class-load-hook" ->
                        ret.defaultClassLoadHook = ai.nextArgRequired(arg)

                    "--default-method-call-hook" ->
                        ret.defaultMethodCallHook = ai.nextArgRequired(arg)

                    "--intersect-stub-jar" ->
                        ret.intersectStubJars += ai.nextArgRequired(arg).ensureFileExists()

@@ -167,9 +174,6 @@ class HostStubGenOptions(
                    "--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

@@ -290,6 +294,8 @@ class HostStubGenOptions(
              substituteAnnotations=$substituteAnnotations,
              nativeSubstituteAnnotations=$nativeSubstituteAnnotations,
              classLoadHookAnnotations=$classLoadHookAnnotations,
              defaultClassLoadHook=$defaultClassLoadHook,
              defaultMethodCallHook=$defaultMethodCallHook,
              intersectStubJars=$intersectStubJars,
              policyOverrideFile=$policyOverrideFile,
              defaultPolicy=$defaultPolicy,
@@ -299,7 +305,6 @@ class HostStubGenOptions(
              enableClassChecker=$enableClassChecker,
              enablePreTrace=$enablePreTrace,
              enablePostTrace=$enablePostTrace,
              enableMethodLogging=$enableMethodLogging,
              enableNonStubMethodCallDetection=$enableNonStubMethodCallDetection,
            }
            """.trimIndent()
+20 −0
Original line number Diff line number Diff line
@@ -31,3 +31,23 @@ fun normalizeTextLine(s: String): String {
    // Remove surrounding whitespace.
    return uncommented.trim()
}

fun <T> addLists(a: List<T>, b: List<T>): List<T> {
    if (a.isEmpty()) {
        return b
    }
    if (b.isEmpty()) {
        return a
    }
    return a + b
}

fun <T> addNonNullElement(a: List<T>, b: T?): List<T> {
    if (b == null) {
        return a
    }
    if (a.isEmpty()) {
        return listOf(b)
    }
    return a + b
}
 No newline at end of file
Loading