summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/Android.bp4
-rw-r--r--packages/SystemUI/checks/Android.bp52
-rw-r--r--packages/SystemUI/checks/src/com/android/internal/systemui/lint/BroadcastSentViaContextDetector.kt83
-rw-r--r--packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt43
-rw-r--r--packages/SystemUI/checks/tests/com/android/systemui/lint/BroadcastSentViaContextDetectorTest.kt150
5 files changed, 332 insertions, 0 deletions
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 8cc9e004f758..f05c1e2e76f2 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -128,6 +128,10 @@ android_library {
kotlincflags: ["-Xjvm-default=enable"],
plugins: ["dagger2-compiler"],
+
+ lint: {
+ extra_check_modules: ["SystemUILintChecker"],
+ },
}
filegroup {
diff --git a/packages/SystemUI/checks/Android.bp b/packages/SystemUI/checks/Android.bp
new file mode 100644
index 000000000000..8457312dc403
--- /dev/null
+++ b/packages/SystemUI/checks/Android.bp
@@ -0,0 +1,52 @@
+// Copyright (C) 2021 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: "SystemUILintChecker",
+ srcs: [
+ "src/**/*.kt",
+ "src/**/*.java",
+ ],
+ plugins: ["auto_service_plugin"],
+ libs: [
+ "auto_service_annotations",
+ "lint_api",
+ ],
+}
+
+java_test_host {
+ name: "SystemUILintCheckerTest",
+ srcs: [
+ "tests/**/*.kt",
+ "tests/**/*.java",
+ ],
+ static_libs: [
+ "SystemUILintChecker",
+ "junit",
+ "lint",
+ "lint_tests",
+ ],
+ test_options: {
+ unit_test: true,
+ },
+}
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/BroadcastSentViaContextDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/BroadcastSentViaContextDetector.kt
new file mode 100644
index 000000000000..8d48f0957be4
--- /dev/null
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/BroadcastSentViaContextDetector.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 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.internal.systemui.lint
+
+import com.android.tools.lint.detector.api.Category
+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 org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.UClass
+import org.jetbrains.uast.getParentOfType
+
+/**
+ * Checks if anyone is calling sendBroadcast / sendBroadcastAsUser on a Context (or subclasses) and
+ * directs them to using com.android.systemui.broadcast.BroadcastSender instead.
+ */
+@Suppress("UnstableApiUsage")
+class BroadcastSentViaContextDetector : Detector(), SourceCodeScanner {
+
+ override fun getApplicableMethodNames(): List<String> {
+ return listOf("sendBroadcast", "sendBroadcastAsUser")
+ }
+
+ override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
+ if (node.getParentOfType(UClass::class.java)?.qualifiedName ==
+ "com.android.systemui.broadcast.BroadcastSender"
+ ) {
+ // Don't warn for class we want the developers to use.
+ return
+ }
+
+ val evaulator = context.evaluator
+ if (evaulator.isMemberInSubClassOf(method, "android.content.Context")) {
+ context.report(
+ ISSUE,
+ method,
+ context.getNameLocation(node),
+ "Please don't call sendBroadcast/sendBroadcastAsUser directly on " +
+ "Context, use com.android.systemui.broadcast.BroadcastSender instead."
+ )
+ }
+ }
+
+ companion object {
+ @JvmField
+ val ISSUE: Issue =
+ Issue.create(
+ id = "BroadcastSentViaContext",
+ briefDescription = "Broadcast sent via Context instead of BroadcastSender.",
+ explanation =
+ "Broadcast was sent via " +
+ "Context.sendBroadcast/Context.sendBroadcastAsUser. Please use " +
+ "BroadcastSender.sendBroadcast/BroadcastSender.sendBroadcastAsUser " +
+ "which will schedule dispatch of broadcasts on background thread. " +
+ "Sending broadcasts on main thread causes jank due to synchronous " +
+ "Binder calls.",
+ category = Category.PERFORMANCE,
+ priority = 8,
+ severity = Severity.WARNING,
+ implementation =
+ Implementation(BroadcastSentViaContextDetector::class.java, Scope.JAVA_FILE_SCOPE)
+ )
+ }
+}
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
new file mode 100644
index 000000000000..397a110f4bc7
--- /dev/null
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 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.internal.systemui.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.android.tools.lint.detector.api.Issue
+import com.google.auto.service.AutoService
+
+@AutoService(IssueRegistry::class)
+@Suppress("UnstableApiUsage")
+class SystemUIIssueRegistry : IssueRegistry() {
+
+ override val issues: List<Issue>
+ get() = listOf(BroadcastSentViaContextDetector.ISSUE)
+
+ 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=78010",
+ contact = "jernej@google.com"
+ )
+}
diff --git a/packages/SystemUI/checks/tests/com/android/systemui/lint/BroadcastSentViaContextDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/systemui/lint/BroadcastSentViaContextDetectorTest.kt
new file mode 100644
index 000000000000..da010212f211
--- /dev/null
+++ b/packages/SystemUI/checks/tests/com/android/systemui/lint/BroadcastSentViaContextDetectorTest.kt
@@ -0,0 +1,150 @@
+package com.android.internal.systemui.lint
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFiles
+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
+
+class BroadcastSentViaContextDetectorTest : LintDetectorTest() {
+
+ override fun getDetector(): Detector = BroadcastSentViaContextDetector()
+ override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
+
+ override fun getIssues(): List<Issue> = listOf(
+ BroadcastSentViaContextDetector.ISSUE)
+
+ @Test
+ fun testSendBroadcast() {
+ lint().files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import android.content.Context;
+
+ public class TestClass1 {
+ public void send(Context context) {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ context.sendBroadcast(intent);
+ }
+ }
+ """
+ ).indented(),
+ *stubs)
+ .issues(BroadcastSentViaContextDetector.ISSUE)
+ .run()
+ .expectWarningCount(1)
+ .expectContains(
+ "Please don't call sendBroadcast/sendBroadcastAsUser directly on " +
+ "Context, use com.android.systemui.broadcast.BroadcastSender instead.")
+ }
+
+ @Test
+ fun testSendBroadcastAsUser() {
+ lint().files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import android.content.Context;
+ import android.os.UserHandle;
+
+ public class TestClass1 {
+ public void send(Context context) {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ context.sendBroadcastAsUser(intent, UserHandle.ALL, "permission");
+ }
+ }
+ """).indented(),
+ *stubs)
+ .issues(BroadcastSentViaContextDetector.ISSUE)
+ .run()
+ .expectWarningCount(1)
+ .expectContains(
+ "Please don't call sendBroadcast/sendBroadcastAsUser directly on " +
+ "Context, use com.android.systemui.broadcast.BroadcastSender instead.")
+ }
+
+ @Test
+ fun testSendBroadcastInActivity() {
+ lint().files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import android.app.Activity;
+ import android.os.UserHandle;
+
+ public class TestClass1 {
+ public void send(Activity activity) {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ activity.sendBroadcastAsUser(intent, UserHandle.ALL, "permission");
+ }
+
+ }
+ """).indented(),
+ *stubs)
+ .issues(BroadcastSentViaContextDetector.ISSUE)
+ .run()
+ .expectWarningCount(1)
+ .expectContains(
+ "Please don't call sendBroadcast/sendBroadcastAsUser directly on " +
+ "Context, use com.android.systemui.broadcast.BroadcastSender instead.")
+ }
+
+ @Test
+ fun testNoopIfNoCall() {
+ lint().files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import android.content.Context;
+
+ public class TestClass1 {
+ public void sendBroadcast() {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ context.startActivity(intent);
+ }
+ }
+ """).indented(),
+ *stubs)
+ .issues(BroadcastSentViaContextDetector.ISSUE)
+ .run()
+ .expectClean()
+ }
+
+ private val contextStub: TestFile = java(
+ """
+ package android.content;
+ import android.os.UserHandle;
+
+ public class Context {
+ public void sendBroadcast(Intent intent) {};
+ public void sendBroadcast(Intent intent, String receiverPermission) {};
+ public void sendBroadcastAsUser(Intent intent, UserHandle userHandle,
+ String permission) {};
+ }
+ """
+ )
+
+ private val activityStub: TestFile = java(
+ """
+ package android.app;
+ import android.content.Context;
+
+ public class Activity extends Context {}
+ """
+ )
+
+ private val userHandleStub: TestFile = java(
+ """
+ package android.os;
+
+ public enum UserHandle {
+ ALL
+ }
+ """
+ )
+
+ private val stubs = arrayOf(contextStub, activityStub, userHandleStub)
+}