Loading packages/SystemUI/checks/src/com/android/internal/systemui/lint/SlowUserQueryDetector.kt 0 → 100644 +103 −0 Original line number Diff line number Diff line /* * 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) ) } } packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt +2 −0 Original line number Diff line number Diff line Loading @@ -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, Loading packages/SystemUI/checks/tests/com/android/systemui/lint/SlowUserQueryDetectorTest.kt 0 → 100644 +194 −0 Original line number Diff line number Diff line 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) } Loading
packages/SystemUI/checks/src/com/android/internal/systemui/lint/SlowUserQueryDetector.kt 0 → 100644 +103 −0 Original line number Diff line number Diff line /* * 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) ) } }
packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt +2 −0 Original line number Diff line number Diff line Loading @@ -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, Loading
packages/SystemUI/checks/tests/com/android/systemui/lint/SlowUserQueryDetectorTest.kt 0 → 100644 +194 −0 Original line number Diff line number Diff line 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) }