summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tools/systemfeatures/Android.bp8
-rw-r--r--tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesMetadataProcessor.kt110
-rw-r--r--tools/systemfeatures/tests/src/PackageManager.java19
-rw-r--r--tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java36
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);
+ }
+}