diff options
3 files changed, 299 insertions, 0 deletions
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SlowUserQueryDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SlowUserQueryDetector.kt new file mode 100644 index 000000000000..b00661575c14 --- /dev/null +++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SlowUserQueryDetector.kt @@ -0,0 +1,103 @@ +/* + * 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 + +/** + * Checks for slow calls to ActivityManager.getCurrentUser() or UserManager.getUserInfo() and + * suggests using UserTracker instead. For more info, see: http://go/multi-user-in-systemui-slides. + */ +@Suppress("UnstableApiUsage") +class SlowUserQueryDetector : Detector(), SourceCodeScanner { + + override fun getApplicableMethodNames(): List<String> { + return listOf("getCurrentUser", "getUserInfo") + } + + override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) { + val evaluator = context.evaluator + if ( + evaluator.isStatic(method) && + method.name == "getCurrentUser" && + method.containingClass?.qualifiedName == "android.app.ActivityManager" + ) { + context.report( + ISSUE_SLOW_USER_ID_QUERY, + method, + context.getNameLocation(node), + "ActivityManager.getCurrentUser() is slow. " + + "Use UserTracker.getUserId() instead." + ) + } + if ( + !evaluator.isStatic(method) && + method.name == "getUserInfo" && + method.containingClass?.qualifiedName == "android.os.UserManager" + ) { + context.report( + ISSUE_SLOW_USER_INFO_QUERY, + method, + context.getNameLocation(node), + "UserManager.getUserInfo() is slow. " + "Use UserTracker.getUserInfo() instead." + ) + } + } + + companion object { + @JvmField + val ISSUE_SLOW_USER_ID_QUERY: Issue = + Issue.create( + id = "SlowUserIdQuery", + briefDescription = "User ID queried using ActivityManager instead of UserTracker.", + explanation = + "ActivityManager.getCurrentUser() makes a binder call and is slow. " + + "Instead, inject a UserTracker and call UserTracker.getUserId(). For " + + "more info, see: http://go/multi-user-in-systemui-slides", + category = Category.PERFORMANCE, + priority = 8, + severity = Severity.WARNING, + implementation = + Implementation(SlowUserQueryDetector::class.java, Scope.JAVA_FILE_SCOPE) + ) + + @JvmField + val ISSUE_SLOW_USER_INFO_QUERY: Issue = + Issue.create( + id = "SlowUserInfoQuery", + briefDescription = "User info queried using UserManager instead of UserTracker.", + explanation = + "UserManager.getUserInfo() makes a binder call and is slow. " + + "Instead, inject a UserTracker and call UserTracker.getUserInfo(). For " + + "more info, see: http://go/multi-user-in-systemui-slides", + category = Category.PERFORMANCE, + priority = 8, + severity = Severity.WARNING, + implementation = + Implementation(SlowUserQueryDetector::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 index c7c73d3c86a1..4879883e7c2e 100644 --- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt +++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt @@ -30,6 +30,8 @@ class SystemUIIssueRegistry : IssueRegistry() { get() = listOf( BindServiceViaContextDetector.ISSUE, BroadcastSentViaContextDetector.ISSUE, + SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY, + SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY, GetMainLooperViaContextDetector.ISSUE, RegisterReceiverViaContextDetector.ISSUE, SoftwareBitmapDetector.ISSUE, diff --git a/packages/SystemUI/checks/tests/com/android/systemui/lint/SlowUserQueryDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/systemui/lint/SlowUserQueryDetectorTest.kt new file mode 100644 index 000000000000..2738f0409fd0 --- /dev/null +++ b/packages/SystemUI/checks/tests/com/android/systemui/lint/SlowUserQueryDetectorTest.kt @@ -0,0 +1,194 @@ +package com.android.internal.systemui.lint + +import com.android.tools.lint.checks.infrastructure.LintDetectorTest +import com.android.tools.lint.checks.infrastructure.TestFile +import com.android.tools.lint.checks.infrastructure.TestFiles +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 SlowUserQueryDetectorTest : LintDetectorTest() { + + override fun getDetector(): Detector = SlowUserQueryDetector() + override fun lint(): TestLintTask = super.lint().allowMissingSdk(true) + + override fun getIssues(): List<Issue> = + listOf( + SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY, + SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY + ) + + @Test + fun testGetCurrentUser() { + lint() + .files( + TestFiles.java( + """ + package test.pkg; + import android.app.ActivityManager; + + public class TestClass1 { + public void slewlyGetCurrentUser() { + ActivityManager.getCurrentUser(); + } + } + """ + ) + .indented(), + *stubs + ) + .issues( + SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY, + SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY + ) + .run() + .expectWarningCount(1) + .expectContains( + "ActivityManager.getCurrentUser() is slow. " + + "Use UserTracker.getUserId() instead." + ) + } + + @Test + fun testGetUserInfo() { + lint() + .files( + TestFiles.java( + """ + package test.pkg; + import android.os.UserManager; + + public class TestClass2 { + public void slewlyGetUserInfo(UserManager userManager) { + userManager.getUserInfo(); + } + } + """ + ) + .indented(), + *stubs + ) + .issues( + SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY, + SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY + ) + .run() + .expectWarningCount(1) + .expectContains( + "UserManager.getUserInfo() is slow. " + "Use UserTracker.getUserInfo() instead." + ) + } + + @Test + fun testUserTrackerGetUserId() { + lint() + .files( + TestFiles.java( + """ + package test.pkg; + import com.android.systemui.settings.UserTracker; + + public class TestClass3 { + public void quicklyGetUserId(UserTracker userTracker) { + userTracker.getUserId(); + } + } + """ + ) + .indented(), + *stubs + ) + .issues( + SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY, + SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY + ) + .run() + .expectClean() + } + + @Test + fun testUserTrackerGetUserInfo() { + lint() + .files( + TestFiles.java( + """ + package test.pkg; + import com.android.systemui.settings.UserTracker; + + public class TestClass4 { + public void quicklyGetUserId(UserTracker userTracker) { + userTracker.getUserInfo(); + } + } + """ + ) + .indented(), + *stubs + ) + .issues( + SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY, + SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY + ) + .run() + .expectClean() + } + + private val activityManagerStub: TestFile = + java( + """ + package android.app; + + public class ActivityManager { + public static int getCurrentUser() {}; + } + """ + ) + + private val userManagerStub: TestFile = + java( + """ + package android.os; + import android.content.pm.UserInfo; + import android.annotation.UserIdInt; + + public class UserManager { + public UserInfo getUserInfo(@UserIdInt int userId) {}; + } + """ + ) + + private val userIdIntStub: TestFile = + java( + """ + package android.annotation; + + public @interface UserIdInt {} + """ + ) + + private val userInfoStub: TestFile = + java( + """ + package android.content.pm; + + public class UserInfo {} + """ + ) + + private val userTrackerStub: TestFile = + java( + """ + package com.android.systemui.settings; + import android.content.pm.UserInfo; + + public interface UserTracker { + public int getUserId(); + public UserInfo getUserInfo(); + } + """ + ) + + private val stubs = + arrayOf(activityManagerStub, userManagerStub, userIdIntStub, userInfoStub, userTrackerStub) +} |