Loading core/api/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -63,6 +64,7 @@ filegroup { visibility: [ "//frameworks/base", "//frameworks/base/api", "//frameworks/base/tools/systemfeatures", "//cts/tests/signature/api", ], } Loading tools/systemfeatures/Android.bp +51 −3 Original line number Diff line number Diff line Loading @@ -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", Loading @@ -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 { Loading Loading @@ -66,6 +113,7 @@ java_test_host { "objenesis", "mockito", "systemfeatures-gen-lib", "systemfeatures-lookup-gen-lib", "truth", ], plugins: ["systemfeatures-metadata-processor"], Loading tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt +6 −5 Original line number Diff line number Diff line Loading @@ -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" } Loading tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesLookupGenerator.kt 0 → 100644 +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() } } tools/systemfeatures/tests/src/SystemFeaturesGeneratorApiTest.java +10 −0 Original line number Diff line number Diff line Loading @@ -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
core/api/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -63,6 +64,7 @@ filegroup { visibility: [ "//frameworks/base", "//frameworks/base/api", "//frameworks/base/tools/systemfeatures", "//cts/tests/signature/api", ], } Loading
tools/systemfeatures/Android.bp +51 −3 Original line number Diff line number Diff line Loading @@ -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", Loading @@ -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 { Loading Loading @@ -66,6 +113,7 @@ java_test_host { "objenesis", "mockito", "systemfeatures-gen-lib", "systemfeatures-lookup-gen-lib", "truth", ], plugins: ["systemfeatures-metadata-processor"], Loading
tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt +6 −5 Original line number Diff line number Diff line Loading @@ -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" } Loading
tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesLookupGenerator.kt 0 → 100644 +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() } }
tools/systemfeatures/tests/src/SystemFeaturesGeneratorApiTest.java +10 −0 Original line number Diff line number Diff line Loading @@ -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()); } }