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

Commit 8cb72b51 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Add SystemFeaturesMetadata.maybeGetSdkFeatureIndex" into main

parents 75f85b43 04c0e02c
Loading
Loading
Loading
Loading
+73 −23
Original line number Diff line number Diff line
@@ -17,8 +17,10 @@
package com.android.systemfeatures

import android.annotation.SdkConstant
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.FieldSpec
import com.squareup.javapoet.JavaFile
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.TypeSpec
import java.io.IOException
import javax.annotation.processing.AbstractProcessor
@@ -27,6 +29,7 @@ import javax.annotation.processing.RoundEnvironment
import javax.lang.model.SourceVersion
import javax.lang.model.element.Modifier
import javax.lang.model.element.TypeElement
import javax.lang.model.element.VariableElement
import javax.tools.Diagnostic

/*
@@ -35,7 +38,16 @@ import javax.tools.Diagnostic
 * <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.
 * invocations are ignored. The generated API is as follows:
 *
 * <pre>
 * package android.content.pm;
 * public final class SystemFeaturesMetadata {
 *     public static final int SDK_FEATURE_COUNT;
 *     // @return [0, SDK_FEATURE_COUNT) if an SDK-defined system feature, -1 otherwise.
 *     public static int maybeGetSdkFeatureIndex(String featureName);
 * }
 * </pre>
 */
class SystemFeaturesMetadataProcessor : AbstractProcessor() {

@@ -56,19 +68,31 @@ class SystemFeaturesMetadataProcessor : AbstractProcessor() {
            return false
        }

        // We're only interested in feature constants defined in PackageManager.
        var featureCount = 0
        roundEnv.getElementsAnnotatedWith(SdkConstant::class.java).forEach {
            if (
        // Collect all FEATURE-annotated fields from PackageManager, and
        //  1) Use the field values to de-duplicate, as there can be multiple FEATURE_* fields that
        //     map to the same feature string name value.
        //  2) Ensure they're sorted to ensure consistency and determinism between builds.
        val featureVarNames =
            roundEnv
                .getElementsAnnotatedWith(SdkConstant::class.java)
                .asSequence()
                .filter {
                    it.enclosingElement == packageManagerType &&
                        it.getAnnotation(SdkConstant::class.java).value ==
                            SdkConstant.SdkConstantType.FEATURE
            ) {
                featureCount++
                }
                .mapNotNull { element ->
                    (element as? VariableElement)?.let { varElement ->
                        varElement.getConstantValue()?.toString() to
                            varElement.simpleName.toString()
                    }
                }
                .toMap()
                .values
                .sorted()
                .toList()

        if (featureCount == 0) {
        if (featureVarNames.isEmpty()) {
            // This is fine, and happens for any environment that doesn't include PackageManager.
            return false
        }
@@ -77,16 +101,8 @@ class SystemFeaturesMetadataProcessor : AbstractProcessor() {
            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()
                )
                .addField(buildFeatureCount(featureVarNames))
                .addMethod(buildFeatureIndexLookup(featureVarNames))
                .build()

        try {
@@ -104,7 +120,41 @@ class SystemFeaturesMetadataProcessor : AbstractProcessor() {
        return true
    }

    private fun buildFeatureCount(featureVarNames: Collection<String>): FieldSpec {
        return FieldSpec.builder(Int::class.java, "SDK_FEATURE_COUNT")
            .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
            .addJavadoc(
                "# of {@link android.annotation.SdkConstant}` features defined in PackageManager."
            )
            .addJavadoc("\n\n@hide")
            .initializer("\$L", featureVarNames.size)
            .build()
    }

    private fun buildFeatureIndexLookup(featureVarNames: Collection<String>): MethodSpec {
        val methodBuilder =
            MethodSpec.methodBuilder("maybeGetSdkFeatureIndex")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .addJavadoc("@return an index in [0, SDK_FEATURE_COUNT) for features defined ")
                .addJavadoc("in PackageManager, else -1.")
                .addJavadoc("\n\n@hide")
                .returns(Int::class.java)
                .addParameter(String::class.java, "featureName")
        methodBuilder.beginControlFlow("switch (featureName)")
        featureVarNames.forEachIndexed { index, featureVarName ->
            methodBuilder
                .addCode("case \$T.\$N: ", PACKAGEMANAGER_CLASS, featureVarName)
                .addStatement("return \$L", index)
        }
        methodBuilder
            .addCode("default: ")
            .addStatement("return -1")
            .endControlFlow()
        return methodBuilder.build()
    }

    companion object {
        private val SDK_CONSTANT_ANNOTATION_NAME = SdkConstant::class.qualifiedName
        private val PACKAGEMANAGER_CLASS = ClassName.get("android.content.pm", "PackageManager")
    }
}
+19 −0
Original line number Diff line number Diff line
@@ -16,10 +16,16 @@

package com.android.systemfeatures;

import static com.android.internal.pm.SystemFeaturesMetadata.maybeGetSdkFeatureIndex;

import static com.google.common.truth.Truth.assertThat;

import android.content.pm.PackageManager;

import com.android.internal.pm.SystemFeaturesMetadata;

import com.google.common.collect.Range;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -33,4 +39,17 @@ public class SystemFeaturesMetadataProcessorTest {
        // It defines 5 annotated features, and any/all other constants should be ignored.
        assertThat(SystemFeaturesMetadata.SDK_FEATURE_COUNT).isEqualTo(5);
    }

    @Test
    public void testSdkFeatureIndex() {
        // Only SDK-defined features return valid indices.
        final Range validIndexRange = Range.closedOpen(0, SystemFeaturesMetadata.SDK_FEATURE_COUNT);
        assertThat(maybeGetSdkFeatureIndex(PackageManager.FEATURE_PC)).isIn(validIndexRange);
        assertThat(maybeGetSdkFeatureIndex(PackageManager.FEATURE_VULKAN)).isIn(validIndexRange);
        assertThat(maybeGetSdkFeatureIndex(PackageManager.FEATURE_NOT_ANNOTATED)).isEqualTo(-1);
        assertThat(maybeGetSdkFeatureIndex(PackageManager.NOT_FEATURE)).isEqualTo(-1);
        assertThat(maybeGetSdkFeatureIndex("foo")).isEqualTo(-1);
        assertThat(maybeGetSdkFeatureIndex("0")).isEqualTo(-1);
        assertThat(maybeGetSdkFeatureIndex("")).isEqualTo(-1);
    }
}