New detector for getCurrentUser and getUserInfo

Detect slow calls to ActivityManager.getCurrentUser() or
UserManager.getUserInfo() and suggest using UserTracker instead.
For more info, see: http://go/multi-user-in-systemui-slides.

Test: manual
Bug: 238923086
Change-Id: I04a79ab3836a35a687d31af9e566a28d89aaeae6
This commit is contained in:
Peter Kalauskas
2022-07-08 23:41:47 +00:00
parent f8f1306eb8
commit 56d14cd7af
3 changed files with 299 additions and 0 deletions

View File

@@ -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)
)
}
}

View File

@@ -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,

View File

@@ -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)
}