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

Commit 0c2eb6f5 authored by John Wu's avatar John Wu Committed by Android (Google) Code Review
Browse files

Merge "Add a "post-processing" step for text policies" into main

parents 4e829783 d5b245c1
Loading
Loading
Loading
Loading
+62 −5
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import com.android.hoststubgen.asm.toHumanReadableClassName
import com.android.hoststubgen.asm.toHumanReadableMethodName
import com.android.hoststubgen.asm.toJvmClassName
import com.android.hoststubgen.log
import com.android.hoststubgen.utils.Trie

// TODO: Validate all input names.

@@ -34,6 +35,27 @@ class InMemoryOutputFilter(
    private val mClassLoadHooks = mutableMapOf<String, String>()
    private val mMethodCallReplaceSpecs = mutableListOf<MethodCallReplaceSpec>()
    private val mTypeRenameSpecs = mutableListOf<TypeRenameSpec>()
    private val mPackagePolicies = PackagePolicyTrie()

    // We want to pick the most specific filter for a package name.
    // Since any package with a matching prefix is a valid match, we can use a prefix tree
    // to help us find the nearest matching filter.
    private class PackagePolicyTrie : Trie<String, String, FilterPolicyWithReason>() {
        // Split package name into individual component
        override fun splitToComponents(key: String): Iterator<String> {
            return key.split('.').iterator()
        }
    }

    private fun getPackageKey(packageName: String): String {
        return packageName.toHumanReadableClassName()
    }

    private fun getPackageKeyFromClass(className: String): String {
        val clazz = className.toHumanReadableClassName()
        val idx = clazz.lastIndexOf('.')
        return if (idx >= 0) clazz.substring(0, idx) else ""
    }

    private fun getClassKey(className: String): String {
        return className.toHumanReadableClassName()
@@ -72,8 +94,35 @@ class InMemoryOutputFilter(
        }
    }

    // Add a "post-processing" step that applies to all policies
    private inline fun processPolicy(
        currentPolicy: FilterPolicyWithReason?,
        fallback: () -> FilterPolicyWithReason
    ): FilterPolicyWithReason {
        // If there's no policy set in our current filter, just use fallback.
        val policy = currentPolicy ?: return fallback()

        // It's possible that getting policy from inner filters may throw.
        // If that's the case, then we don't apply additional post-processing.
        val innerPolicy = runCatching(fallback).getOrNull() ?: return policy

        // Note, because policies in this filter are defined in the policy file, it takes precedence
        // over annotations. However, if an item has both a text policy and inner (lower-priority)
        // policies such as an annotation-based policy and if they're the same, we use the inner
        // policy's "reason" instead. This allows us to differentiate "APIs that are enabled with an
        // annotation" from "APIs that are enabled by a text policy (which are usually only used
        // during development)".
        if (policy.policy == innerPolicy.policy) {
            return innerPolicy
        }

        return policy
    }

    override fun getPolicyForClass(className: String): FilterPolicyWithReason {
        return mPolicies[getClassKey(className)] ?: super.getPolicyForClass(className)
        val policy = mPolicies[getClassKey(className)]
            ?: mPackagePolicies[getPackageKeyFromClass(className)]
        return processPolicy(policy) { super.getPolicyForClass(className) }
    }

    fun setPolicyForClass(className: String, policy: FilterPolicyWithReason) {
@@ -81,9 +130,14 @@ class InMemoryOutputFilter(
        mPolicies[getClassKey(className)] = policy
    }

    fun setPolicyForPackage(packageName: String, policy: FilterPolicyWithReason) {
        mPackagePolicies[getPackageKey(packageName)] = policy
    }

    override fun getPolicyForField(className: String, fieldName: String): FilterPolicyWithReason {
        return mPolicies[getFieldKey(className, fieldName)]
            ?: super.getPolicyForField(className, fieldName)
        return processPolicy(mPolicies[getFieldKey(className, fieldName)]) {
            super.getPolicyForField(className, fieldName)
        }
    }

    fun setPolicyForField(className: String, fieldName: String, policy: FilterPolicyWithReason) {
@@ -96,9 +150,12 @@ class InMemoryOutputFilter(
        methodName: String,
        descriptor: String,
    ): FilterPolicyWithReason {
        return mPolicies[getMethodKey(className, methodName, descriptor)]
        val policy = mPolicies[getMethodKey(className, methodName, descriptor)]
            ?: mPolicies[getMethodKey(className, methodName, "*")]
            ?: super.getPolicyForMethod(className, methodName, descriptor)

        return processPolicy(policy) {
            super.getPolicyForMethod(className, methodName, descriptor)
        }
    }

    fun setPolicyForMethod(
+0 −79
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.toHumanReadableClassName
import com.android.hoststubgen.utils.Trie

/**
 * Filter to apply a policy to classes inside a package, either directly or indirectly.
 *
 * Note, because package-wide policies are defined in the policy file, it takes precedence
 * over annotations. However, if a class has both a package policy and other (lower-priority)
 * policies such as an annotation-based policy and if they're the same, we use the other policy's
 * "reason" instead. This allows us to differentiate "APIs that are enabled with an annotation"
 * from "APIs that are enabled by a package policy (which is usually used during development)".
 */
class PackageFilter(
    fallback: OutputFilter
) : DelegatingFilter(fallback) {

    private val mPackagePolicies = PackagePolicyTrie()

    // We want to pick the most specific filter for a package name.
    // Since any package with a matching prefix is a valid match, we can use a prefix tree
    // to help us find the nearest matching filter.
    private class PackagePolicyTrie : Trie<String, String, FilterPolicyWithReason>() {
        // Split package name into individual component
        override fun splitToComponents(key: String): Iterator<String> {
            return key.split('.').iterator()
        }
    }

    private fun getPackageKey(packageName: String): String {
        return packageName.toHumanReadableClassName()
    }

    private fun getPackageKeyFromClass(className: String): String {
        val clazz = className.toHumanReadableClassName()
        val idx = clazz.lastIndexOf('.')
        return if (idx >= 0) clazz.substring(0, idx) else ""
    }

    /**
     * Add a policy to all classes inside a package, either directly or indirectly.
     */
    fun addPolicy(packageName: String, policy: FilterPolicyWithReason) {
        mPackagePolicies[getPackageKey(packageName)] = policy
    }

    override fun getPolicyForClass(className: String): FilterPolicyWithReason {
        val packageWidePolicy = mPackagePolicies[getPackageKeyFromClass(className)]
        val origPolicy = super.getPolicyForClass(className)

        // If there's no package-wide policy, just fall-back.
        if (packageWidePolicy == null) {
            return origPolicy
        }

        // Otherwise, see if the package wide policy is different from the fallback policy.
        // If they're the same, use the fallback one.
        if (packageWidePolicy.policy == origPolicy.policy) {
            return origPolicy
        }
        return packageWidePolicy
    }
}
+2 −4
Original line number Diff line number Diff line
@@ -136,8 +136,7 @@ class TextFileFilterPolicyBuilder(
    private val parser = TextFileFilterPolicyParser()

    private val subclassFilter = SubclassFilter(classes, fallback)
    private val packageFilter = PackageFilter(subclassFilter)
    private val imf = InMemoryOutputFilter(classes, packageFilter)
    private val imf = InMemoryOutputFilter(classes, subclassFilter)
    private var aidlPolicy: FilterPolicyWithReason? = null
    private var featureFlagsPolicy: FilterPolicyWithReason? = null
    private var syspropsPolicy: FilterPolicyWithReason? = null
@@ -184,9 +183,8 @@ class TextFileFilterPolicyBuilder(
            if (policy.policy == FilterPolicy.AnnotationAllowed) {
                throw ParseException("${FilterPolicy.AnnotationAllowed.policyStringOrPrefix}" +
                        " on `package` isn't supported yet.")
                return
            }
            packageFilter.addPolicy(name, policy)
            imf.setPolicyForPackage(name, policy)
        }

        override fun onRename(pattern: Pattern, prefix: String) {
+19 −19
Original line number Diff line number Diff line
@@ -2434,7 +2434,7 @@ Constant pool:
    RuntimeVisibleAnnotations:
      x: #x(#x=s#x)
        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep(
          reason="class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester [inner-reason: file-override]"
          reason="class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester [inner-reason: class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace [inner-reason: class-annotation]]"
        )

  public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester(int);
@@ -2456,7 +2456,7 @@ Constant pool:
    RuntimeVisibleAnnotations:
      x: #x(#x=s#x)
        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep(
          reason="file-override"
          reason="class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester [inner-reason: class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace [inner-reason: class-annotation]]"
        )
}
InnerClasses:
@@ -2465,7 +2465,7 @@ SourceFile: "TinyFrameworkMethodCallReplace.java"
RuntimeVisibleAnnotations:
  x: #x(#x=s#x)
    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep(
      reason="file-override"
      reason="class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace [inner-reason: class-annotation]"
    )
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class
@@ -2494,7 +2494,7 @@ Constant pool:
    RuntimeVisibleAnnotations:
      x: #x(#x=s#x)
        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep(
          reason="class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo [inner-reason: class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace [inner-reason: file-override]]"
          reason="class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo [inner-reason: class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace [inner-reason: class-annotation]]"
        )

  public static void startThread(java.lang.Thread);
@@ -2515,7 +2515,7 @@ Constant pool:
    RuntimeVisibleAnnotations:
      x: #x(#x=s#x)
        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep(
          reason="class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo [inner-reason: class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace [inner-reason: file-override]]"
          reason="class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo [inner-reason: class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace [inner-reason: class-annotation]]"
        )

  public static int add(int, int);
@@ -2535,7 +2535,7 @@ Constant pool:
    RuntimeVisibleAnnotations:
      x: #x(#x=s#x)
        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep(
          reason="class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo [inner-reason: class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace [inner-reason: file-override]]"
          reason="class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo [inner-reason: class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace [inner-reason: class-annotation]]"
        )

  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester newConstructorTester(int);
@@ -2557,7 +2557,7 @@ Constant pool:
    RuntimeVisibleAnnotations:
      x: #x(#x=s#x)
        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep(
          reason="class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo [inner-reason: class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace [inner-reason: file-override]]"
          reason="class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo [inner-reason: class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace [inner-reason: class-annotation]]"
        )
}
InnerClasses:
@@ -2567,7 +2567,7 @@ SourceFile: "TinyFrameworkMethodCallReplace.java"
RuntimeVisibleAnnotations:
  x: #x(#x=s#x)
    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep(
      reason="class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace [inner-reason: file-override]"
      reason="class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace [inner-reason: class-annotation]"
    )
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.class
@@ -2596,7 +2596,7 @@ Constant pool:
    RuntimeVisibleAnnotations:
      x: #x(#x=s#x)
        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep(
          reason="class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace [inner-reason: file-override]"
          reason="class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace [inner-reason: class-annotation]"
        )

  public static boolean nonStaticMethodCallReplaceTester() throws java.lang.Exception;
@@ -2632,7 +2632,7 @@ Constant pool:
    RuntimeVisibleAnnotations:
      x: #x(#x=s#x)
        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep(
          reason="class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace [inner-reason: file-override]"
          reason="class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace [inner-reason: class-annotation]"
        )

  public static int staticMethodCallReplaceTester();
@@ -2648,7 +2648,7 @@ Constant pool:
    RuntimeVisibleAnnotations:
      x: #x(#x=s#x)
        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep(
          reason="class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace [inner-reason: file-override]"
          reason="class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace [inner-reason: class-annotation]"
        )

  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester constructorReplaceTester(int);
@@ -2672,7 +2672,7 @@ Constant pool:
    RuntimeVisibleAnnotations:
      x: #x(#x=s#x)
        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep(
          reason="class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace [inner-reason: file-override]"
          reason="class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace [inner-reason: class-annotation]"
        )

  private static int originalAdd(int, int);
@@ -2694,7 +2694,7 @@ Constant pool:
    RuntimeVisibleAnnotations:
      x: #x(#x=s#x)
        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep(
          reason="file-override"
          reason="class-wide in com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace [inner-reason: class-annotation]"
        )

  private static void lambda$nonStaticMethodCallReplaceTester$0(java.util.concurrent.atomic.AtomicBoolean);
@@ -2714,7 +2714,7 @@ Constant pool:
    RuntimeVisibleAnnotations:
      x: #x(#x=s#x)
        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep(
          reason="is-synthetic-method [inner-reason: file-override]"
          reason="is-synthetic-method [inner-reason: class-annotation]"
        )
}
InnerClasses:
@@ -2725,7 +2725,7 @@ SourceFile: "TinyFrameworkMethodCallReplace.java"
RuntimeVisibleAnnotations:
  x: #x(#x=s#x)
    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep(
      reason="file-override"
      reason="class-annotation"
    )
  x: #x()
    android.hosttest.annotation.HostSideTestWholeClassKeep
@@ -4369,7 +4369,7 @@ SourceFile: "C2.java"
RuntimeVisibleAnnotations:
  x: #x(#x=s#x)
    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep(
      reason="file-override"
      reason="extends com.android.hoststubgen.test.tinyframework.subclasstest.C1"
    )
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
  Compiled from "C3.java"
@@ -4387,7 +4387,7 @@ SourceFile: "C3.java"
RuntimeVisibleAnnotations:
  x: #x(#x=s#x)
    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep(
      reason="file-override"
      reason="extends com.android.hoststubgen.test.tinyframework.subclasstest.C1"
    )
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
  Compiled from "CA.java"
@@ -4585,7 +4585,7 @@ SourceFile: "I2.java"
RuntimeVisibleAnnotations:
  x: #x(#x=s#x)
    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep(
      reason="file-override"
      reason="extends com.android.hoststubgen.test.tinyframework.subclasstest.I1"
    )
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
  Compiled from "I3.java"
@@ -4603,7 +4603,7 @@ SourceFile: "I3.java"
RuntimeVisibleAnnotations:
  x: #x(#x=s#x)
    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep(
      reason="file-override"
      reason="extends com.android.hoststubgen.test.tinyframework.subclasstest.I1"
    )
## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
  Compiled from "IA.java"
+19 −19

File changed.

Preview size limit exceeded, changes collapsed.

Loading