Loading tools/systemfeatures/Android.bp +4 −1 Original line number Diff line number Diff line Loading @@ -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", Loading @@ -99,6 +100,7 @@ genrule { "RoFeatures.java", "RwFeaturesFromXml.java", "RoFeaturesFromXml.java", "RoUnavailableFeaturesFromXml.java", ], tools: ["systemfeatures-gen-tool"], } Loading Loading @@ -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", ], } Loading tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt +30 −7 Original line number Diff line number Diff line Loading @@ -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=" Loading Loading @@ -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.") } Loading Loading @@ -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) } } Loading Loading @@ -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) } Loading @@ -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() Loading @@ -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 } } Loading tools/systemfeatures/tests/golden/RoUnavailableFeaturesFromXml.java.gen 0 → 100644 +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; } } tools/systemfeatures/tests/src/SystemFeaturesGeneratorApiTest.java +10 −0 Original line number Diff line number Diff line Loading @@ -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()); } } tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java +33 −0 Original line number Diff line number Diff line Loading @@ -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(); } } Loading
tools/systemfeatures/Android.bp +4 −1 Original line number Diff line number Diff line Loading @@ -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", Loading @@ -99,6 +100,7 @@ genrule { "RoFeatures.java", "RwFeaturesFromXml.java", "RoFeaturesFromXml.java", "RoUnavailableFeaturesFromXml.java", ], tools: ["systemfeatures-gen-tool"], } Loading Loading @@ -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", ], } Loading
tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt +30 −7 Original line number Diff line number Diff line Loading @@ -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=" Loading Loading @@ -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.") } Loading Loading @@ -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) } } Loading Loading @@ -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) } Loading @@ -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() Loading @@ -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 } } Loading
tools/systemfeatures/tests/golden/RoUnavailableFeaturesFromXml.java.gen 0 → 100644 +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; } }
tools/systemfeatures/tests/src/SystemFeaturesGeneratorApiTest.java +10 −0 Original line number Diff line number Diff line Loading @@ -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()); } }
tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java +33 −0 Original line number Diff line number Diff line Loading @@ -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(); } }