Merge "Add ExemptAidlInterfacesGenerator for PermissionAnnotationDetector" into main
diff --git a/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
index 624a198..5c64697 100644
--- a/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
+++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
@@ -41,6 +41,7 @@
         PermissionAnnotationDetector.ISSUE_MISSING_PERMISSION_ANNOTATION,
         PermissionMethodDetector.ISSUE_PERMISSION_METHOD_USAGE,
         PermissionMethodDetector.ISSUE_CAN_BE_PERMISSION_METHOD,
+        FeatureAutomotiveDetector.ISSUE,
     )
 
     override val api: Int
diff --git a/tools/lint/framework/checks/src/main/java/com/google/android/lint/FeatureAutomotiveDetector.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/FeatureAutomotiveDetector.kt
new file mode 100644
index 0000000..972b0c9
--- /dev/null
+++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/FeatureAutomotiveDetector.kt
@@ -0,0 +1,90 @@
+/*
+ * 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.google.android.lint
+
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.ConstantEvaluator
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiMethod
+import java.util.EnumSet
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.ULiteralExpression
+import org.jetbrains.uast.UReferenceExpression
+
+/**
+ * A detector to check the usage of PackageManager.hasSystemFeature("
+ * android.hardware.type.automotive") in CTS tests.
+ */
+class FeatureAutomotiveDetector : Detector(), SourceCodeScanner {
+
+    companion object {
+
+        val EXPLANATION =
+            """
+            This class uses PackageManager.hasSystemFeature(\"android.hardware.type.automotive\") \
+            or other equivalent methods. \
+            If it is used to make a CTS test behave differently on AAOS, you should use \
+            @RequireAutomotive or @RequireNotAutomotive instead; otherwise, please ignore this \
+            warning. See https://g3doc.corp.google.com/wireless/android/partner/compatibility/\
+            g3doc/dev/write-a-test/index.md#write-a-test-that-behaves-differently-on-aaos
+            """
+
+        val ISSUE: Issue =
+            Issue.create(
+                id = "UsingFeatureAutomotiveInCTS",
+                briefDescription =
+                    "PackageManager.hasSystemFeature(\"" +
+                        " android.hardware.type.automotive\") is used in CTS tests",
+                explanation = EXPLANATION,
+                category = Category.TESTING,
+                priority = 8,
+                severity = Severity.WARNING,
+                implementation =
+                    Implementation(
+                        FeatureAutomotiveDetector::class.java,
+                        EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
+                    )
+            )
+    }
+
+    override fun getApplicableMethodNames() =
+        listOf("hasSystemFeature", "hasFeature", "hasDeviceFeature", "bypassTestForFeatures")
+
+    override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
+        node.valueArguments.forEach {
+            val value =
+                when (it) {
+                    is ULiteralExpression -> it.value
+                    is UReferenceExpression -> ConstantEvaluator.evaluate(context, it)
+                    else -> null
+                }
+            if (value is String && value == "android.hardware.type.automotive") {
+                context.report(
+                    issue = ISSUE,
+                    location = context.getNameLocation(method),
+                    message = EXPLANATION
+                )
+            }
+        }
+    }
+}
diff --git a/tools/lint/framework/checks/src/test/java/com/google/android/lint/FeatureAutomotiveDetectorTest.kt b/tools/lint/framework/checks/src/test/java/com/google/android/lint/FeatureAutomotiveDetectorTest.kt
new file mode 100644
index 0000000..b5e2c00
--- /dev/null
+++ b/tools/lint/framework/checks/src/test/java/com/google/android/lint/FeatureAutomotiveDetectorTest.kt
@@ -0,0 +1,366 @@
+/*
+ * 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.google.android.lint
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestLintTask
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+
+@Suppress("UnstableApiUsage")
+class FeatureAutomotiveDetectorTest : LintDetectorTest() {
+    val explanation =
+        FeatureAutomotiveDetector.EXPLANATION.replace("\\", "").replace("\n            ", "") +
+            " [UsingFeatureAutomotiveInCTS]"
+
+    override fun getDetector(): Detector = FeatureAutomotiveDetector()
+    override fun getIssues(): List<Issue> = listOf(FeatureAutomotiveDetector.ISSUE)
+    override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
+
+    @Test
+    fun testWarning1() {
+        lint()
+            .files(
+                java(
+                        """
+                import android.content.pm.PackageManager;
+
+                public class Foo {
+
+                    private void fun() {
+                        PackageManager.getInstance().hasSystemFeature(
+                            "android.hardware.type.automotive");
+                    }
+                }
+                """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(FeatureAutomotiveDetector.ISSUE)
+            .run()
+            .expect(
+                """
+                            src/android/content/pm/PackageManager.java:13: Warning: $explanation
+                public boolean hasSystemFeature(String feature) {
+                               ~~~~~~~~~~~~~~~~
+    0 errors, 1 warnings
+                            """
+            )
+    }
+
+    @Test
+    fun testWarning2() {
+        lint()
+            .files(
+                java(
+                        """
+                import android.content.pm.PackageManager;
+
+                public class Foo {
+
+                    private void fun() {
+                        String featureName = "android.hardware.type.automotive";
+                        PackageManager.getInstance().hasSystemFeature(featureName);
+                    }
+                }
+                """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(FeatureAutomotiveDetector.ISSUE)
+            .run()
+            .expect(
+                """
+                            src/android/content/pm/PackageManager.java:13: Warning: $explanation
+                public boolean hasSystemFeature(String feature) {
+                               ~~~~~~~~~~~~~~~~
+    0 errors, 1 warnings
+                            """
+            )
+    }
+
+    @Test
+    fun testWarning3() {
+        lint()
+            .files(
+                java(
+                        """
+                import android.content.pm.PackageManager;
+
+                public class Foo {
+
+                    private void fun() {
+                        PackageManager.getInstance().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+                    }
+                }
+                """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(FeatureAutomotiveDetector.ISSUE)
+            .run()
+            .expect(
+                """
+                            src/android/content/pm/PackageManager.java:13: Warning: $explanation
+                public boolean hasSystemFeature(String feature) {
+                               ~~~~~~~~~~~~~~~~
+    0 errors, 1 warnings
+                            """
+            )
+    }
+
+    @Test
+    fun testWarning4() {
+        lint()
+            .files(
+                java(
+                        """
+                import android.content.pm.PackageManager;
+
+                public class Foo {
+
+                    private void fun() {
+                        String featureName = PackageManager.FEATURE_AUTOMOTIVE;
+                        PackageManager.getInstance().hasSystemFeature(featureName);
+                    }
+                }
+                """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(FeatureAutomotiveDetector.ISSUE)
+            .run()
+            .expect(
+                """
+                            src/android/content/pm/PackageManager.java:13: Warning: $explanation
+                public boolean hasSystemFeature(String feature) {
+                               ~~~~~~~~~~~~~~~~
+    0 errors, 1 warnings
+                            """
+            )
+    }
+
+    @Test
+    fun testWarning5() {
+        lint()
+            .files(
+                java(
+                        """
+                import com.android.example.Utils;
+
+                public class Foo {
+
+                    private void fun() {
+                        Utils.hasFeature("android.hardware.type.automotive");
+                    }
+                }
+                """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(FeatureAutomotiveDetector.ISSUE)
+            .run()
+            .expect(
+                """
+                    src/com/android/example/Utils.java:7: Warning: $explanation
+                public static boolean hasFeature(String feature) {
+                                      ~~~~~~~~~~
+    0 errors, 1 warnings
+                            """
+            )
+    }
+
+    @Test
+    fun testWarning6() {
+        lint()
+            .files(
+                java(
+                        """
+                import com.android.example.Utils;
+
+                public class Foo {
+
+                    private void fun() {
+                        Utils.hasDeviceFeature("android.hardware.type.automotive");
+                    }
+                }
+                """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(FeatureAutomotiveDetector.ISSUE)
+            .run()
+            .expect(
+                """
+                    src/com/android/example/Utils.java:11: Warning: $explanation
+                public static boolean hasDeviceFeature(String feature) {
+                                      ~~~~~~~~~~~~~~~~
+    0 errors, 1 warnings
+                            """
+            )
+    }
+
+    @Test
+    fun testWarning7() {
+        lint()
+            .files(
+                java(
+                        """
+                import com.android.example.Utils;
+
+                public class Foo {
+
+                    private void fun() {
+                        Utils.hasFeature(new Object(), "android.hardware.type.automotive");
+                    }
+                }
+                """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(FeatureAutomotiveDetector.ISSUE)
+            .run()
+            .expect(
+                """
+                    src/com/android/example/Utils.java:15: Warning: $explanation
+                public static boolean hasFeature(Object object, String feature) {
+                                      ~~~~~~~~~~
+    0 errors, 1 warnings
+                            """
+            )
+    }
+
+    @Test
+    fun testWarning8() {
+        lint()
+            .files(
+                java(
+                        """
+                import com.android.example.Utils;
+
+                public class Foo {
+
+                    private void fun() {
+                        Utils.bypassTestForFeatures("android.hardware.type.automotive");
+                    }
+                }
+                """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(FeatureAutomotiveDetector.ISSUE)
+            .run()
+            .expect(
+                """
+                    src/com/android/example/Utils.java:19: Warning: $explanation
+                public static boolean bypassTestForFeatures(String feature) {
+                                      ~~~~~~~~~~~~~~~~~~~~~
+    0 errors, 1 warnings
+                            """
+            )
+    }
+
+    @Test
+    fun testNoWarning() {
+        lint()
+            .files(
+                java(
+                        """
+                import android.content.pm.PackageManager;
+
+                public class Foo {
+                    private void fun() {
+                        String featureName1 = "android.hardware.type.automotive";
+                        String featureName2 = PackageManager.FEATURE_AUTOMOTIVE;
+                        String notFeatureName = "FEATURE_AUTOMOTIVE";
+                        PackageManager.getInstance().hasSystemFeature(notFeatureName);
+                        /*
+                        PackageManager.getInstance().hasSystemFeature(
+                            "android.hardware.type.automotive");
+                         */
+                    }
+                }
+                """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(FeatureAutomotiveDetector.ISSUE)
+            .run()
+            .expectClean()
+    }
+
+    private val pmStub: TestFile =
+        java(
+            """
+        package android.content.pm;
+
+        import java.lang.String;
+
+        public class PackageManager {
+            public static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
+
+            public static PackageManager getInstance() {
+                return new PackageManager();
+            }
+
+            public boolean hasSystemFeature(String feature) {
+                return true;
+            }
+        }
+        """
+        )
+
+    private val exampleStub: TestFile =
+        java(
+            """
+        package com.android.example;
+
+        import java.lang.String;
+
+        public class Utils {
+            public static boolean hasFeature(String feature) {
+                return true;
+            }
+
+            public static boolean hasDeviceFeature(String feature) {
+                return true;
+            }
+
+            public static boolean hasFeature(Object object, String feature) {
+                return true;
+            }
+
+            public static boolean bypassTestForFeatures(String feature) {
+                return true;
+            }
+        }
+        """
+        )
+
+    private val stubs = arrayOf(pmStub, exampleStub)
+}