Loading tools/systemfeatures/Android.bp +15 −1 Original line number Diff line number Diff line Loading @@ -82,15 +82,23 @@ java_plugin { genrule { name: "systemfeatures-gen-tests-srcs", srcs: [ "tests/data/features-1.xml", "tests/data/features-2.xml", ], cmd: "$(location systemfeatures-gen-tool) com.android.systemfeatures.RwNoFeatures --readonly=false > $(location RwNoFeatures.java) && " + "$(location systemfeatures-gen-tool) com.android.systemfeatures.RoNoFeatures --readonly=true --feature-apis=WATCH > $(location RoNoFeatures.java) && " + "$(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.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)", out: [ "RwNoFeatures.java", "RoNoFeatures.java", "RwFeatures.java", "RoFeatures.java", "RwFeaturesFromXml.java", "RoFeaturesFromXml.java", ], tools: ["systemfeatures-gen-tool"], } Loading @@ -103,6 +111,10 @@ java_test_host { "tests/src/**/*.java", ":systemfeatures-gen-tests-srcs", ], data: [ "tests/data/features-1.xml", "tests/data/features-2.xml", ], test_options: { unit_test: true, }, Loading Loading @@ -130,6 +142,8 @@ genrule { "tests/gen/RoNoFeatures.java.gen", "tests/gen/RwFeatures.java.gen", "tests/gen/RoFeatures.java.gen", "tests/gen/RwFeaturesFromXml.java.gen", "tests/gen/RoFeaturesFromXml.java.gen", ], } Loading tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt +105 −9 Original line number Diff line number Diff line Loading @@ -22,7 +22,13 @@ import com.squareup.javapoet.JavaFile import com.squareup.javapoet.MethodSpec import com.squareup.javapoet.ParameterizedTypeName import com.squareup.javapoet.TypeSpec import java.io.File import javax.lang.model.element.Modifier import javax.xml.parsers.DocumentBuilderFactory import javax.xml.xpath.XPathConstants import javax.xml.xpath.XPathFactory import org.w3c.dom.Element import org.w3c.dom.Node /* * Simple Java code generator that takes as input a list of defined features and generates an Loading Loading @@ -64,6 +70,7 @@ import javax.lang.model.element.Modifier */ object SystemFeaturesGenerator { private const val FEATURE_ARG = "--feature=" private const val FEATURE_XML_FILES_ARG = "--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 @@ -93,6 +100,10 @@ object SystemFeaturesGenerator { println(" runtime passthrough API will be generated, regardless") println(" of the `--readonly` flag. This allows decoupling the") println(" API surface from variations in device feature sets.") 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(" --metadata-only=true|false Whether to simply output metadata about the") println(" generated API surface.") } Loading Loading @@ -139,19 +150,37 @@ object SystemFeaturesGenerator { } ) } arg.startsWith(FEATURE_XML_FILES_ARG) -> { featureArgs.addAll( parseFeatureXmlFiles(arg.substring(FEATURE_XML_FILES_ARG.length).split(",")) ) } else -> outputClassName = ClassName.bestGuess(arg) } } // First load in all of the feature APIs we want to generate. Explicit feature definitions // will then override this set with the appropriate readonly and version value. val features = mutableMapOf<String, FeatureInfo>() // will then override this set with the appropriate readonly and version value. Note that we // use a sorted map to ensure stable codegen outputs given identical inputs. val features = sortedMapOf<String, FeatureInfo>() featureApiArgs.associateByTo( features, { it }, { FeatureInfo(it, version = null, readonly = false) }, ) featureArgs.associateByTo( // Multiple defs for the same feature may be present when aggregating permission files. // To preserve SystemConfig semantics, use the following ordering for insertion priority: // * readonly (build-time overrides runtime) // * unavailable (version == null, overrides available) // * version (higher overrides lower) featureArgs .sortedWith( compareBy<FeatureInfo> { it.readonly } .thenBy { it.version == null } .thenBy { it.version } ) .associateByTo( features, { it.name }, { FeatureInfo(it.name, it.version, it.readonly && readonly) }, Loading @@ -159,7 +188,7 @@ object SystemFeaturesGenerator { outputClassName ?: run { println("Output class name must be provided.") System.err.println("Output class name must be provided.") usage() return } Loading Loading @@ -214,7 +243,7 @@ object SystemFeaturesGenerator { private fun parseFeatureName(name: String): String = when { name.startsWith("android") -> { SystemFeaturesLookup.getDeclaredFeatureVarNameFromValue(name) parseFeatureNameFromValue(name) ?: throw IllegalArgumentException( "Unrecognized Android system feature name: $name" ) Loading @@ -223,6 +252,73 @@ object SystemFeaturesGenerator { else -> "FEATURE_$name" } private fun parseFeatureNameFromValue(name: String): String? = SystemFeaturesLookup.getDeclaredFeatureVarNameFromValue(name) /** * Parses a list of feature permission XML file paths into a list of FeatureInfo definitions. */ private fun parseFeatureXmlFiles(filePaths: Collection<String>): Collection<FeatureInfo> = filePaths.flatMap { try { parseFeatureXmlFile(File(it)) } catch (e: Exception) { throw IllegalArgumentException("Error parsing feature XML file: $it", e) } } /** * Parses a feature permission XML file into a (possibly empty) list of FeatureInfo definitions. */ private fun parseFeatureXmlFile(file: File): Collection<FeatureInfo> { val doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file) doc.documentElement.normalize() val xPath = XPathFactory.newInstance().newXPath() val rootElement = xPath.evaluate("/permissions", doc, XPathConstants.NODE) as? Element ?: xPath.evaluate("/config", doc, XPathConstants.NODE) as? Element if (rootElement == null) { System.err.println("Warning: No <permissions>/<config> elements found in ${file.path}") return emptyList() } return rootElement.childNodes.let { nodeList -> (0 until nodeList.length) .asSequence() .map { nodeList.item(it) } .filter { it.nodeType == Node.ELEMENT_NODE } .map { it as Element } .mapNotNull { element -> when (element.tagName) { "feature" -> parseFeatureElement(element) "unavailable-feature" -> parseUnavailableFeatureElement(element) else -> null } } .toList() } } private fun parseFeatureElement(element: Element): FeatureInfo? { val name = parseFeatureNameFromValue(element.getAttribute("name")) ?: return null return if (element.getAttribute("notLowRam") == "true") { // If a feature is marked as being disabled on low-ram devices (notLowRam==true), we // we cannot finalize the exported feature version or its availability, as we don't // (yet) know whether the target product is low-ram. FeatureInfo(name, version = null, readonly = false) } else { val version = element.getAttribute("version") FeatureInfo(name, version.toIntOrNull() ?: 0, readonly = true) } } private fun parseUnavailableFeatureElement(element: Element): FeatureInfo? { val name = parseFeatureNameFromValue(element.getAttribute("name")) ?: return null return FeatureInfo(name, version = null, readonly = true) } /* * Adds per-feature query methods to the class with the form: * {@code public static boolean hasFeatureX(Context context)}, Loading tools/systemfeatures/tests/data/features-1.xml 0 → 100644 +29 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2025 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. --> <permissions> <!-- Implicit version=0 will be overridden in features-2.xml. --> <feature name="android.hardware.type.embedded" /> <!--- Overridden by the pc <unavailable-feature /> def in features-2.xml. --> <feature name="android.hardware.type.pc" /> <!-- Ignored as it's not a platform-defined feature. --> <feature name="com.arbitrary.feature" /> <!-- Overrides the watch <feature /> def in features-2.xml. --> <unavailable-feature name="android.hardware.type.watch" /> <!-- Exposed in the API but not as a build-time constant (readonly) feature, as we don't know if the device is low-ram. --> <feature name="android.hardware.bluetooth" notLowRam="true" /> </permissions> No newline at end of file tools/systemfeatures/tests/data/features-2.xml 0 → 100644 +28 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2025 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. --> <permissions> <!-- Overrides the embedded <feature /> version from features-1.xml. --> <feature name="android.hardware.type.embedded" version="1"/> <!-- Overridden by the <unavailable-feature /> def in features-1.xml. --> <feature name="android.hardware.type.watch" version="1" /> <!-- Redundant with the wifi <feature /> def in features-1.xml. --> <feature name="android.hardware.wifi" /> <!-- Ignored as it's not a platform-defined feature. --> <feature name="com.arbitrary.feature.2" /> <!-- Overrides the pc <feature /> def from features-1.xml. --> <unavailable-feature name="android.hardware.type.pc" /> </permissions> No newline at end of file tools/systemfeatures/tests/golden/RoFeatures.java.gen +16 −16 Original line number Diff line number Diff line Loading @@ -22,13 +22,12 @@ import com.android.aconfig.annotations.AssumeTrueForR8; */ public final class RoFeatures { /** * Check for FEATURE_WATCH. * Check for FEATURE_AUTO. * * @hide */ @AssumeTrueForR8 public static boolean hasFeatureWatch(Context context) { return true; public static boolean hasFeatureAuto(Context context) { return hasFeatureFallback(context, PackageManager.FEATURE_AUTO); } /** Loading @@ -41,32 +40,33 @@ public final class RoFeatures { } /** * Check for FEATURE_WIFI. * Check for FEATURE_VULKAN. * * @hide */ @AssumeTrueForR8 public static boolean hasFeatureWifi(Context context) { return true; @AssumeFalseForR8 public static boolean hasFeatureVulkan(Context context) { return false; } /** * Check for FEATURE_VULKAN. * Check for FEATURE_WATCH. * * @hide */ @AssumeFalseForR8 public static boolean hasFeatureVulkan(Context context) { return false; @AssumeTrueForR8 public static boolean hasFeatureWatch(Context context) { return true; } /** * Check for FEATURE_AUTO. * Check for FEATURE_WIFI. * * @hide */ public static boolean hasFeatureAuto(Context context) { return hasFeatureFallback(context, PackageManager.FEATURE_AUTO); @AssumeTrueForR8 public static boolean hasFeatureWifi(Context context) { return true; } private static boolean hasFeatureFallback(Context context, String featureName) { Loading @@ -79,9 +79,9 @@ public final class RoFeatures { @Nullable public static Boolean maybeHasFeature(String featureName, int version) { switch (featureName) { case PackageManager.FEATURE_VULKAN: return false; case PackageManager.FEATURE_WATCH: return 1 >= version; case PackageManager.FEATURE_WIFI: return 0 >= version; case PackageManager.FEATURE_VULKAN: return false; default: break; } return null; Loading Loading
tools/systemfeatures/Android.bp +15 −1 Original line number Diff line number Diff line Loading @@ -82,15 +82,23 @@ java_plugin { genrule { name: "systemfeatures-gen-tests-srcs", srcs: [ "tests/data/features-1.xml", "tests/data/features-2.xml", ], cmd: "$(location systemfeatures-gen-tool) com.android.systemfeatures.RwNoFeatures --readonly=false > $(location RwNoFeatures.java) && " + "$(location systemfeatures-gen-tool) com.android.systemfeatures.RoNoFeatures --readonly=true --feature-apis=WATCH > $(location RoNoFeatures.java) && " + "$(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.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)", out: [ "RwNoFeatures.java", "RoNoFeatures.java", "RwFeatures.java", "RoFeatures.java", "RwFeaturesFromXml.java", "RoFeaturesFromXml.java", ], tools: ["systemfeatures-gen-tool"], } Loading @@ -103,6 +111,10 @@ java_test_host { "tests/src/**/*.java", ":systemfeatures-gen-tests-srcs", ], data: [ "tests/data/features-1.xml", "tests/data/features-2.xml", ], test_options: { unit_test: true, }, Loading Loading @@ -130,6 +142,8 @@ genrule { "tests/gen/RoNoFeatures.java.gen", "tests/gen/RwFeatures.java.gen", "tests/gen/RoFeatures.java.gen", "tests/gen/RwFeaturesFromXml.java.gen", "tests/gen/RoFeaturesFromXml.java.gen", ], } Loading
tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt +105 −9 Original line number Diff line number Diff line Loading @@ -22,7 +22,13 @@ import com.squareup.javapoet.JavaFile import com.squareup.javapoet.MethodSpec import com.squareup.javapoet.ParameterizedTypeName import com.squareup.javapoet.TypeSpec import java.io.File import javax.lang.model.element.Modifier import javax.xml.parsers.DocumentBuilderFactory import javax.xml.xpath.XPathConstants import javax.xml.xpath.XPathFactory import org.w3c.dom.Element import org.w3c.dom.Node /* * Simple Java code generator that takes as input a list of defined features and generates an Loading Loading @@ -64,6 +70,7 @@ import javax.lang.model.element.Modifier */ object SystemFeaturesGenerator { private const val FEATURE_ARG = "--feature=" private const val FEATURE_XML_FILES_ARG = "--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 @@ -93,6 +100,10 @@ object SystemFeaturesGenerator { println(" runtime passthrough API will be generated, regardless") println(" of the `--readonly` flag. This allows decoupling the") println(" API surface from variations in device feature sets.") 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(" --metadata-only=true|false Whether to simply output metadata about the") println(" generated API surface.") } Loading Loading @@ -139,19 +150,37 @@ object SystemFeaturesGenerator { } ) } arg.startsWith(FEATURE_XML_FILES_ARG) -> { featureArgs.addAll( parseFeatureXmlFiles(arg.substring(FEATURE_XML_FILES_ARG.length).split(",")) ) } else -> outputClassName = ClassName.bestGuess(arg) } } // First load in all of the feature APIs we want to generate. Explicit feature definitions // will then override this set with the appropriate readonly and version value. val features = mutableMapOf<String, FeatureInfo>() // will then override this set with the appropriate readonly and version value. Note that we // use a sorted map to ensure stable codegen outputs given identical inputs. val features = sortedMapOf<String, FeatureInfo>() featureApiArgs.associateByTo( features, { it }, { FeatureInfo(it, version = null, readonly = false) }, ) featureArgs.associateByTo( // Multiple defs for the same feature may be present when aggregating permission files. // To preserve SystemConfig semantics, use the following ordering for insertion priority: // * readonly (build-time overrides runtime) // * unavailable (version == null, overrides available) // * version (higher overrides lower) featureArgs .sortedWith( compareBy<FeatureInfo> { it.readonly } .thenBy { it.version == null } .thenBy { it.version } ) .associateByTo( features, { it.name }, { FeatureInfo(it.name, it.version, it.readonly && readonly) }, Loading @@ -159,7 +188,7 @@ object SystemFeaturesGenerator { outputClassName ?: run { println("Output class name must be provided.") System.err.println("Output class name must be provided.") usage() return } Loading Loading @@ -214,7 +243,7 @@ object SystemFeaturesGenerator { private fun parseFeatureName(name: String): String = when { name.startsWith("android") -> { SystemFeaturesLookup.getDeclaredFeatureVarNameFromValue(name) parseFeatureNameFromValue(name) ?: throw IllegalArgumentException( "Unrecognized Android system feature name: $name" ) Loading @@ -223,6 +252,73 @@ object SystemFeaturesGenerator { else -> "FEATURE_$name" } private fun parseFeatureNameFromValue(name: String): String? = SystemFeaturesLookup.getDeclaredFeatureVarNameFromValue(name) /** * Parses a list of feature permission XML file paths into a list of FeatureInfo definitions. */ private fun parseFeatureXmlFiles(filePaths: Collection<String>): Collection<FeatureInfo> = filePaths.flatMap { try { parseFeatureXmlFile(File(it)) } catch (e: Exception) { throw IllegalArgumentException("Error parsing feature XML file: $it", e) } } /** * Parses a feature permission XML file into a (possibly empty) list of FeatureInfo definitions. */ private fun parseFeatureXmlFile(file: File): Collection<FeatureInfo> { val doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file) doc.documentElement.normalize() val xPath = XPathFactory.newInstance().newXPath() val rootElement = xPath.evaluate("/permissions", doc, XPathConstants.NODE) as? Element ?: xPath.evaluate("/config", doc, XPathConstants.NODE) as? Element if (rootElement == null) { System.err.println("Warning: No <permissions>/<config> elements found in ${file.path}") return emptyList() } return rootElement.childNodes.let { nodeList -> (0 until nodeList.length) .asSequence() .map { nodeList.item(it) } .filter { it.nodeType == Node.ELEMENT_NODE } .map { it as Element } .mapNotNull { element -> when (element.tagName) { "feature" -> parseFeatureElement(element) "unavailable-feature" -> parseUnavailableFeatureElement(element) else -> null } } .toList() } } private fun parseFeatureElement(element: Element): FeatureInfo? { val name = parseFeatureNameFromValue(element.getAttribute("name")) ?: return null return if (element.getAttribute("notLowRam") == "true") { // If a feature is marked as being disabled on low-ram devices (notLowRam==true), we // we cannot finalize the exported feature version or its availability, as we don't // (yet) know whether the target product is low-ram. FeatureInfo(name, version = null, readonly = false) } else { val version = element.getAttribute("version") FeatureInfo(name, version.toIntOrNull() ?: 0, readonly = true) } } private fun parseUnavailableFeatureElement(element: Element): FeatureInfo? { val name = parseFeatureNameFromValue(element.getAttribute("name")) ?: return null return FeatureInfo(name, version = null, readonly = true) } /* * Adds per-feature query methods to the class with the form: * {@code public static boolean hasFeatureX(Context context)}, Loading
tools/systemfeatures/tests/data/features-1.xml 0 → 100644 +29 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2025 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. --> <permissions> <!-- Implicit version=0 will be overridden in features-2.xml. --> <feature name="android.hardware.type.embedded" /> <!--- Overridden by the pc <unavailable-feature /> def in features-2.xml. --> <feature name="android.hardware.type.pc" /> <!-- Ignored as it's not a platform-defined feature. --> <feature name="com.arbitrary.feature" /> <!-- Overrides the watch <feature /> def in features-2.xml. --> <unavailable-feature name="android.hardware.type.watch" /> <!-- Exposed in the API but not as a build-time constant (readonly) feature, as we don't know if the device is low-ram. --> <feature name="android.hardware.bluetooth" notLowRam="true" /> </permissions> No newline at end of file
tools/systemfeatures/tests/data/features-2.xml 0 → 100644 +28 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2025 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. --> <permissions> <!-- Overrides the embedded <feature /> version from features-1.xml. --> <feature name="android.hardware.type.embedded" version="1"/> <!-- Overridden by the <unavailable-feature /> def in features-1.xml. --> <feature name="android.hardware.type.watch" version="1" /> <!-- Redundant with the wifi <feature /> def in features-1.xml. --> <feature name="android.hardware.wifi" /> <!-- Ignored as it's not a platform-defined feature. --> <feature name="com.arbitrary.feature.2" /> <!-- Overrides the pc <feature /> def from features-1.xml. --> <unavailable-feature name="android.hardware.type.pc" /> </permissions> No newline at end of file
tools/systemfeatures/tests/golden/RoFeatures.java.gen +16 −16 Original line number Diff line number Diff line Loading @@ -22,13 +22,12 @@ import com.android.aconfig.annotations.AssumeTrueForR8; */ public final class RoFeatures { /** * Check for FEATURE_WATCH. * Check for FEATURE_AUTO. * * @hide */ @AssumeTrueForR8 public static boolean hasFeatureWatch(Context context) { return true; public static boolean hasFeatureAuto(Context context) { return hasFeatureFallback(context, PackageManager.FEATURE_AUTO); } /** Loading @@ -41,32 +40,33 @@ public final class RoFeatures { } /** * Check for FEATURE_WIFI. * Check for FEATURE_VULKAN. * * @hide */ @AssumeTrueForR8 public static boolean hasFeatureWifi(Context context) { return true; @AssumeFalseForR8 public static boolean hasFeatureVulkan(Context context) { return false; } /** * Check for FEATURE_VULKAN. * Check for FEATURE_WATCH. * * @hide */ @AssumeFalseForR8 public static boolean hasFeatureVulkan(Context context) { return false; @AssumeTrueForR8 public static boolean hasFeatureWatch(Context context) { return true; } /** * Check for FEATURE_AUTO. * Check for FEATURE_WIFI. * * @hide */ public static boolean hasFeatureAuto(Context context) { return hasFeatureFallback(context, PackageManager.FEATURE_AUTO); @AssumeTrueForR8 public static boolean hasFeatureWifi(Context context) { return true; } private static boolean hasFeatureFallback(Context context, String featureName) { Loading @@ -79,9 +79,9 @@ public final class RoFeatures { @Nullable public static Boolean maybeHasFeature(String featureName, int version) { switch (featureName) { case PackageManager.FEATURE_VULKAN: return false; case PackageManager.FEATURE_WATCH: return 1 >= version; case PackageManager.FEATURE_WIFI: return 0 >= version; case PackageManager.FEATURE_VULKAN: return false; default: break; } return null; Loading