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

Commit d3ba32cd authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Support system feature lookups in host tooling" into main

parents fbd99f24 807df5e7
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@ package {
    default_visibility: [
        "//frameworks/base",
        "//frameworks/base/api",
        "//frameworks/base/tools/systemfeatures",
    ],
    // See: http://go/android-license-faq
    // A large-scale-change added 'default_applicable_licenses' to import
@@ -63,6 +64,7 @@ filegroup {
    visibility: [
        "//frameworks/base",
        "//frameworks/base/api",
        "//frameworks/base/tools/systemfeatures",
        "//cts/tests/signature/api",
    ],
}
+51 −3
Original line number Diff line number Diff line
@@ -8,12 +8,13 @@ package {
    default_team: "trendy_team_system_performance",
}

// Code generation for compile-time system feature queries in the framework.
java_library_host {
    name: "systemfeatures-gen-lib",
    srcs: [
        "src/**/*.java",
        "src/**/*.kt",
        ":framework-metalava-annotations",
        ":systemfeatures-lookup-srcs",
        "src/com/android/systemfeatures/SystemFeaturesGenerator.kt",
    ],
    static_libs: [
        "guava",
@@ -27,10 +28,56 @@ java_binary_host {
    static_libs: ["systemfeatures-gen-lib"],
}

// Code generation for system feature name lookups in host tools that cannot
// depend on the framework.
java_library_host {
    name: "systemfeatures-lookup-gen-lib",
    srcs: ["src/com/android/systemfeatures/SystemFeaturesLookupGenerator.kt"],
    static_libs: [
        "javapoet",
        "metalava-signature-reader",
    ],
    visibility: ["//visibility:private"],
}

java_binary_host {
    name: "systemfeatures-lookup-gen-tool",
    main_class: "com.android.systemfeatures.SystemFeaturesLookupGenerator",
    static_libs: ["systemfeatures-lookup-gen-lib"],
    visibility: ["//visibility:private"],
}

genrule {
    name: "systemfeatures-lookup-srcs",
    cmd: "$(location systemfeatures-lookup-gen-tool) $(in) > $(out)",
    out: ["SystemFeaturesLookup.java"],
    srcs: [
        ":non-updatable-current.txt",
        ":non-updatable-module-lib-current.txt",
        ":non-updatable-system-current.txt",
        ":non-updatable-test-current.txt",
    ],
    tools: ["systemfeatures-lookup-gen-tool"],
    visibility: ["//visibility:private"],
}

// Code generation for runtime system feature metadata queries in the framework.
java_library_host {
    name: "systemfeatures-metadata-lib",
    srcs: [
        ":framework-metalava-annotations",
        "src/com/android/systemfeatures/SystemFeaturesMetadataProcessor.kt",
    ],
    static_libs: [
        "guava",
        "javapoet",
    ],
}

java_plugin {
    name: "systemfeatures-metadata-processor",
    processor_class: "com.android.systemfeatures.SystemFeaturesMetadataProcessor",
    static_libs: ["systemfeatures-gen-lib"],
    static_libs: ["systemfeatures-metadata-lib"],
}

genrule {
@@ -66,6 +113,7 @@ java_test_host {
        "objenesis",
        "mockito",
        "systemfeatures-gen-lib",
        "systemfeatures-lookup-gen-lib",
        "truth",
    ],
    plugins: ["systemfeatures-metadata-processor"],
+6 −5
Original line number Diff line number Diff line
@@ -213,11 +213,12 @@ object SystemFeaturesGenerator {

    private fun parseFeatureName(name: String): String =
        when {
            name.startsWith("android") ->
                throw IllegalArgumentException(
                    "Invalid feature name input: \"android\"-namespaced features must be " +
                    "provided as PackageManager.FEATURE_* suffixes, not raw feature strings."
            name.startsWith("android") -> {
                SystemFeaturesLookup.getDeclaredFeatureVarNameFromValue(name)
                    ?: throw IllegalArgumentException(
                        "Unrecognized Android system feature name: $name"
                    )
            }
            name.startsWith("FEATURE_") -> name
            else -> "FEATURE_$name"
        }
+116 −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.systemfeatures

import com.android.tools.metalava.model.text.ApiFile
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.JavaFile
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.TypeSpec
import java.io.File
import javax.lang.model.element.Modifier

/*
 * Simple Java code generator that takes as input a list of Metalava API files and generates an
 * accessory class that maps from PackageManager system feature variable values to their
 * declared PackageManager variable names. This is needed for host tooling that cannot depend
 * directly on the base framework lib/srcs.
 *
 * <pre>
 * package com.android.systemfeatures;
 * public final class SystemFeaturesLookup {
 *     // Gets the declared system feature var name from its string value.
 *     // Example: "android.software.print" -> "FEATURE_PRINTING"
 *     public static String getDeclaredFeatureVarNameFromValue(String featureVarValue);
 * }
 * </pre>
 */
object SystemFeaturesLookupGenerator {

    /** Main entrypoint for system feature constant lookup codegen. */
    @JvmStatic
    fun main(args: Array<String>) {
        generate(args.asIterable(), System.out)
    }

    /**
     * Simple API entrypoint for system feature lookup codegen.
     *
     * Given a list of Metalava API files, pipes a generated SystemFeaturesLookup class into output.
     */
    @JvmStatic
    fun generate(apiFilePaths: Iterable<String>, output: Appendable) {
        val featuresMap = parse(apiFilePaths)

        val stringClassName = ClassName.get(String::class.java)

        val featureLookupMethod =
            MethodSpec.methodBuilder("getDeclaredFeatureVarNameFromValue")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .addAnnotation(ClassName.get("android.annotation", "Nullable"))
                .addJavadoc("Gets the declared system feature var name from its string value.")
                .addJavadoc("\n\nExample: \"android.software.print\" -> \"FEATURE_PRINTING\"")
                .addJavadoc("\n\n@hide")
                .returns(stringClassName)
                .addParameter(stringClassName, "featureVarValue")
                .beginControlFlow("switch (featureVarValue)")
                .apply {
                    featuresMap.forEach { (key, value) ->
                        addStatement("case \$S: return \$S", key, value)
                    }
                }
                .addStatement("default: return null")
                .endControlFlow()
                .build()

        val outputClassName = ClassName.get("com.android.systemfeatures", "SystemFeaturesLookup")
        val systemFeaturesApiLookupClass =
            TypeSpec.classBuilder(outputClassName)
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                .addMethod(featureLookupMethod)
                .addJavadoc("@hide")
                .build()

        JavaFile.builder(outputClassName.packageName(), systemFeaturesApiLookupClass)
            .indent("    ")
            .skipJavaLangImports(true)
            .addFileComment("This file is auto-generated. DO NOT MODIFY.\n")
            .build()
            .writeTo(output)
    }

    /**
     * Given a list of Metalava API files, extracts a mapping from all @SdkConstantType.FEATURE
     * PackageManager values to their declared variable names, e.g.,
     * - "android.hardware.type.automotive" -> "FEATURE_AUTOMOTIVE"
     * - "android.software.print" -> "FEATURE_PRINTING"
     */
    @JvmStatic
    fun parse(apiFilePaths: Iterable<String>): Map<String, String> {
        return ApiFile.parseApi(apiFilePaths.map(::File))
            .findClass("android.content.pm.PackageManager")
            ?.fields()
            ?.filter { field ->
                field.type().isString() &&
                    field.modifiers.isStatic() &&
                    field.modifiers.isFinal() &&
                    field.name().startsWith("FEATURE_") &&
                    field.legacyInitialValue() != null
            }
            ?.associateBy({ it.legacyInitialValue()!!.toString() }, { it.name() }) ?: emptyMap()
    }
}
+10 −0
Original line number Diff line number Diff line
@@ -78,4 +78,14 @@ public class SystemFeaturesGeneratorApiTest {
        SystemFeaturesGenerator.generate(args, mOut);
        verify(mOut, never()).append(any());
    }

    @Test
    public void testValidFeatureNameFromAndroidNamespace() throws IOException {
        final String[] args = new String[] {
            "com.foo.Features",
            "--feature=android.hardware.touchscreen:0",
        };
        SystemFeaturesGenerator.generate(args, mOut);
        verify(mOut, atLeastOnce()).append(any());
    }
}
Loading