diff options
7 files changed, 152 insertions, 1 deletions
diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt index dc0d8a39dbc1..cba521e639cb 100644 --- a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt +++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt @@ -20,7 +20,10 @@ import com.google.common.base.CaseFormat import com.squareup.javapoet.ClassName import com.squareup.javapoet.JavaFile import com.squareup.javapoet.MethodSpec +import com.squareup.javapoet.ParameterizedTypeName import com.squareup.javapoet.TypeSpec +import java.util.HashMap +import java.util.Map import javax.lang.model.element.Modifier /* @@ -49,6 +52,7 @@ import javax.lang.model.element.Modifier * public static boolean hasFeatureAutomotive(Context context); * public static boolean hasFeatureLeanback(Context context); * public static Boolean maybeHasFeature(String feature, int version); + * public static ArrayMap<String, FeatureInfo> getCompileTimeAvailableFeatures(); * } * </pre> */ @@ -58,6 +62,7 @@ object SystemFeaturesGenerator { private const val READONLY_ARG = "--readonly=" private val PACKAGEMANAGER_CLASS = ClassName.get("android.content.pm", "PackageManager") private val CONTEXT_CLASS = ClassName.get("android.content", "Context") + private val FEATUREINFO_CLASS = ClassName.get("android.content.pm", "FeatureInfo") private val ASSUME_TRUE_CLASS = ClassName.get("com.android.aconfig.annotations", "AssumeTrueForR8") private val ASSUME_FALSE_CLASS = @@ -142,6 +147,7 @@ object SystemFeaturesGenerator { addFeatureMethodsToClass(classBuilder, features.values) addMaybeFeatureMethodToClass(classBuilder, features.values) + addGetFeaturesMethodToClass(classBuilder, features.values) // TODO(b/203143243): Add validation of build vs runtime values to ensure consistency. JavaFile.builder(outputClassName.packageName(), classBuilder.build()) @@ -225,7 +231,7 @@ object SystemFeaturesGenerator { /* * Adds a generic query method to the class with the form: {@code public static boolean * maybeHasFeature(String featureName, int version)}, returning null if the feature version is - * undefined or not readonly. + * undefined or not (compile-time) readonly. * * This method is useful for internal usage within the framework, e.g., from the implementation * of {@link android.content.pm.PackageManager#hasSystemFeature(Context)}, when we may only @@ -274,5 +280,41 @@ object SystemFeaturesGenerator { builder.addMethod(methodBuilder.build()) } + /* + * Adds a method to get all compile-time enabled features. + * + * This method is useful for internal usage within the framework to augment + * any system features that are parsed from the various partitions. + */ + private fun addGetFeaturesMethodToClass( + builder: TypeSpec.Builder, + features: Collection<FeatureInfo>, + ) { + val methodBuilder = + MethodSpec.methodBuilder("getCompileTimeAvailableFeatures") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .addAnnotation(ClassName.get("android.annotation", "NonNull")) + .addJavadoc("Gets features marked as available at compile-time, keyed by name." + + "\n\n@hide") + .returns(ParameterizedTypeName.get( + ClassName.get(Map::class.java), + ClassName.get(String::class.java), + FEATUREINFO_CLASS)) + + val availableFeatures = features.filter { it.readonly && it.version != null } + methodBuilder.addStatement("Map<String, FeatureInfo> features = new \$T<>(\$L)", + HashMap::class.java, availableFeatures.size) + if (!availableFeatures.isEmpty()) { + methodBuilder.addStatement("FeatureInfo fi = new FeatureInfo()") + } + for (feature in availableFeatures) { + methodBuilder.addStatement("fi.name = \$T.\$N", PACKAGEMANAGER_CLASS, feature.name) + methodBuilder.addStatement("fi.version = \$L", feature.version) + methodBuilder.addStatement("features.put(fi.name, new FeatureInfo(fi))") + } + methodBuilder.addStatement("return features") + builder.addMethod(methodBuilder.build()) + } + private data class FeatureInfo(val name: String, val version: Int?, val readonly: Boolean) } diff --git a/tools/systemfeatures/tests/golden/RoFeatures.java.gen b/tools/systemfeatures/tests/golden/RoFeatures.java.gen index dfc2937dc41d..edbfc4237547 100644 --- a/tools/systemfeatures/tests/golden/RoFeatures.java.gen +++ b/tools/systemfeatures/tests/golden/RoFeatures.java.gen @@ -8,11 +8,15 @@ // --feature-apis=WATCH,PC 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 com.android.aconfig.annotations.AssumeFalseForR8; import com.android.aconfig.annotations.AssumeTrueForR8; +import java.util.HashMap; +import java.util.Map; /** * @hide @@ -83,4 +87,22 @@ public final class RoFeatures { } return null; } + + /** + * Gets features marked as available at compile-time, keyed by name. + * + * @hide + */ + @NonNull + public static Map<String, FeatureInfo> getCompileTimeAvailableFeatures() { + Map<String, FeatureInfo> features = new HashMap<>(2); + FeatureInfo fi = new FeatureInfo(); + fi.name = PackageManager.FEATURE_WATCH; + fi.version = 1; + features.put(fi.name, new FeatureInfo(fi)); + fi.name = PackageManager.FEATURE_WIFI; + fi.version = 0; + features.put(fi.name, new FeatureInfo(fi)); + return features; + } } diff --git a/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen b/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen index 59c5b4e8fecb..bf7a00679fa6 100644 --- a/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen +++ b/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen @@ -4,9 +4,13 @@ // --feature-apis=WATCH 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 java.util.HashMap; +import java.util.Map; /** * @hide @@ -32,4 +36,15 @@ public final class RoNoFeatures { public static Boolean maybeHasFeature(String featureName, int version) { return null; } + + /** + * Gets features marked as available at compile-time, keyed by name. + * + * @hide + */ + @NonNull + public static Map<String, FeatureInfo> getCompileTimeAvailableFeatures() { + Map<String, FeatureInfo> features = new HashMap<>(0); + return features; + } } diff --git a/tools/systemfeatures/tests/golden/RwFeatures.java.gen b/tools/systemfeatures/tests/golden/RwFeatures.java.gen index 89097fbfd180..b20b228f9814 100644 --- a/tools/systemfeatures/tests/golden/RwFeatures.java.gen +++ b/tools/systemfeatures/tests/golden/RwFeatures.java.gen @@ -7,9 +7,13 @@ // --feature=AUTO: 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 java.util.HashMap; +import java.util.Map; /** * @hide @@ -62,4 +66,15 @@ public final class RwFeatures { public static Boolean maybeHasFeature(String featureName, int version) { return null; } + + /** + * Gets features marked as available at compile-time, keyed by name. + * + * @hide + */ + @NonNull + public static Map<String, FeatureInfo> getCompileTimeAvailableFeatures() { + Map<String, FeatureInfo> features = new HashMap<>(0); + return features; + } } diff --git a/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen b/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen index 2111d564f28d..d91f5b62d8d4 100644 --- a/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen +++ b/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen @@ -3,8 +3,12 @@ // --readonly=false package com.android.systemfeatures; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.content.pm.FeatureInfo; +import java.util.HashMap; +import java.util.Map; /** * @hide @@ -21,4 +25,15 @@ public final class RwNoFeatures { public static Boolean maybeHasFeature(String featureName, int version) { return null; } + + /** + * Gets features marked as available at compile-time, keyed by name. + * + * @hide + */ + @NonNull + public static Map<String, FeatureInfo> getCompileTimeAvailableFeatures() { + Map<String, FeatureInfo> features = new HashMap<>(0); + return features; + } } diff --git a/tools/systemfeatures/tests/src/FeatureInfo.java b/tools/systemfeatures/tests/src/FeatureInfo.java new file mode 100644 index 000000000000..9d57edc64ca5 --- /dev/null +++ b/tools/systemfeatures/tests/src/FeatureInfo.java @@ -0,0 +1,30 @@ +/* + * 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 android.content.pm; + +/** Stub for testing */ +public final class FeatureInfo { + public String name; + public int version; + + public FeatureInfo() {} + + public FeatureInfo(FeatureInfo orig) { + name = orig.name; + version = orig.version; + } +} diff --git a/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java index c3a23cbd8e48..39f8fc44fe23 100644 --- a/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java +++ b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java @@ -25,6 +25,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; +import android.content.pm.FeatureInfo; import android.content.pm.PackageManager; import org.junit.Before; @@ -36,6 +37,8 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import java.util.Map; + @RunWith(JUnit4.class) public class SystemFeaturesGeneratorTest { @@ -57,6 +60,7 @@ public class SystemFeaturesGeneratorTest { assertThat(RwNoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isNull(); assertThat(RwNoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull(); assertThat(RwNoFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull(); + assertThat(RwNoFeatures.getCompileTimeAvailableFeatures()).isEmpty(); } @Test @@ -68,6 +72,7 @@ public class SystemFeaturesGeneratorTest { assertThat(RoNoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isNull(); assertThat(RoNoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull(); assertThat(RoNoFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull(); + assertThat(RoNoFeatures.getCompileTimeAvailableFeatures()).isEmpty(); // Also ensure we fall back to the PackageManager for feature APIs without an accompanying // versioned feature definition. @@ -101,6 +106,7 @@ public class SystemFeaturesGeneratorTest { assertThat(RwFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isNull(); assertThat(RwFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull(); assertThat(RwFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull(); + assertThat(RwFeatures.getCompileTimeAvailableFeatures()).isEmpty(); } @Test @@ -156,5 +162,11 @@ public class SystemFeaturesGeneratorTest { assertThat(RoFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull(); assertThat(RoFeatures.maybeHasFeature("com.arbitrary.feature", 100)).isNull(); assertThat(RoFeatures.maybeHasFeature("", 0)).isNull(); + + Map<String, FeatureInfo> compiledFeatures = RoFeatures.getCompileTimeAvailableFeatures(); + assertThat(compiledFeatures.keySet()) + .containsExactly(PackageManager.FEATURE_WATCH, PackageManager.FEATURE_WIFI); + assertThat(compiledFeatures.get(PackageManager.FEATURE_WATCH).version).isEqualTo(1); + assertThat(compiledFeatures.get(PackageManager.FEATURE_WIFI).version).isEqualTo(0); } } |