Merge changes I08f5aa3e,I7876e44c,I4e90a045 into main
* changes:
Support multiple modules for lint_fix
Add utility lint for metrics on @EnforcePermission
Move AidlImplementationDetector from global to common
diff --git a/tools/lint/common/Android.bp b/tools/lint/common/Android.bp
index 898f88b..8bfbfe5 100644
--- a/tools/lint/common/Android.bp
+++ b/tools/lint/common/Android.bp
@@ -27,3 +27,30 @@
libs: ["lint_api"],
kotlincflags: ["-Xjvm-default=all"],
}
+
+java_defaults {
+ name: "AndroidLintCheckerTestDefaults",
+ srcs: ["checks/src/test/java/**/*.kt"],
+ static_libs: [
+ "junit",
+ "lint",
+ "lint_tests",
+ ],
+ test_options: {
+ unit_test: true,
+ tradefed_options: [
+ {
+ // lint bundles in some classes that were built with older versions
+ // of libraries, and no longer load. Since tradefed tries to load
+ // all classes in the jar to look for tests, it crashes loading them.
+ // Exclude these classes from tradefed's search.
+ name: "exclude-paths",
+ value: "org/apache",
+ },
+ {
+ name: "exclude-paths",
+ value: "META-INF",
+ },
+ ],
+ },
+}
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/AidlImplementationDetector.kt b/tools/lint/common/src/main/java/com/google/android/lint/aidl/AidlImplementationDetector.kt
similarity index 100%
rename from tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/AidlImplementationDetector.kt
rename to tools/lint/common/src/main/java/com/google/android/lint/aidl/AidlImplementationDetector.kt
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/Constants.kt b/tools/lint/common/src/main/java/com/google/android/lint/aidl/Constants.kt
similarity index 100%
rename from tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/Constants.kt
rename to tools/lint/common/src/main/java/com/google/android/lint/aidl/Constants.kt
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt b/tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt
similarity index 100%
rename from tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt
rename to tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt
diff --git a/tools/lint/fix/soong_lint_fix.py b/tools/lint/fix/soong_lint_fix.py
index cd4d778d..acc0ad0 100644
--- a/tools/lint/fix/soong_lint_fix.py
+++ b/tools/lint/fix/soong_lint_fix.py
@@ -29,6 +29,39 @@
PATH_SUFFIX = "android_common/lint"
FIX_ZIP = "suggested-fixes.zip"
+
+class SoongModule:
+ """A Soong module to lint.
+
+ The constructor takes the name of the module (for example,
+ "framework-minus-apex"). find() must be called to extract the intermediate
+ module path from Soong's module-info.json
+ """
+ def __init__(self, name):
+ self._name = name
+
+ def find(self, module_info):
+ """Finds the module in the loaded module_info.json."""
+ if self._name not in module_info:
+ raise Exception(f"Module {self._name} not found!")
+
+ partial_path = module_info[self._name]["path"][0]
+ print(f"Found module {partial_path}/{self._name}.")
+ self._path = f"{PATH_PREFIX}/{partial_path}/{self._name}/{PATH_SUFFIX}"
+
+ @property
+ def name(self):
+ return self._name
+
+ @property
+ def lint_report(self):
+ return f"{self._path}/lint-report.txt"
+
+ @property
+ def suggested_fixes(self):
+ return f"{self._path}/{FIX_ZIP}"
+
+
class SoongLintFix:
"""
This class creates a command line tool that will
@@ -53,16 +86,14 @@
self._parser = _setup_parser()
self._args = None
self._kwargs = None
- self._path = None
- self._target = None
+ self._modules = []
-
- def run(self, additional_setup=None, custom_fix=None):
+ def run(self):
"""
Run the script
"""
self._setup()
- self._find_module()
+ self._find_modules()
self._lint()
if not self._args.no_fix:
@@ -87,8 +118,6 @@
os.chdir(ANDROID_BUILD_TOP)
-
- def _find_module(self):
print("Refreshing soong modules...")
try:
os.mkdir(ANDROID_PRODUCT_OUT)
@@ -97,48 +126,47 @@
subprocess.call(f"{SOONG_UI} --make-mode {PRODUCT_OUT}/module-info.json", **self._kwargs)
print("done.")
+
+ def _find_modules(self):
with open(f"{ANDROID_PRODUCT_OUT}/module-info.json") as f:
module_info = json.load(f)
- if self._args.module not in module_info:
- sys.exit(f"Module {self._args.module} not found!")
-
- module_path = module_info[self._args.module]["path"][0]
- print(f"Found module {module_path}/{self._args.module}.")
-
- self._path = f"{PATH_PREFIX}/{module_path}/{self._args.module}/{PATH_SUFFIX}"
- self._target = f"{self._path}/lint-report.txt"
-
+ for module_name in self._args.modules:
+ module = SoongModule(module_name)
+ module.find(module_info)
+ self._modules.append(module)
def _lint(self):
print("Cleaning up any old lint results...")
- try:
- os.remove(f"{self._target}")
- os.remove(f"{self._path}/{FIX_ZIP}")
- except FileNotFoundError:
- pass
+ for module in self._modules:
+ try:
+ os.remove(f"{module.lint_report}")
+ os.remove(f"{module.suggested_fixes}")
+ except FileNotFoundError:
+ pass
print("done.")
- print(f"Generating {self._target}")
- subprocess.call(f"{SOONG_UI} --make-mode {self._target}", **self._kwargs)
+ target = " ".join([ module.lint_report for module in self._modules ])
+ print(f"Generating {target}")
+ subprocess.call(f"{SOONG_UI} --make-mode {target}", **self._kwargs)
print("done.")
-
def _fix(self):
- print("Copying suggested fixes to the tree...")
- with zipfile.ZipFile(f"{self._path}/{FIX_ZIP}") as zip:
- for name in zip.namelist():
- if name.startswith("out") or not name.endswith(".java"):
- continue
- with zip.open(name) as src, open(f"{ANDROID_BUILD_TOP}/{name}", "wb") as dst:
- shutil.copyfileobj(src, dst)
+ for module in self._modules:
+ print(f"Copying suggested fixes for {module.name} to the tree...")
+ with zipfile.ZipFile(f"{module.suggested_fixes}") as zip:
+ for name in zip.namelist():
+ if name.startswith("out") or not name.endswith(".java"):
+ continue
+ with zip.open(name) as src, open(f"{ANDROID_BUILD_TOP}/{name}", "wb") as dst:
+ shutil.copyfileobj(src, dst)
print("done.")
-
def _print(self):
- print("### lint-report.txt ###", end="\n\n")
- with open(self._target, "r") as f:
- print(f.read())
+ for module in self._modules:
+ print(f"### lint-report.txt {module.name} ###", end="\n\n")
+ with open(module.lint_report, "r") as f:
+ print(f.read())
def _setup_parser():
@@ -151,7 +179,8 @@
**Gotcha**: You must have run `source build/envsetup.sh` and `lunch` first.
""", formatter_class=argparse.RawTextHelpFormatter)
- parser.add_argument('module',
+ parser.add_argument('modules',
+ nargs='+',
help='The soong build module to run '
'(e.g. framework-minus-apex or services.core.unboosted)')
@@ -170,4 +199,4 @@
return parser
if __name__ == "__main__":
- SoongLintFix().run()
\ No newline at end of file
+ SoongLintFix().run()
diff --git a/tools/lint/framework/Android.bp b/tools/lint/framework/Android.bp
index 30a6daa..5acdf43 100644
--- a/tools/lint/framework/Android.bp
+++ b/tools/lint/framework/Android.bp
@@ -37,28 +37,9 @@
java_test_host {
name: "AndroidFrameworkLintCheckerTest",
+ defaults: ["AndroidLintCheckerTestDefaults"],
srcs: ["checks/src/test/java/**/*.kt"],
static_libs: [
"AndroidFrameworkLintChecker",
- "junit",
- "lint",
- "lint_tests",
],
- test_options: {
- unit_test: true,
- tradefed_options: [
- {
- // lint bundles in some classes that were built with older versions
- // of libraries, and no longer load. Since tradefed tries to load
- // all classes in the jar to look for tests, it crashes loading them.
- // Exclude these classes from tradefed's search.
- name: "exclude-paths",
- value: "org/apache",
- },
- {
- name: "exclude-paths",
- value: "META-INF",
- },
- ],
- },
}
diff --git a/tools/lint/global/Android.bp b/tools/lint/global/Android.bp
index bedb7bd..3e74171 100644
--- a/tools/lint/global/Android.bp
+++ b/tools/lint/global/Android.bp
@@ -38,28 +38,9 @@
java_test_host {
name: "AndroidGlobalLintCheckerTest",
+ defaults: ["AndroidLintCheckerTestDefaults"],
srcs: ["checks/src/test/java/**/*.kt"],
static_libs: [
"AndroidGlobalLintChecker",
- "junit",
- "lint",
- "lint_tests",
],
- test_options: {
- unit_test: true,
- tradefed_options: [
- {
- // lint bundles in some classes that were built with older versions
- // of libraries, and no longer load. Since tradefed tries to load
- // all classes in the jar to look for tests, it crashes loading them.
- // Exclude these classes from tradefed's search.
- name: "exclude-paths",
- value: "org/apache",
- },
- {
- name: "exclude-paths",
- value: "META-INF",
- },
- ],
- },
}
diff --git a/tools/lint/utils/Android.bp b/tools/lint/utils/Android.bp
new file mode 100644
index 0000000..75e8d68
--- /dev/null
+++ b/tools/lint/utils/Android.bp
@@ -0,0 +1,45 @@
+// Copyright (C) 2023 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_library_host {
+ name: "AndroidUtilsLintChecker",
+ srcs: ["checks/src/main/java/**/*.kt"],
+ plugins: ["auto_service_plugin"],
+ libs: [
+ "auto_service_annotations",
+ "lint_api",
+ ],
+ static_libs: [
+ "AndroidCommonLint",
+ ],
+ kotlincflags: ["-Xjvm-default=all"],
+}
+
+java_test_host {
+ name: "AndroidUtilsLintCheckerTest",
+ defaults: ["AndroidLintCheckerTestDefaults"],
+ srcs: ["checks/src/test/java/**/*.kt"],
+ static_libs: [
+ "AndroidUtilsLintChecker",
+ ],
+}
diff --git a/tools/lint/utils/checks/src/main/java/com/google/android/lint/AndroidUtilsIssueRegistry.kt b/tools/lint/utils/checks/src/main/java/com/google/android/lint/AndroidUtilsIssueRegistry.kt
new file mode 100644
index 0000000..fa61c42
--- /dev/null
+++ b/tools/lint/utils/checks/src/main/java/com/google/android/lint/AndroidUtilsIssueRegistry.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 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.client.api.IssueRegistry
+import com.android.tools.lint.client.api.Vendor
+import com.android.tools.lint.detector.api.CURRENT_API
+import com.google.android.lint.aidl.AnnotatedAidlCounter
+import com.google.auto.service.AutoService
+
+@AutoService(IssueRegistry::class)
+@Suppress("UnstableApiUsage")
+class AndroidUtilsIssueRegistry : IssueRegistry() {
+ override val issues = listOf(
+ AnnotatedAidlCounter.ISSUE_ANNOTATED_AIDL_COUNTER,
+ )
+
+ override val api: Int
+ get() = CURRENT_API
+
+ override val minApi: Int
+ get() = 8
+
+ override val vendor: Vendor = Vendor(
+ vendorName = "Android",
+ feedbackUrl = "http://b/issues/new?component=315013",
+ contact = "tweek@google.com"
+ )
+}
diff --git a/tools/lint/utils/checks/src/main/java/com/google/android/lint/aidl/AnnotatedAidlCounter.kt b/tools/lint/utils/checks/src/main/java/com/google/android/lint/aidl/AnnotatedAidlCounter.kt
new file mode 100644
index 0000000..f0ec3f4
--- /dev/null
+++ b/tools/lint/utils/checks/src/main/java/com/google/android/lint/aidl/AnnotatedAidlCounter.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2023 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.aidl
+
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Context
+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.Location
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import org.jetbrains.uast.UBlockExpression
+import org.jetbrains.uast.UMethod
+
+import java.util.TreeMap
+
+/**
+ * Count the number of AIDL interfaces. Reports the number of annotated and
+ * non-annotated methods.
+ */
+@Suppress("UnstableApiUsage")
+class AnnotatedAidlCounter : AidlImplementationDetector() {
+
+ private data class Stat(
+ var unannotated: Int = 0,
+ var enforced: Int = 0,
+ var notRequired: Int = 0,
+ )
+
+ private var packagesStats: TreeMap<String, Stat> = TreeMap<String, Stat>()
+
+ override fun visitAidlMethod(
+ context: JavaContext,
+ node: UMethod,
+ interfaceName: String,
+ body: UBlockExpression
+ ) {
+ val packageName = context.uastFile?.packageName ?: "<unknown>"
+ var packageStat = packagesStats.getOrDefault(packageName, Stat())
+ when {
+ node.hasAnnotation(ANNOTATION_ENFORCE_PERMISSION) -> packageStat.enforced += 1
+ node.hasAnnotation(ANNOTATION_REQUIRES_NO_PERMISSION) -> packageStat.notRequired += 1
+ else -> packageStat.unannotated += 1
+ }
+ packagesStats.put(packageName, packageStat)
+ // context.driver.client.log(null, "%s.%s#%s".format(packageName, interfaceName, node.name))
+ }
+
+ override fun afterCheckRootProject(context: Context) {
+ var total = Stat()
+ for ((packageName, stat) in packagesStats) {
+ context.client.log(null, "package $packageName => $stat")
+ total.unannotated += stat.unannotated
+ total.enforced += stat.enforced
+ total.notRequired += stat.notRequired
+ }
+ val location = Location.create(context.project.dir)
+ context.report(
+ ISSUE_ANNOTATED_AIDL_COUNTER,
+ location,
+ "module ${context.project.name} => $total"
+ )
+ }
+
+ companion object {
+
+ @JvmField
+ val ISSUE_ANNOTATED_AIDL_COUNTER = Issue.create(
+ id = "AnnotatedAidlCounter",
+ briefDescription = "Statistics on the number of annotated AIDL methods.",
+ explanation = "",
+ category = Category.SECURITY,
+ priority = 5,
+ severity = Severity.INFORMATIONAL,
+ implementation = Implementation(
+ AnnotatedAidlCounter::class.java,
+ Scope.JAVA_FILE_SCOPE
+ ),
+ )
+ }
+}
diff --git a/tools/lint/utils/checks/src/test/java/com/google/android/lint/aidl/AnnotatedAidlCounterTest.kt b/tools/lint/utils/checks/src/test/java/com/google/android/lint/aidl/AnnotatedAidlCounterTest.kt
new file mode 100644
index 0000000..692b7da
--- /dev/null
+++ b/tools/lint/utils/checks/src/test/java/com/google/android/lint/aidl/AnnotatedAidlCounterTest.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2023 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.aidl
+
+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
+
+@Suppress("UnstableApiUsage")
+class AnnotatedAidlCounterTest : LintDetectorTest() {
+ override fun getDetector(): Detector = AnnotatedAidlCounter()
+
+ override fun getIssues(): List<Issue> = listOf(
+ AnnotatedAidlCounter.ISSUE_ANNOTATED_AIDL_COUNTER,
+ )
+
+ override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
+
+ /** No issue scenario */
+
+ fun testDoesNotDetectIssuesCorrectAnnotationOnMethod() {
+ lint().files(java(
+ """
+ package test.pkg;
+ import android.annotation.EnforcePermission;
+ public class TestClass2 extends IFooMethod.Stub {
+ @Override
+ @EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void testMethod() {}
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expect("""
+ app: Information: module app => Stat(unannotated=0, enforced=1, notRequired=0) [AnnotatedAidlCounter]
+ 0 errors, 0 warnings
+ """)
+ }
+
+ // A service with permission annotation on the method.
+ private val interfaceIFooMethodStub: TestFile = java(
+ """
+ public interface IFooMethod extends android.os.IInterface {
+ public static abstract class Stub extends android.os.Binder implements IFooMethod {}
+ @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void testMethod();
+ }
+ """
+ ).indented()
+
+ // A service without any permission annotation.
+ private val interfaceIBarStub: TestFile = java(
+ """
+ public interface IBar extends android.os.IInterface {
+ public static abstract class Stub extends android.os.Binder implements IBar {
+ @Override
+ public void testMethod() {}
+ }
+ public void testMethod();
+ }
+ """
+ ).indented()
+
+ private val manifestPermissionStub: TestFile = java(
+ """
+ package android.Manifest;
+ class permission {
+ public static final String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE";
+ }
+ """
+ ).indented()
+
+ private val enforcePermissionAnnotationStub: TestFile = java(
+ """
+ package android.annotation;
+ public @interface EnforcePermission {}
+ """
+ ).indented()
+
+ private val stubs = arrayOf(interfaceIFooMethodStub, interfaceIBarStub,
+ manifestPermissionStub, enforcePermissionAnnotationStub)
+}