Create ErrorProne lint rule for RoSystemFeatures usage
Create a basic ErrorProne rule that can be used for linting and
in-place updates of calls to system feature queries. A similar
Android Lint rule will be used after work that would enable in-place
updates for both Java/Kotlin sources, but for now an ErrorProne rule
handles most of the impactful callsites.
A follow-up change will add this lint rule to system_server targets
after performing a batch migration update.
Bug: 203143243
Test: atest systemfeatures-errorprone-tests
Flag: build.RELEASE_USE_SYSTEM_FEATURE_BUILD_FLAGS
Change-Id: I8a75edd3ef282a8f35e5d9555ecf3cf849fd3de0
diff --git a/tools/systemfeatures/Android.bp b/tools/systemfeatures/Android.bp
index 2ebede3..87ea5db 100644
--- a/tools/systemfeatures/Android.bp
+++ b/tools/systemfeatures/Android.bp
@@ -100,3 +100,72 @@
unit_test: true,
},
}
+
+java_library_host {
+ name: "systemfeatures-errorprone-lib",
+ srcs: [
+ ":systemfeatures-gen-metadata-srcs",
+ "errorprone/java/**/*.java",
+ ],
+ static_libs: [
+ "//external/error_prone:error_prone_core",
+ "guava",
+ "jsr305",
+ ],
+ libs: [
+ "//external/auto:auto_service_annotations",
+ ],
+ javacflags: [
+ // These exports are needed because this errorprone plugin access some private classes
+ // of the java compiler.
+ "--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
+ "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
+ "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+ ],
+ plugins: [
+ "//external/auto:auto_service_plugin",
+ ],
+}
+
+java_plugin {
+ name: "systemfeatures-errorprone",
+ static_libs: ["systemfeatures-errorprone-lib"],
+}
+
+java_test_host {
+ name: "systemfeatures-errorprone-tests",
+ srcs: [
+ "errorprone/tests/java/**/*.java",
+ ],
+ java_resource_dirs: ["tests/src"],
+ java_resources: [
+ ":systemfeatures-errorprone-tests-data",
+ ],
+ static_libs: [
+ "compile-testing-prebuilt",
+ "error_prone_test_helpers",
+ "framework-annotations-lib",
+ "hamcrest",
+ "hamcrest-library",
+ "junit",
+ "systemfeatures-errorprone-lib",
+ "truth",
+ ],
+ test_options: {
+ unit_test: true,
+ },
+}
+
+java_system_features_srcs {
+ name: "systemfeatures-gen-metadata-srcs",
+ full_class_name: "com.android.systemfeatures.RoSystemFeaturesMetadata",
+ metadata_only: true,
+ visibility: ["//visibility:private"],
+}
+
+filegroup {
+ name: "systemfeatures-errorprone-tests-data",
+ path: "tests/src",
+ srcs: ["tests/src/android/**/*.java"],
+ visibility: ["//visibility:private"],
+}
diff --git a/tools/systemfeatures/README.md b/tools/systemfeatures/README.md
index 5836f81..b1fec1a 100644
--- a/tools/systemfeatures/README.md
+++ b/tools/systemfeatures/README.md
@@ -4,8 +4,110 @@
System features exposed from `PackageManager` are defined and aggregated as
`<feature>` xml attributes across various partitions, and are currently queried
-at runtime through the framework. This directory contains tooling that will
-support *build-time* queries of select system features, enabling optimizations
+at runtime through the framework. This directory contains tooling that supports
+*build-time* queries of select system features, enabling optimizations
like code stripping and conditionally dependencies when so configured.
-### TODO(b/203143243): Expand readme after landing codegen.
+### System Feature Codegen
+
+As not all system features can be fully specified or defined at build time (e.g.
+updatable partitisions and apex modules can change/remove such features), we
+use a conditional, build flag approach that allows a given device to customize
+the subset of build-time defined system features that are immutable and cannot
+be updated.
+
+#### Build Flags
+
+System features that can be fixed at build-time are declared in a common
+location, `build/release/flag_declarations/`. These have the form
+`RELEASE_SYSTEM_FEATURE_${X}`, where `${X}` corresponds to a feature defined in
+`PackageManager`, e.g., `TELEVISION` or `WATCH`.
+
+Build flag values can then be defined per device (or form factor), where such
+values either indicate the existence/version of the system feature, or that the
+feature is unavailable, e.g., for TV, we could define these build flag values:
+```
+name: "RELEASE_SYSTEM_FEATURE_TELEVISION"
+value: {
+ string_value: "0" # Feature version = 0
+}
+```
+```
+name: "RELEASE_SYSTEM_FEATURE_WATCH"
+value: {
+ string_value: "UNAVAILABLE"
+}
+```
+
+See also [SystemFeaturesGenerator](src/com/android/systemfeatures/SystemFeaturesGenerator.kt)
+for more details.
+
+#### Runtime Queries
+
+Each declared build flag system feature is routed into codegen, generating a
+getter API in the internal class, `com.android.internal.pm.RoSystemFeatures`:
+```
+class RoSystemFeatures {
+ ...
+ public static boolean hasFeatureX(Context context);
+ ...
+}
+```
+By default, these queries simply fall back to the usual
+`PackageManager.hasSystemFeature(...)` runtime queries. However, if a device
+defines these features via build flags, the generated code will add annotations
+indicating fixed value for this query, and adjust the generated code to return
+the value directly. This in turn enables build-time stripping and optimization.
+
+> **_NOTE:_** Any build-time defined system features will also be implicitly
+used to accelerate calls to `PackageManager.hasSystemFeature(...)` for the
+feature, avoiding binder calls when possible.
+
+#### Lint
+
+A new `ErrorProne` rule is introduced to assist with migration and maintenance
+of codegen APIs for build-time defined system features. This is defined in the
+`systemfeatures-errorprone` build rule, which can be added to any Java target's
+`plugins` list.
+
+// TODO(b/203143243): Add plugin to key system targets after initial migration.
+
+1) Add the plugin dependency to a given `${TARGET}`:
+```
+java_library {
+ name: "${TARGET}",
+ plugins: ["systemfeatures-errorprone"],
+}
+```
+2) Run locally:
+```
+RUN_ERROR_PRONE=true m ${TARGET}
+```
+3) (Optional) Update the target rule to generate in-place patch files:
+```
+java_library {
+ name: "${TARGET}",
+ plugins: ["systemfeatures-errorprone"],
+ // DO NOT SUBMIT: GENERATE IN-PLACE PATCH FILES
+ errorprone: {
+ javacflags: [
+ "-XepPatchChecks:RoSystemFeaturesChecker",
+ "-XepPatchLocation:IN_PLACE",
+ ],
+ }
+ ...
+}
+```
+```
+RUN_ERROR_PRONE=true m ${TARGET}
+```
+
+See also [RoSystemFeaturesChecker](errorprone/java/com/android/systemfeatures/errorprone/RoSystemFeaturesChecker.java)
+for more details.
+
+> **_NOTE:_** Not all system feature queries or targets need or should be
+migrated. Only system features that are explicitly declared with build flags,
+and only targets that are built with the platform (i.e., not updatable), are
+candidates for this linting and migration, e.g., SystemUI, System Server, etc...
+
+// TODO(b/203143243): Wrap the in-place lint updates with a simple script for convenience.
diff --git a/tools/systemfeatures/errorprone/java/com/android/systemfeatures/errorprone/RoSystemFeaturesChecker.java b/tools/systemfeatures/errorprone/java/com/android/systemfeatures/errorprone/RoSystemFeaturesChecker.java
new file mode 100644
index 0000000..7812377
--- /dev/null
+++ b/tools/systemfeatures/errorprone/java/com/android/systemfeatures/errorprone/RoSystemFeaturesChecker.java
@@ -0,0 +1,119 @@
+/*
+ * 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 com.android.systemfeatures.errorprone;
+
+import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+
+import com.android.systemfeatures.RoSystemFeaturesMetadata;
+
+import com.google.auto.service.AutoService;
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.fixes.SuggestedFix;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.matchers.Matcher;
+import com.google.errorprone.matchers.Matchers;
+import com.google.errorprone.util.ASTHelpers;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.tools.javac.code.Symbol;
+
+@AutoService(BugChecker.class)
+@BugPattern(
+ name = "RoSystemFeaturesChecker",
+ summary = "Use RoSystemFeature instead of PackageManager.hasSystemFeature",
+ explanation =
+ "Directly invoking `PackageManager.hasSystemFeature` is less efficient than using"
+ + " the `RoSystemFeatures` helper class. This check flags invocations like"
+ + " `context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FOO)`"
+ + " and suggests replacing them with"
+ + " `com.android.internal.pm.RoSystemFeatures.hasFeatureFoo(context)`.",
+ severity = WARNING)
+public class RoSystemFeaturesChecker extends BugChecker
+ implements BugChecker.MethodInvocationTreeMatcher {
+
+ private static final String PACKAGE_MANAGER_CLASS = "android.content.pm.PackageManager";
+ private static final String CONTEXT_CLASS = "android.content.Context";
+ private static final String RO_SYSTEM_FEATURE_SIMPLE_CLASS = "RoSystemFeatures";
+ private static final String RO_SYSTEM_FEATURE_CLASS =
+ "com.android.internal.pm." + RO_SYSTEM_FEATURE_SIMPLE_CLASS;
+ private static final String GET_PACKAGE_MANAGER_METHOD = "getPackageManager";
+ private static final String HAS_SYSTEM_FEATURE_METHOD = "hasSystemFeature";
+ private static final String FEATURE_PREFIX = "FEATURE_";
+
+ private static final Matcher<ExpressionTree> HAS_SYSTEM_FEATURE_MATCHER =
+ Matchers.instanceMethod()
+ .onDescendantOf(PACKAGE_MANAGER_CLASS)
+ .named(HAS_SYSTEM_FEATURE_METHOD)
+ .withParameters(String.class.getName());
+
+ private static final Matcher<ExpressionTree> GET_PACKAGE_MANAGER_MATCHER =
+ Matchers.instanceMethod()
+ .onDescendantOf(CONTEXT_CLASS)
+ .named(GET_PACKAGE_MANAGER_METHOD);
+
+ @Override
+ public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
+ if (!HAS_SYSTEM_FEATURE_MATCHER.matches(tree, state)) {
+ return Description.NO_MATCH;
+ }
+
+ // Check if the PackageManager was obtained from a Context instance.
+ ExpressionTree packageManager = ASTHelpers.getReceiver(tree);
+ if (!GET_PACKAGE_MANAGER_MATCHER.matches(packageManager, state)) {
+ return Description.NO_MATCH;
+ }
+
+ // Get the feature argument and check if it's a PackageManager.FEATURE_X constant.
+ ExpressionTree feature = tree.getArguments().isEmpty() ? null : tree.getArguments().get(0);
+ Symbol featureSymbol = ASTHelpers.getSymbol(feature);
+ if (featureSymbol == null
+ || !featureSymbol.isStatic()
+ || !featureSymbol.getSimpleName().toString().startsWith(FEATURE_PREFIX)
+ || ASTHelpers.enclosingClass(featureSymbol) == null
+ || !ASTHelpers.enclosingClass(featureSymbol)
+ .getQualifiedName()
+ .contentEquals(PACKAGE_MANAGER_CLASS)) {
+ return Description.NO_MATCH;
+ }
+
+ // Check if the feature argument is part of the RoSystemFeatures API surface.
+ String featureName = featureSymbol.getSimpleName().toString();
+ String methodName = RoSystemFeaturesMetadata.getMethodNameForFeatureName(featureName);
+ if (methodName == null) {
+ return Description.NO_MATCH;
+ }
+
+ // Generate the appropriate fix.
+ String replacement =
+ String.format(
+ "%s.%s(%s)",
+ RO_SYSTEM_FEATURE_SIMPLE_CLASS,
+ methodName,
+ state.getSourceForNode(ASTHelpers.getReceiver(packageManager)));
+ // Note that ErrorProne doesn't offer a seamless way of removing the `PackageManager` import
+ // if unused after fix application, so for now we only offer best effort import suggestions.
+ SuggestedFix fix =
+ SuggestedFix.builder()
+ .replace(tree, replacement)
+ .addImport(RO_SYSTEM_FEATURE_CLASS)
+ .removeStaticImport(PACKAGE_MANAGER_CLASS + "." + featureName)
+ .build();
+ return describeMatch(tree, fix);
+ }
+}
diff --git a/tools/systemfeatures/errorprone/tests/java/com/android/systemfeatures/errorprone/RoSystemFeaturesCheckerTest.java b/tools/systemfeatures/errorprone/tests/java/com/android/systemfeatures/errorprone/RoSystemFeaturesCheckerTest.java
new file mode 100644
index 0000000..c517b24
--- /dev/null
+++ b/tools/systemfeatures/errorprone/tests/java/com/android/systemfeatures/errorprone/RoSystemFeaturesCheckerTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2020 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 com.android.systemfeatures.errorprone;
+
+import com.google.errorprone.BugCheckerRefactoringTestHelper;
+import com.google.errorprone.CompilationTestHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class RoSystemFeaturesCheckerTest {
+ private BugCheckerRefactoringTestHelper mRefactoringHelper;
+ private CompilationTestHelper mCompilationHelper;
+
+ @Before
+ public void setUp() {
+ mCompilationHelper =
+ CompilationTestHelper.newInstance(RoSystemFeaturesChecker.class, getClass());
+ mRefactoringHelper =
+ BugCheckerRefactoringTestHelper.newInstance(
+ RoSystemFeaturesChecker.class, getClass());
+ }
+
+ @Test
+ public void testNoDiagnostic() {
+ mCompilationHelper
+ .addSourceFile("/android/content/Context.java")
+ .addSourceFile("/android/content/pm/PackageManager.java")
+ .addSourceLines("Example.java",
+ """
+ import android.content.Context;
+ import android.content.pm.PackageManager;
+ public class Example {
+ void test(Context context) {
+ boolean hasCustomFeature = context.getPackageManager()
+ .hasSystemFeature("my.custom.feature");
+ boolean hasNonAnnotatedFeature = context.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_NOT_ANNOTATED);
+ boolean hasNonRoApiFeature = context.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_NOT_IN_RO_FEATURE_API);
+ }
+ }
+ """)
+ .doTest();
+ }
+
+ @Test
+ public void testDiagnostic() {
+ mCompilationHelper
+ .addSourceFile("/android/content/Context.java")
+ .addSourceFile("/android/content/pm/PackageManager.java")
+ .addSourceLines("Example.java",
+ """
+ import android.content.Context;
+ import android.content.pm.PackageManager;
+ public class Example {
+ void test(Context context) {
+ boolean hasFeature = context.getPackageManager()
+ // BUG: Diagnostic contains:
+ .hasSystemFeature(PackageManager.FEATURE_PC);
+ }
+ }
+ """)
+ .doTest();
+ }
+
+ @Test
+ public void testFix() {
+ mRefactoringHelper
+ .addInputLines("Example.java",
+ """
+ import static android.content.pm.PackageManager.FEATURE_WATCH;
+
+ import android.content.Context;
+ import android.content.pm.PackageManager;
+ public class Example {
+ static class CustomContext extends Context {};
+ private CustomContext mContext;
+ void test(Context context) {
+ boolean hasPc = mContext.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_PC);
+ boolean hasWatch = context.getPackageManager()
+ .hasSystemFeature(FEATURE_WATCH);
+ }
+ }
+ """)
+ .addOutputLines("Example.java",
+ """
+ import android.content.Context;
+ import android.content.pm.PackageManager;
+ import com.android.internal.pm.RoSystemFeatures;
+ public class Example {
+ static class CustomContext extends Context {};
+ private CustomContext mContext;
+ void test(Context context) {
+ boolean hasPc = RoSystemFeatures.hasFeaturePc(mContext);
+ boolean hasWatch = RoSystemFeatures.hasFeatureWatch(context);
+ }
+ }
+ """)
+ // Don't try compiling the output, as it requires pulling in the full set of code
+ // dependencies.
+ .allowBreakingChanges()
+ .doTest();
+ }
+}
diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
index f260e27..ea660b0 100644
--- a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
+++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
@@ -53,11 +53,20 @@
* public static ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures();
* }
* </pre>
+ *
+ * <p> If `--metadata-only=true` is set, the resulting class would simply be:
+ * <pre>
+ * package com.foo;
+ * public final class RoSystemFeatures {
+ * public static String getMethodNameForFeatureName(String featureName);
+ * }
+ * </pre>
*/
object SystemFeaturesGenerator {
private const val FEATURE_ARG = "--feature="
private const val FEATURE_APIS_ARG = "--feature-apis="
private const val READONLY_ARG = "--readonly="
+ private const val METADATA_ONLY_ARG = "--metadata-only="
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")
@@ -84,6 +93,8 @@
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(" --metadata-only=true|false Whether to simply output metadata about the")
+ println(" generated API surface.")
}
/** Main entrypoint for build-time system feature codegen. */
@@ -106,6 +117,7 @@
}
var readonly = false
+ var metadataOnly = false
var outputClassName: ClassName? = null
val featureArgs = mutableListOf<FeatureInfo>()
// We could just as easily hardcode this list, as the static API surface should change
@@ -115,6 +127,8 @@
when {
arg.startsWith(READONLY_ARG) ->
readonly = arg.substring(READONLY_ARG.length).toBoolean()
+ arg.startsWith(METADATA_ONLY_ARG) ->
+ metadataOnly = arg.substring(METADATA_ONLY_ARG.length).toBoolean()
arg.startsWith(FEATURE_ARG) -> {
featureArgs.add(parseFeatureArg(arg))
}
@@ -155,9 +169,13 @@
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addJavadoc("@hide")
- addFeatureMethodsToClass(classBuilder, features.values)
- addMaybeFeatureMethodToClass(classBuilder, features.values)
- addGetFeaturesMethodToClass(classBuilder, features.values)
+ if (metadataOnly) {
+ addMetadataMethodToClass(classBuilder, features.values)
+ } else {
+ 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())
@@ -214,11 +232,8 @@
features: Collection<FeatureInfo>,
) {
for (feature in features) {
- // Turn "FEATURE_FOO" into "hasFeatureFoo".
- val methodName =
- "has" + CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, feature.name)
val methodBuilder =
- MethodSpec.methodBuilder(methodName)
+ MethodSpec.methodBuilder(feature.methodName)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addJavadoc("Check for ${feature.name}.\n\n@hide")
.returns(Boolean::class.java)
@@ -341,5 +356,32 @@
builder.addMethod(methodBuilder.build())
}
- private data class FeatureInfo(val name: String, val version: Int?, val readonly: Boolean)
+ /*
+ * Adds a metadata helper method that maps FEATURE_FOO names to their generated hasFeatureFoo()
+ * API counterpart, if defined.
+ */
+ private fun addMetadataMethodToClass(
+ builder: TypeSpec.Builder,
+ features: Collection<FeatureInfo>,
+ ) {
+ val methodBuilder =
+ MethodSpec.methodBuilder("getMethodNameForFeatureName")
+ .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
+ .addJavadoc("@return \"hasFeatureFoo\" if FEATURE_FOO is in the API, else null")
+ .returns(String::class.java)
+ .addParameter(String::class.java, "featureVarName")
+
+ methodBuilder.beginControlFlow("switch (featureVarName)")
+ for (feature in features) {
+ methodBuilder.addStatement("case \$S: return \$S", feature.name, feature.methodName)
+ }
+ methodBuilder.addStatement("default: return null").endControlFlow()
+
+ builder.addMethod(methodBuilder.build())
+ }
+
+ private data class FeatureInfo(val name: String, val version: Int?, val readonly: Boolean) {
+ // Turn "FEATURE_FOO" into "hasFeatureFoo".
+ val methodName get() = "has" + CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name)
+ }
}
diff --git a/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java b/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java
index 74ce6da..560454b 100644
--- a/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java
+++ b/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java
@@ -36,8 +36,8 @@
@Test
public void testSdkFeatureCount() {
// See the fake PackageManager definition in this directory.
- // It defines 5 annotated features, and any/all other constants should be ignored.
- assertThat(SystemFeaturesMetadata.SDK_FEATURE_COUNT).isEqualTo(5);
+ // It defines 6 annotated features, and any/all other constants should be ignored.
+ assertThat(SystemFeaturesMetadata.SDK_FEATURE_COUNT).isEqualTo(6);
}
@Test
diff --git a/tools/systemfeatures/tests/src/Context.java b/tools/systemfeatures/tests/src/android/content/Context.java
similarity index 100%
rename from tools/systemfeatures/tests/src/Context.java
rename to tools/systemfeatures/tests/src/android/content/Context.java
diff --git a/tools/systemfeatures/tests/src/FeatureInfo.java b/tools/systemfeatures/tests/src/android/content/pm/FeatureInfo.java
similarity index 100%
rename from tools/systemfeatures/tests/src/FeatureInfo.java
rename to tools/systemfeatures/tests/src/android/content/pm/FeatureInfo.java
diff --git a/tools/systemfeatures/tests/src/PackageManager.java b/tools/systemfeatures/tests/src/android/content/pm/PackageManager.java
similarity index 86%
rename from tools/systemfeatures/tests/src/PackageManager.java
rename to tools/systemfeatures/tests/src/android/content/pm/PackageManager.java
index 839a937..4a9edd6 100644
--- a/tools/systemfeatures/tests/src/PackageManager.java
+++ b/tools/systemfeatures/tests/src/android/content/pm/PackageManager.java
@@ -36,6 +36,9 @@
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_WIFI = "wifi";
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_NOT_IN_RO_FEATURE_API = "not_in_ro_feature_api";
+
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String FEATURE_INTENT_CATEGORY = "intent_category_with_feature_name_prefix";
@@ -47,4 +50,9 @@
public boolean hasSystemFeature(String featureName, int version) {
return false;
}
+
+ /** @hide */
+ public boolean hasSystemFeature(String featureName) {
+ return hasSystemFeature(featureName, 0);
+ }
}
diff --git a/tools/systemfeatures/tests/src/ArrayMap.java b/tools/systemfeatures/tests/src/android/util/ArrayMap.java
similarity index 100%
rename from tools/systemfeatures/tests/src/ArrayMap.java
rename to tools/systemfeatures/tests/src/android/util/ArrayMap.java
diff --git a/tools/systemfeatures/tests/src/ArraySet.java b/tools/systemfeatures/tests/src/android/util/ArraySet.java
similarity index 100%
rename from tools/systemfeatures/tests/src/ArraySet.java
rename to tools/systemfeatures/tests/src/android/util/ArraySet.java