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

Commit 039691a4 authored by John Wu's avatar John Wu
Browse files

[HostStubGen] Support experimental APIs

We would like to be able to "toggle" certain APIs at runtime. To achieve
this, we inject a method call hook into the experimental API method
body, and in the method call hook, we can implement whatever logic that
is necessary to determine whether the API should throw or not.

The implementation basically reuses the existing method call hook
infrastructure, but instead of applying it on every single method, the
method call hook can be explicitly set on package/class/method level in
text policy files.

Bug: 292141694
Flag: EXEMPT host side change only
Test: f/b/r/scripts/run-ravenwood-tests.sh
Change-Id: I69036be5bf028fd9e002c3ac7c4db77c613cf172
parent 611dfbee
Loading
Loading
Loading
Loading
+35 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.METHOD;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Annotation injected to all methods that are processed as "experimental".
 */
@Target({METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface HostStubGenProcessedAsExperimental {
    String CLASS_INTERNAL_NAME = HostTestUtils.getInternalName(
            HostStubGenProcessedAsExperimental.class);
    String CLASS_DESCRIPTOR = "L" + CLASS_INTERNAL_NAME + ";";

    String reason() default "";
}
+2 −1
Original line number Diff line number Diff line
@@ -74,7 +74,8 @@ class HostStubGenClassProcessor(
            deleteClassFinals = options.deleteFinals.get,
            deleteMethodFinals = options.deleteFinals.get,
            throwExceptionType = options.throwExceptionType.get,
            annotationsToMakeVisible = options.allAnnotationSet
            annotationsToMakeVisible = options.allAnnotationSet,
            experimentalMethodCallHook = options.experimentalMethodCallHook.get,
        )

        val verbosePrinter = PrintWriter(log.getWriter(LogLevel.Verbose))
+5 −0
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ open class HostStubGenClassProcessorOptions(

    val defaultClassLoadHook: SetOnce<String?> = SetOnce(null),
    val defaultMethodCallHook: SetOnce<String?> = SetOnce(null),
    val experimentalMethodCallHook: SetOnce<String?> = SetOnce(null),

    val policyOverrideFiles: MutableList<FileOrResource> = mutableListOf(),

@@ -146,6 +147,9 @@ open class HostStubGenClassProcessorOptions(
            "--default-method-call-hook" ->
                defaultMethodCallHook.set(nextArg())

            "--experimental-method-call-hook" ->
                experimentalMethodCallHook.set(nextArg())

            "--delete-finals" -> deleteFinals.set(true)

            "--throw-exception" -> throwExceptionType.set(nextArg())
@@ -185,6 +189,7 @@ open class HostStubGenClassProcessorOptions(
            annotationAllowedClassesFile=$annotationAllowedClassesFile,
            defaultClassLoadHook=$defaultClassLoadHook,
            defaultMethodCallHook=$defaultMethodCallHook,
            experimentalMethodCallHook=$experimentalMethodCallHook,
            policyOverrideFiles=${policyOverrideFiles.toTypedArray().contentToString()},
            defaultPolicy=$defaultPolicy,
            deleteFinals=$deleteFinals,
+1 −1
Original line number Diff line number Diff line
@@ -52,7 +52,7 @@ class ClassWidePolicyPropagatingFilter(

    private fun getClassWidePolicy(className: String, resolve: Boolean): FilterPolicyWithReason? {
        outermostFilter.getPolicyForClass(className).let { policy ->
            if (policy.policy == FilterPolicy.KeepClass) {
            if (policy.policy.isClassWide) {
                val p = if (resolve) {
                    policy.policy.resolveClassWidePolicy()
                } else {
+9 −3
Original line number Diff line number Diff line
@@ -59,7 +59,13 @@ enum class FilterPolicy(val policyStringOrPrefix: String) {
     * used in the "main" filter chain. (which would be detected by [SanitizationFilter].)
     * It's used in a separate filter chain used by [AnnotationBasedFilter].
     */
    AnnotationAllowed("allow-annotation");
    AnnotationAllowed("allow-annotation"),

    /**
     * Mark APIs as experimental. A new method call hook will be injected into methods so
     * these APIs can be toggled at runtime using environment variables.
     */
    Experimental("experimental");

    val needsInOutput: Boolean
        get() {
@@ -73,7 +79,7 @@ enum class FilterPolicy(val policyStringOrPrefix: String) {
    val isUsableWithClasses: Boolean
        get() {
            return when (this) {
                Keep, KeepClass, Remove, AnnotationAllowed -> true
                Keep, KeepClass, Remove, AnnotationAllowed, Experimental -> true
                else -> false
            }
        }
@@ -126,7 +132,7 @@ enum class FilterPolicy(val policyStringOrPrefix: String) {
    val isClassWide: Boolean
        get() {
            return when (this) {
                Remove, KeepClass -> true
                Remove, KeepClass, Experimental -> true
                else -> false
            }
        }
Loading