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

Commit 080d7867 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add system feature codegen for <unavailable-feature /> defs only" into main

parents 897ff551 ad9fa43d
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -91,7 +91,8 @@ genrule {
        "$(location systemfeatures-gen-tool) com.android.systemfeatures.RwFeatures --readonly=false --feature=WATCH:1 --feature=WIFI:0 --feature=VULKAN:UNAVAILABLE --feature=AUTO: > $(location RwFeatures.java) && " +
        "$(location systemfeatures-gen-tool) com.android.systemfeatures.RoFeatures --readonly=true --feature=WATCH:1 --feature=WIFI:0 --feature=VULKAN:UNAVAILABLE --feature=AUTO: --feature-apis=WATCH,PC > $(location RoFeatures.java) && " +
        "$(location systemfeatures-gen-tool) com.android.systemfeatures.RwFeaturesFromXml --readonly=false --feature-xml-files=$(location tests/data/features-1.xml),$(location tests/data/features-2.xml) > $(location RwFeaturesFromXml.java) && " +
        "$(location systemfeatures-gen-tool) com.android.systemfeatures.RoFeaturesFromXml --readonly=true --feature-xml-files=$(location tests/data/features-1.xml),$(location tests/data/features-2.xml) > $(location RoFeaturesFromXml.java)",
        "$(location systemfeatures-gen-tool) com.android.systemfeatures.RoFeaturesFromXml --readonly=true --feature-xml-files=$(location tests/data/features-1.xml),$(location tests/data/features-2.xml) > $(location RoFeaturesFromXml.java) && " +
        "$(location systemfeatures-gen-tool) com.android.systemfeatures.RoUnavailableFeaturesFromXml --readonly=true --unavailable-feature-xml-files=$(location tests/data/features-1.xml),$(location tests/data/features-2.xml) > $(location RoUnavailableFeaturesFromXml.java)",
    out: [
        "RwNoFeatures.java",
        "RoNoFeatures.java",
@@ -99,6 +100,7 @@ genrule {
        "RoFeatures.java",
        "RwFeaturesFromXml.java",
        "RoFeaturesFromXml.java",
        "RoUnavailableFeaturesFromXml.java",
    ],
    tools: ["systemfeatures-gen-tool"],
}
@@ -144,6 +146,7 @@ genrule {
        "tests/gen/RoFeatures.java.gen",
        "tests/gen/RwFeaturesFromXml.java.gen",
        "tests/gen/RoFeaturesFromXml.java.gen",
        "tests/gen/RoUnavailableFeaturesFromXml.java.gen",
    ],
}

+30 −7
Original line number Diff line number Diff line
@@ -71,6 +71,7 @@ import org.w3c.dom.Node
object SystemFeaturesGenerator {
    private const val FEATURE_ARG = "--feature="
    private const val FEATURE_XML_FILES_ARG = "--feature-xml-files="
    private const val UNAVAILABLE_FEATURE_XML_FILES_ARG = "--unavailable-feature-xml-files="
    private const val FEATURE_APIS_ARG = "--feature-apis="
    private const val READONLY_ARG = "--readonly="
    private const val METADATA_ONLY_ARG = "--metadata-only="
@@ -103,7 +104,13 @@ object SystemFeaturesGenerator {
        println("  --feature-xml-files=\$XML_FILE_1,\$XML_FILE_2")
        println("                           A comma-separated list of XML permission feature files")
        println("                           to parse and add to the generated query APIs. The file")
        println("                           format matches that used by SystemConfig parsing.")
        println("                           format matches that used by SystemConfig parsing. This")
        println("                           parses <feature /> and <unavailable-feature /> defs.")
        println("  --unavailable-feature-xml-files=\$XML_FILE_1,\$XML_FILE_2")
        println("                           A comma-separated list of XML permission feature files")
        println("                           to parse and add to the generated query APIs. The file")
        println("                           format matches that used by SystemConfig parsing. This")
        println("                           parses *only* <unavailable-feature /> defs.")
        println("  --metadata-only=true|false Whether to simply output metadata about the")
        println("                             generated API surface.")
    }
@@ -155,6 +162,14 @@ object SystemFeaturesGenerator {
                        parseFeatureXmlFiles(arg.substring(FEATURE_XML_FILES_ARG.length).split(","))
                    )
                }
                arg.startsWith(UNAVAILABLE_FEATURE_XML_FILES_ARG) -> {
                    featureArgs.addAll(
                        parseFeatureXmlFiles(
                            arg.substring(UNAVAILABLE_FEATURE_XML_FILES_ARG.length).split(","),
                            parseOnlyUnavailableFeatures = true,
                        )
                    )
                }
                else -> outputClassName = ClassName.bestGuess(arg)
            }
        }
@@ -259,10 +274,13 @@ object SystemFeaturesGenerator {
    /**
     * Parses a list of feature permission XML file paths into a list of FeatureInfo definitions.
     */
    private fun parseFeatureXmlFiles(filePaths: Collection<String>): Collection<FeatureInfo> =
    private fun parseFeatureXmlFiles(
        filePaths: Collection<String>,
        parseOnlyUnavailableFeatures: Boolean = false,
    ): Collection<FeatureInfo> =
        filePaths.flatMap {
            try {
                parseFeatureXmlFile(File(it))
                parseFeatureXmlFile(File(it), parseOnlyUnavailableFeatures)
            } catch (e: Exception) {
                throw IllegalArgumentException("Error parsing feature XML file: $it", e)
            }
@@ -271,7 +289,10 @@ object SystemFeaturesGenerator {
    /**
     * Parses a feature permission XML file into a (possibly empty) list of FeatureInfo definitions.
     */
    private fun parseFeatureXmlFile(file: File): Collection<FeatureInfo> {
    private fun parseFeatureXmlFile(
        file: File,
        parseOnlyUnavailableFeatures: Boolean = false,
    ): Collection<FeatureInfo> {
        val doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file)
        doc.documentElement.normalize()

@@ -291,9 +312,11 @@ object SystemFeaturesGenerator {
                .filter { it.nodeType == Node.ELEMENT_NODE }
                .map { it as Element }
                .mapNotNull { element ->
                    when (element.tagName) {
                        "feature" -> parseFeatureElement(element)
                        "unavailable-feature" -> parseUnavailableFeatureElement(element)
                    when {
                        element.tagName == "unavailable-feature" ->
                            parseUnavailableFeatureElement(element)
                        !parseOnlyUnavailableFeatures && element.tagName == "feature" ->
                            parseFeatureElement(element)
                        else -> null
                    }
                }
+66 −0
Original line number Diff line number Diff line
// This file is auto-generated. DO NOT MODIFY.
// Args: com.android.systemfeatures.RoUnavailableFeaturesFromXml \
//            --readonly=true \
//            --unavailable-feature-xml-files=frameworks/base/tools/systemfeatures/tests/data/features-1.xml,frameworks/base/tools/systemfeatures/tests/data/features-2.xml
package com.android.systemfeatures;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager;
import android.util.ArrayMap;
import com.android.aconfig.annotations.AssumeFalseForR8;

/**
 * @hide
 */
public final class RoUnavailableFeaturesFromXml {
    /**
     * Check for FEATURE_PC.
     *
     * @hide
     */
    @AssumeFalseForR8
    public static boolean hasFeaturePc(Context context) {
        return false;
    }

    /**
     * Check for FEATURE_WATCH.
     *
     * @hide
     */
    @AssumeFalseForR8
    public static boolean hasFeatureWatch(Context context) {
        return false;
    }

    private static boolean hasFeatureFallback(Context context, String featureName) {
        return context.getPackageManager().hasSystemFeature(featureName);
    }

    /**
     * @hide
     */
    @Nullable
    public static Boolean maybeHasFeature(String featureName, int version) {
        switch (featureName) {
            case PackageManager.FEATURE_PC: return false;
            case PackageManager.FEATURE_WATCH: return false;
            default: break;
        }
        return null;
    }

    /**
     * Gets features marked as available at compile-time, keyed by name.
     *
     * @hide
     */
    @NonNull
    public static ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures() {
        ArrayMap<String, FeatureInfo> features = new ArrayMap<>(0);
        return features;
    }
}
+10 −0
Original line number Diff line number Diff line
@@ -108,4 +108,14 @@ public class SystemFeaturesGeneratorApiTest {
        SystemFeaturesGenerator.generate(args, mOut);
        verify(mOut, never()).append(any());
    }

    @Test
    public void testUnavailableFeatureXmlFiles() throws IOException {
        final String[] args = new String[] {
            "com.foo.Features",
            "--unavailable-feature-xml-files=tests/data/features-1.xml,tests/data/features-2.xml",
        };
        SystemFeaturesGenerator.generate(args, mOut);
        verify(mOut, atLeastOnce()).append(any());
    }
}
+33 −0
Original line number Diff line number Diff line
@@ -252,4 +252,37 @@ public class SystemFeaturesGeneratorTest {
        assertThat(compiledFeatures.get(PackageManager.FEATURE_EMBEDDED).version).isEqualTo(1);
        assertThat(compiledFeatures.get(PackageManager.FEATURE_WIFI).version).isEqualTo(0);
    }

    @Test
    public void testReadonlyWithUnavailableFeaturesFromXml() {
        // Always use the build-time feature version for explicit feature queries for unavailable
        // features, never falling back to the runtime query.
        assertThat(RoUnavailableFeaturesFromXml.hasFeaturePc(mContext)).isFalse();
        assertThat(RoUnavailableFeaturesFromXml.hasFeatureWatch(mContext)).isFalse();
        verify(mPackageManager, never()).hasSystemFeature(anyString(), anyInt());

        // When parsing only unavailable feature types from XML, conditional queries should reflect:
        //  * Disabled if the feature was *ever* declared w/ <unavailable-feature />
        //  * Unknown otherwise.

        // <unavailable-feature />
        assertThat(RoUnavailableFeaturesFromXml.maybeHasFeature(PackageManager.FEATURE_PC, 0))
                .isFalse();
        assertThat(RoUnavailableFeaturesFromXml.maybeHasFeature(PackageManager.FEATURE_WATCH, 0))
                .isFalse();

        // For other feature types, conditional queries should report null (unknown).
        assertThat(
                        RoUnavailableFeaturesFromXml.maybeHasFeature(
                                PackageManager.FEATURE_BLUETOOTH, 0))
                .isNull();
        assertThat(RoUnavailableFeaturesFromXml.maybeHasFeature(PackageManager.FEATURE_EMBEDDED, 0))
                .isNull();
        assertThat(RoUnavailableFeaturesFromXml.maybeHasFeature(PackageManager.FEATURE_WIFI, 0))
                .isNull();
        assertThat(RoUnavailableFeaturesFromXml.maybeHasFeature("com.arbitrary.feature", 0))
                .isNull();
        assertThat(RoUnavailableFeaturesFromXml.maybeHasFeature("", 0)).isNull();
        assertThat(RoUnavailableFeaturesFromXml.getReadOnlySystemEnabledFeatures()).isEmpty();
    }
}