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

Commit 5d992e24 authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Introduce a system feature annotation processor" into main am: 389bf1a8

parents 021fb1b3 389bf1a8
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -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
+110 −0
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.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
    }
}
+19 −0
Original line number Diff line number Diff line
@@ -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;
+36 −0
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.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);
    }
}