diff options
4 files changed, 173 insertions, 0 deletions
diff --git a/tools/systemfeatures/Android.bp b/tools/systemfeatures/Android.bp index e6d0a3d4149f..2ebede39504e 100644 --- a/tools/systemfeatures/Android.bp +++ b/tools/systemfeatures/Android.bp @@ -13,6 +13,7 @@ java_library_host { srcs: [ "src/**/*.java", "src/**/*.kt", + ":framework-metalava-annotations", ], static_libs: [ "guava", @@ -26,6 +27,12 @@ java_binary_host { static_libs: ["systemfeatures-gen-lib"], } +java_plugin { + name: "systemfeatures-metadata-processor", + processor_class: "com.android.systemfeatures.SystemFeaturesMetadataProcessor", + static_libs: ["systemfeatures-gen-lib"], +} + genrule { name: "systemfeatures-gen-tests-srcs", cmd: "$(location systemfeatures-gen-tool) com.android.systemfeatures.RwNoFeatures --readonly=false > $(location RwNoFeatures.java) && " + @@ -61,6 +68,7 @@ java_test_host { "systemfeatures-gen-lib", "truth", ], + plugins: ["systemfeatures-metadata-processor"], } // Rename the goldens as they may be copied into the source tree, and we don't diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesMetadataProcessor.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesMetadataProcessor.kt new file mode 100644 index 000000000000..100d869a663f --- /dev/null +++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesMetadataProcessor.kt @@ -0,0 +1,110 @@ +/* + * 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.systemfeatures + +import android.annotation.SdkConstant +import com.squareup.javapoet.FieldSpec +import com.squareup.javapoet.JavaFile +import com.squareup.javapoet.TypeSpec +import java.io.IOException +import javax.annotation.processing.AbstractProcessor +import javax.annotation.processing.ProcessingEnvironment +import javax.annotation.processing.RoundEnvironment +import javax.lang.model.SourceVersion +import javax.lang.model.element.Modifier +import javax.lang.model.element.TypeElement +import javax.tools.Diagnostic + +/* + * Simple Java code generator for computing metadata for system features. + * + * <p>The output is a single class file, `com.android.internal.pm.SystemFeaturesMetadata`, with + * properties computed from feature constant definitions in the PackageManager class. This + * class is only produced if the processed environment includes PackageManager; all other + * invocations are ignored. + */ +class SystemFeaturesMetadataProcessor : AbstractProcessor() { + + private lateinit var packageManagerType: TypeElement + + override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.latestSupported() + + override fun getSupportedAnnotationTypes() = setOf(SDK_CONSTANT_ANNOTATION_NAME) + + override fun init(processingEnv: ProcessingEnvironment) { + super.init(processingEnv) + packageManagerType = + processingEnv.elementUtils.getTypeElement("android.content.pm.PackageManager")!! + } + + override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean { + if (roundEnv.processingOver()) { + return false + } + + // We're only interested in feature constants defined in PackageManager. + var featureCount = 0 + roundEnv.getElementsAnnotatedWith(SdkConstant::class.java).forEach { + if ( + it.enclosingElement == packageManagerType && + it.getAnnotation(SdkConstant::class.java).value == + SdkConstant.SdkConstantType.FEATURE + ) { + featureCount++ + } + } + + if (featureCount == 0) { + // This is fine, and happens for any environment that doesn't include PackageManager. + return false + } + + val systemFeatureMetadata = + TypeSpec.classBuilder("SystemFeaturesMetadata") + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .addJavadoc("@hide") + .addField( + FieldSpec.builder(Int::class.java, "SDK_FEATURE_COUNT") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + .addJavadoc( + "The number of `@SdkConstant` features defined in PackageManager." + ) + .addJavadoc("@hide") + .initializer("\$L", featureCount) + .build() + ) + .build() + + try { + JavaFile.builder("com.android.internal.pm", systemFeatureMetadata) + .skipJavaLangImports(true) + .build() + .writeTo(processingEnv.filer) + } catch (e: IOException) { + processingEnv.messager.printMessage( + Diagnostic.Kind.ERROR, + "Failed to write file: ${e.message}", + ) + } + + return true + } + + companion object { + private val SDK_CONSTANT_ANNOTATION_NAME = SdkConstant::class.qualifiedName + } +} diff --git a/tools/systemfeatures/tests/src/PackageManager.java b/tools/systemfeatures/tests/src/PackageManager.java index db670482065a..839a9377476d 100644 --- a/tools/systemfeatures/tests/src/PackageManager.java +++ b/tools/systemfeatures/tests/src/PackageManager.java @@ -16,14 +16,33 @@ package android.content.pm; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; + /** Stub for testing */ public class PackageManager { + @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_AUTO = "automotive"; + + @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_PC = "pc"; + + @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_VULKAN = "vulkan"; + + @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_WATCH = "watch"; + + @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_WIFI = "wifi"; + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String FEATURE_INTENT_CATEGORY = "intent_category_with_feature_name_prefix"; + + public static final String FEATURE_NOT_ANNOTATED = "not_annotated"; + + public static final String NOT_FEATURE = "not_feature"; + /** @hide */ public boolean hasSystemFeature(String featureName, int version) { return false; diff --git a/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java b/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java new file mode 100644 index 000000000000..4ffb5b979d75 --- /dev/null +++ b/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java @@ -0,0 +1,36 @@ +/* + * 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.systemfeatures; + +import static com.google.common.truth.Truth.assertThat; + +import com.android.internal.pm.SystemFeaturesMetadata; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class SystemFeaturesMetadataProcessorTest { + + @Test + public void testSdkFeatureCount() { + // See the fake PackageManager definition in this directory. + // It defines 5 annotated features, and any/all other constants should be ignored. + assertThat(SystemFeaturesMetadata.SDK_FEATURE_COUNT).isEqualTo(5); + } +} |