Merge "Update linter formatting, add new tests" into tm-qpr-dev

This commit is contained in:
Lucas Dupin
2022-10-13 18:12:28 +00:00
committed by Android (Google) Code Review
19 changed files with 636 additions and 334 deletions

View File

@@ -0,0 +1,95 @@
/*
* 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.SdkConstants.CLASS_CONTEXT
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 com.intellij.psi.PsiModifierListOwner
import org.jetbrains.uast.UCallExpression
import org.jetbrains.uast.UClass
import org.jetbrains.uast.UMethod
import org.jetbrains.uast.getParentOfType
/**
* Warns if {@code Context.bindService}, {@code Context.bindServiceAsUser}, or {@code
* Context.unbindService} is not called on a {@code WorkerThread}
*/
@Suppress("UnstableApiUsage")
class BindServiceOnMainThreadDetector : Detector(), SourceCodeScanner {
override fun getApplicableMethodNames(): List<String> {
return listOf("bindService", "bindServiceAsUser", "unbindService")
}
private fun hasWorkerThreadAnnotation(
context: JavaContext,
annotated: PsiModifierListOwner?
): Boolean {
return context.evaluator.getAnnotations(annotated, inHierarchy = true).any { uAnnotation ->
uAnnotation.qualifiedName == "androidx.annotation.WorkerThread"
}
}
override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
if (context.evaluator.isMemberInSubClassOf(method, CLASS_CONTEXT)) {
if (
!hasWorkerThreadAnnotation(context, node.getParentOfType(UMethod::class.java)) &&
!hasWorkerThreadAnnotation(context, node.getParentOfType(UClass::class.java))
) {
context.report(
ISSUE,
method,
context.getLocation(node),
"This method should be annotated with `@WorkerThread` because " +
"it calls ${method.name}",
)
}
}
}
companion object {
@JvmField
val ISSUE: Issue =
Issue.create(
id = "BindServiceOnMainThread",
briefDescription = "Service bound or unbound on main thread",
explanation =
"""
Binding and unbinding services are synchronous calls to `ActivityManager`. \
They usually take multiple milliseconds to complete. If called on the main \
thread, it will likely cause missed frames. To fix it, use a `@Background \
Executor` and annotate the calling method with `@WorkerThread`.
""",
category = Category.PERFORMANCE,
priority = 8,
severity = Severity.WARNING,
implementation =
Implementation(
BindServiceOnMainThreadDetector::class.java,
Scope.JAVA_FILE_SCOPE
)
)
}
}

View File

@@ -16,6 +16,7 @@
package com.android.internal.systemui.lint package com.android.internal.systemui.lint
import com.android.SdkConstants.CLASS_CONTEXT
import com.android.tools.lint.detector.api.Category import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Implementation import com.android.tools.lint.detector.api.Implementation
@@ -48,14 +49,14 @@ class BroadcastSentViaContextDetector : Detector(), SourceCodeScanner {
return return
} }
val evaulator = context.evaluator val evaluator = context.evaluator
if (evaulator.isMemberInSubClassOf(method, "android.content.Context")) { if (evaluator.isMemberInSubClassOf(method, CLASS_CONTEXT)) {
context.report( context.report(
ISSUE, ISSUE,
method, method,
context.getNameLocation(node), context.getNameLocation(node),
"Please don't call sendBroadcast/sendBroadcastAsUser directly on " + "`Context.${method.name}()` should be replaced with " +
"Context, use com.android.systemui.broadcast.BroadcastSender instead." "`BroadcastSender.${method.name}()`"
) )
} }
} }
@@ -65,14 +66,14 @@ class BroadcastSentViaContextDetector : Detector(), SourceCodeScanner {
val ISSUE: Issue = val ISSUE: Issue =
Issue.create( Issue.create(
id = "BroadcastSentViaContext", id = "BroadcastSentViaContext",
briefDescription = "Broadcast sent via Context instead of BroadcastSender.", briefDescription = "Broadcast sent via `Context` instead of `BroadcastSender`",
explanation = // lint trims indents and converts \ to line continuations
"Broadcast was sent via " + explanation = """
"Context.sendBroadcast/Context.sendBroadcastAsUser. Please use " + Broadcasts sent via `Context.sendBroadcast()` or \
"BroadcastSender.sendBroadcast/BroadcastSender.sendBroadcastAsUser " + `Context.sendBroadcastAsUser()` will block the main thread and may cause \
"which will schedule dispatch of broadcasts on background thread. " + missed frames. Instead, use `BroadcastSender.sendBroadcast()` or \
"Sending broadcasts on main thread causes jank due to synchronous " + `BroadcastSender.sendBroadcastAsUser()` which will schedule and dispatch \
"Binder calls.", broadcasts on a background worker thread.""",
category = Category.PERFORMANCE, category = Category.PERFORMANCE,
priority = 8, priority = 8,
severity = Severity.WARNING, severity = Severity.WARNING,

View File

@@ -1,66 +0,0 @@
/*
* 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
@Suppress("UnstableApiUsage")
class GetMainLooperViaContextDetector : Detector(), SourceCodeScanner {
override fun getApplicableMethodNames(): List<String> {
return listOf("getMainThreadHandler", "getMainLooper", "getMainExecutor")
}
override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
if (context.evaluator.isMemberInSubClassOf(method, "android.content.Context")) {
context.report(
ISSUE,
method,
context.getNameLocation(node),
"Please inject a @Main Executor instead."
)
}
}
companion object {
@JvmField
val ISSUE: Issue =
Issue.create(
id = "GetMainLooperViaContextDetector",
briefDescription = "Please use idiomatic SystemUI executors, injecting " +
"them via Dagger.",
explanation = "Injecting the @Main Executor is preferred in order to make" +
"dependencies explicit and increase testability. It's much " +
"easier to pass a FakeExecutor on your test ctor than to " +
"deal with loopers in unit tests.",
category = Category.LINT,
priority = 8,
severity = Severity.WARNING,
implementation = Implementation(GetMainLooperViaContextDetector::class.java,
Scope.JAVA_FILE_SCOPE)
)
}
}

View File

@@ -16,6 +16,7 @@
package com.android.internal.systemui.lint package com.android.internal.systemui.lint
import com.android.SdkConstants.CLASS_CONTEXT
import com.android.tools.lint.detector.api.Category import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Implementation import com.android.tools.lint.detector.api.Implementation
@@ -28,20 +29,19 @@ import com.intellij.psi.PsiMethod
import org.jetbrains.uast.UCallExpression import org.jetbrains.uast.UCallExpression
@Suppress("UnstableApiUsage") @Suppress("UnstableApiUsage")
class BindServiceViaContextDetector : Detector(), SourceCodeScanner { class NonInjectedMainThreadDetector : Detector(), SourceCodeScanner {
override fun getApplicableMethodNames(): List<String> { override fun getApplicableMethodNames(): List<String> {
return listOf("bindService", "bindServiceAsUser", "unbindService") return listOf("getMainThreadHandler", "getMainLooper", "getMainExecutor")
} }
override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) { override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
if (context.evaluator.isMemberInSubClassOf(method, "android.content.Context")) { if (context.evaluator.isMemberInSubClassOf(method, CLASS_CONTEXT)) {
context.report( context.report(
ISSUE, ISSUE,
method, method,
context.getNameLocation(node), context.getNameLocation(node),
"Binding or unbinding services are synchronous calls, please make " + "Replace with injected `@Main Executor`."
"sure you're on a @Background Executor."
) )
} }
} }
@@ -50,18 +50,20 @@ class BindServiceViaContextDetector : Detector(), SourceCodeScanner {
@JvmField @JvmField
val ISSUE: Issue = val ISSUE: Issue =
Issue.create( Issue.create(
id = "BindServiceViaContextDetector", id = "NonInjectedMainThread",
briefDescription = "Service bound/unbound via Context, please make sure " + briefDescription = "Main thread usage without dependency injection",
"you're on a background thread.",
explanation = explanation =
"Binding or unbinding services are synchronous calls to ActivityManager, " + """
"they usually take multiple milliseconds to complete and will make" + Main thread should be injected using the `@Main Executor` instead \
"the caller drop frames. Make sure you're on a @Background Executor.", of using the accessors in `Context`. This is to make the \
category = Category.PERFORMANCE, dependencies explicit and increase testability. It's much easier \
to pass a `FakeExecutor` on test constructors than it is to deal \
with loopers in unit tests.""",
category = Category.LINT,
priority = 8, priority = 8,
severity = Severity.WARNING, severity = Severity.WARNING,
implementation = implementation =
Implementation(BindServiceViaContextDetector::class.java, Scope.JAVA_FILE_SCOPE) Implementation(NonInjectedMainThreadDetector::class.java, Scope.JAVA_FILE_SCOPE)
) )
} }
} }

View File

@@ -16,6 +16,7 @@
package com.android.internal.systemui.lint package com.android.internal.systemui.lint
import com.android.SdkConstants.CLASS_CONTEXT
import com.android.tools.lint.detector.api.Category import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Implementation import com.android.tools.lint.detector.api.Implementation
@@ -32,7 +33,7 @@ import org.jetbrains.uast.UCallExpression
class NonInjectedServiceDetector : Detector(), SourceCodeScanner { class NonInjectedServiceDetector : Detector(), SourceCodeScanner {
override fun getApplicableMethodNames(): List<String> { override fun getApplicableMethodNames(): List<String> {
return listOf("getSystemService") return listOf("getSystemService", "get")
} }
override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) { override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
@@ -40,14 +41,25 @@ class NonInjectedServiceDetector : Detector(), SourceCodeScanner {
if ( if (
!evaluator.isStatic(method) && !evaluator.isStatic(method) &&
method.name == "getSystemService" && method.name == "getSystemService" &&
method.containingClass?.qualifiedName == "android.content.Context" method.containingClass?.qualifiedName == CLASS_CONTEXT
) { ) {
context.report( context.report(
ISSUE, ISSUE,
method, method,
context.getNameLocation(node), context.getNameLocation(node),
"Use @Inject to get the handle to a system-level services instead of using " + "Use `@Inject` to get system-level service handles instead of " +
"Context.getSystemService()" "`Context.getSystemService()`"
)
} else if (
evaluator.isStatic(method) &&
method.name == "get" &&
method.containingClass?.qualifiedName == "android.accounts.AccountManager"
) {
context.report(
ISSUE,
method,
context.getNameLocation(node),
"Replace `AccountManager.get()` with an injected instance of `AccountManager`"
) )
} }
} }
@@ -57,14 +69,14 @@ class NonInjectedServiceDetector : Detector(), SourceCodeScanner {
val ISSUE: Issue = val ISSUE: Issue =
Issue.create( Issue.create(
id = "NonInjectedService", id = "NonInjectedService",
briefDescription = briefDescription = "System service not injected",
"System-level services should be retrieved using " +
"@Inject instead of Context.getSystemService().",
explanation = explanation =
"Context.getSystemService() should be avoided because it makes testing " + """
"difficult. Instead, use an injected service. For example, " + `Context.getSystemService()` should be avoided because it makes testing \
"instead of calling Context.getSystemService(UserManager.class), " + difficult. Instead, use an injected service. For example, instead of calling \
"use @Inject and add UserManager to the constructor", `Context.getSystemService(UserManager.class)` in a class, annotate the class' \
constructor with `@Inject` and add `UserManager` to the parameters.
""",
category = Category.CORRECTNESS, category = Category.CORRECTNESS,
priority = 8, priority = 8,
severity = Severity.WARNING, severity = Severity.WARNING,

View File

@@ -16,6 +16,7 @@
package com.android.internal.systemui.lint package com.android.internal.systemui.lint
import com.android.SdkConstants.CLASS_CONTEXT
import com.android.tools.lint.detector.api.Category import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Implementation import com.android.tools.lint.detector.api.Implementation
@@ -35,12 +36,12 @@ class RegisterReceiverViaContextDetector : Detector(), SourceCodeScanner {
} }
override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) { override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
if (context.evaluator.isMemberInSubClassOf(method, "android.content.Context")) { if (context.evaluator.isMemberInSubClassOf(method, CLASS_CONTEXT)) {
context.report( context.report(
ISSUE, ISSUE,
method, method,
context.getNameLocation(node), context.getNameLocation(node),
"BroadcastReceivers should be registered via BroadcastDispatcher." "Register `BroadcastReceiver` using `BroadcastDispatcher` instead of `Context`"
) )
} }
} }
@@ -49,14 +50,16 @@ class RegisterReceiverViaContextDetector : Detector(), SourceCodeScanner {
@JvmField @JvmField
val ISSUE: Issue = val ISSUE: Issue =
Issue.create( Issue.create(
id = "RegisterReceiverViaContextDetector", id = "RegisterReceiverViaContext",
briefDescription = "Broadcast registrations via Context are blocking " + briefDescription = "Blocking broadcast registration",
"calls. Please use BroadcastDispatcher.", // lint trims indents and converts \ to line continuations
explanation = explanation = """
"Context#registerReceiver is a blocking call to the system server, " + `Context.registerReceiver()` is a blocking call to the system server, \
"making it very likely that you'll drop a frame. Please use " + making it very likely that you'll drop a frame. Please use \
"BroadcastDispatcher instead (or move this call to a " + `BroadcastDispatcher` instead, which registers the receiver on a \
"@Background Executor.)", background thread. `BroadcastDispatcher` also improves our visibility \
into ANRs.""",
moreInfo = "go/identifying-broadcast-threads",
category = Category.PERFORMANCE, category = Category.PERFORMANCE,
priority = 8, priority = 8,
severity = Severity.WARNING, severity = Severity.WARNING,

View File

@@ -49,8 +49,7 @@ class SlowUserQueryDetector : Detector(), SourceCodeScanner {
ISSUE_SLOW_USER_ID_QUERY, ISSUE_SLOW_USER_ID_QUERY,
method, method,
context.getNameLocation(node), context.getNameLocation(node),
"ActivityManager.getCurrentUser() is slow. " + "Use `UserTracker.getUserId()` instead of `ActivityManager.getCurrentUser()`"
"Use UserTracker.getUserId() instead."
) )
} }
if ( if (
@@ -62,7 +61,7 @@ class SlowUserQueryDetector : Detector(), SourceCodeScanner {
ISSUE_SLOW_USER_INFO_QUERY, ISSUE_SLOW_USER_INFO_QUERY,
method, method,
context.getNameLocation(node), context.getNameLocation(node),
"UserManager.getUserInfo() is slow. " + "Use UserTracker.getUserInfo() instead." "Use `UserTracker.getUserInfo()` instead of `UserManager.getUserInfo()`"
) )
} }
} }
@@ -72,11 +71,13 @@ class SlowUserQueryDetector : Detector(), SourceCodeScanner {
val ISSUE_SLOW_USER_ID_QUERY: Issue = val ISSUE_SLOW_USER_ID_QUERY: Issue =
Issue.create( Issue.create(
id = "SlowUserIdQuery", id = "SlowUserIdQuery",
briefDescription = "User ID queried using ActivityManager instead of UserTracker.", briefDescription = "User ID queried using ActivityManager",
explanation = explanation =
"ActivityManager.getCurrentUser() makes a binder call and is slow. " + """
"Instead, inject a UserTracker and call UserTracker.getUserId(). For " + `ActivityManager.getCurrentUser()` uses a blocking binder call and is slow. \
"more info, see: http://go/multi-user-in-systemui-slides", Instead, inject a `UserTracker` and call `UserTracker.getUserId()`.
""",
moreInfo = "http://go/multi-user-in-systemui-slides",
category = Category.PERFORMANCE, category = Category.PERFORMANCE,
priority = 8, priority = 8,
severity = Severity.WARNING, severity = Severity.WARNING,
@@ -88,11 +89,13 @@ class SlowUserQueryDetector : Detector(), SourceCodeScanner {
val ISSUE_SLOW_USER_INFO_QUERY: Issue = val ISSUE_SLOW_USER_INFO_QUERY: Issue =
Issue.create( Issue.create(
id = "SlowUserInfoQuery", id = "SlowUserInfoQuery",
briefDescription = "User info queried using UserManager instead of UserTracker.", briefDescription = "User info queried using UserManager",
explanation = explanation =
"UserManager.getUserInfo() makes a binder call and is slow. " + """
"Instead, inject a UserTracker and call UserTracker.getUserInfo(). For " + `UserManager.getUserInfo()` uses a blocking binder call and is slow. \
"more info, see: http://go/multi-user-in-systemui-slides", Instead, inject a `UserTracker` and call `UserTracker.getUserInfo()`.
""",
moreInfo = "http://go/multi-user-in-systemui-slides",
category = Category.PERFORMANCE, category = Category.PERFORMANCE,
priority = 8, priority = 8,
severity = Severity.WARNING, severity = Severity.WARNING,

View File

@@ -47,7 +47,7 @@ class SoftwareBitmapDetector : Detector(), SourceCodeScanner {
ISSUE, ISSUE,
referenced, referenced,
context.getNameLocation(referenced), context.getNameLocation(referenced),
"Usage of Config.HARDWARE is highly encouraged." "Replace software bitmap with `Config.HARDWARE`"
) )
} }
} }
@@ -56,12 +56,12 @@ class SoftwareBitmapDetector : Detector(), SourceCodeScanner {
@JvmField @JvmField
val ISSUE: Issue = val ISSUE: Issue =
Issue.create( Issue.create(
id = "SoftwareBitmapDetector", id = "SoftwareBitmap",
briefDescription = "Software bitmap detected. Please use Config.HARDWARE instead.", briefDescription = "Software bitmap",
explanation = explanation = """
"Software bitmaps occupy twice as much memory, when compared to Config.HARDWARE. " + Software bitmaps occupy twice as much memory as `Config.HARDWARE` bitmaps \
"In case you need to manipulate the pixels, please consider to either use" + do. However, hardware bitmaps are read-only. If you need to manipulate the \
"a shader (encouraged), or a short lived software bitmap.", pixels, use a shader (preferably) or a short lived software bitmap.""",
category = Category.PERFORMANCE, category = Category.PERFORMANCE,
priority = 8, priority = 8,
severity = Severity.WARNING, severity = Severity.WARNING,

View File

@@ -28,11 +28,11 @@ class SystemUIIssueRegistry : IssueRegistry() {
override val issues: List<Issue> override val issues: List<Issue>
get() = listOf( get() = listOf(
BindServiceViaContextDetector.ISSUE, BindServiceOnMainThreadDetector.ISSUE,
BroadcastSentViaContextDetector.ISSUE, BroadcastSentViaContextDetector.ISSUE,
SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY, SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY, SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY,
GetMainLooperViaContextDetector.ISSUE, NonInjectedMainThreadDetector.ISSUE,
RegisterReceiverViaContextDetector.ISSUE, RegisterReceiverViaContextDetector.ISSUE,
SoftwareBitmapDetector.ISSUE, SoftwareBitmapDetector.ISSUE,
NonInjectedServiceDetector.ISSUE, NonInjectedServiceDetector.ISSUE,

View File

@@ -16,16 +16,21 @@
package com.android.internal.systemui.lint package com.android.internal.systemui.lint
import com.android.annotations.NonNull
import com.android.tools.lint.checks.infrastructure.LintDetectorTest.java import com.android.tools.lint.checks.infrastructure.LintDetectorTest.java
import org.intellij.lang.annotations.Language
@Suppress("UnstableApiUsage")
@NonNull
private fun indentedJava(@NonNull @Language("JAVA") source: String) = java(source).indented()
/* /*
* This file contains stubs of framework APIs and System UI classes for testing purposes only. The * This file contains stubs of framework APIs and System UI classes for testing purposes only. The
* stubs are not used in the lint detectors themselves. * stubs are not used in the lint detectors themselves.
*/ */
@Suppress("UnstableApiUsage")
internal val androidStubs = internal val androidStubs =
arrayOf( arrayOf(
java( indentedJava(
""" """
package android.app; package android.app;
@@ -34,7 +39,16 @@ public class ActivityManager {
} }
""" """
), ),
java( indentedJava(
"""
package android.accounts;
public class AccountManager {
public static AccountManager get(Context context) { return null; }
}
"""
),
indentedJava(
""" """
package android.os; package android.os;
import android.content.pm.UserInfo; import android.content.pm.UserInfo;
@@ -45,39 +59,39 @@ public class UserManager {
} }
""" """
), ),
java(""" indentedJava("""
package android.annotation; package android.annotation;
public @interface UserIdInt {} public @interface UserIdInt {}
"""), """),
java(""" indentedJava("""
package android.content.pm; package android.content.pm;
public class UserInfo {} public class UserInfo {}
"""), """),
java(""" indentedJava("""
package android.os; package android.os;
public class Looper {} public class Looper {}
"""), """),
java(""" indentedJava("""
package android.os; package android.os;
public class Handler {} public class Handler {}
"""), """),
java(""" indentedJava("""
package android.content; package android.content;
public class ServiceConnection {} public class ServiceConnection {}
"""), """),
java(""" indentedJava("""
package android.os; package android.os;
public enum UserHandle { public enum UserHandle {
ALL ALL
} }
"""), """),
java( indentedJava(
""" """
package android.content; package android.content;
import android.os.UserHandle; import android.os.UserHandle;
@@ -108,7 +122,7 @@ public class Context {
} }
""" """
), ),
java( indentedJava(
""" """
package android.app; package android.app;
import android.content.Context; import android.content.Context;
@@ -116,7 +130,7 @@ import android.content.Context;
public class Activity extends Context {} public class Activity extends Context {}
""" """
), ),
java( indentedJava(
""" """
package android.graphics; package android.graphics;
@@ -132,17 +146,17 @@ public class Bitmap {
} }
""" """
), ),
java(""" indentedJava("""
package android.content; package android.content;
public class BroadcastReceiver {} public class BroadcastReceiver {}
"""), """),
java(""" indentedJava("""
package android.content; package android.content;
public class IntentFilter {} public class IntentFilter {}
"""), """),
java( indentedJava(
""" """
package com.android.systemui.settings; package com.android.systemui.settings;
import android.content.pm.UserInfo; import android.content.pm.UserInfo;
@@ -151,6 +165,25 @@ public interface UserTracker {
int getUserId(); int getUserId();
UserInfo getUserInfo(); UserInfo getUserInfo();
} }
"""
),
indentedJava(
"""
package androidx.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@Retention(SOURCE)
@Target({METHOD,CONSTRUCTOR,TYPE,PARAMETER})
public @interface WorkerThread {
}
""" """
), ),
) )

View File

@@ -0,0 +1,204 @@
/*
* 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.checks.infrastructure.LintDetectorTest
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
@Suppress("UnstableApiUsage")
class BindServiceOnMainThreadDetectorTest : LintDetectorTest() {
override fun getDetector(): Detector = BindServiceOnMainThreadDetector()
override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
override fun getIssues(): List<Issue> = listOf(BindServiceOnMainThreadDetector.ISSUE)
@Test
fun testBindService() {
lint()
.files(
TestFiles.java(
"""
package test.pkg;
import android.content.Context;
public class TestClass {
public void bind(Context context) {
Intent intent = new Intent(Intent.ACTION_VIEW);
context.bindService(intent, null, 0);
}
}
"""
)
.indented(),
*stubs
)
.issues(BindServiceOnMainThreadDetector.ISSUE)
.run()
.expect(
"""
src/test/pkg/TestClass.java:7: Warning: This method should be annotated with @WorkerThread because it calls bindService [BindServiceOnMainThread]
context.bindService(intent, null, 0);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0 errors, 1 warnings
"""
)
}
@Test
fun testBindServiceAsUser() {
lint()
.files(
TestFiles.java(
"""
package test.pkg;
import android.content.Context;
import android.os.UserHandle;
public class TestClass {
public void bind(Context context) {
Intent intent = new Intent(Intent.ACTION_VIEW);
context.bindServiceAsUser(intent, null, 0, UserHandle.ALL);
}
}
"""
)
.indented(),
*stubs
)
.issues(BindServiceOnMainThreadDetector.ISSUE)
.run()
.expect(
"""
src/test/pkg/TestClass.java:8: Warning: This method should be annotated with @WorkerThread because it calls bindServiceAsUser [BindServiceOnMainThread]
context.bindServiceAsUser(intent, null, 0, UserHandle.ALL);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0 errors, 1 warnings
"""
)
}
@Test
fun testUnbindService() {
lint()
.files(
TestFiles.java(
"""
package test.pkg;
import android.content.Context;
import android.content.ServiceConnection;
public class TestClass {
public void unbind(Context context, ServiceConnection connection) {
context.unbindService(connection);
}
}
"""
)
.indented(),
*stubs
)
.issues(BindServiceOnMainThreadDetector.ISSUE)
.run()
.expect(
"""
src/test/pkg/TestClass.java:7: Warning: This method should be annotated with @WorkerThread because it calls unbindService [BindServiceOnMainThread]
context.unbindService(connection);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0 errors, 1 warnings
"""
)
}
@Test
fun testWorkerMethod() {
lint()
.files(
TestFiles.java(
"""
package test.pkg;
import android.content.Context;
import android.content.ServiceConnection;
import androidx.annotation.WorkerThread;
public class TestClass {
@WorkerThread
public void unbind(Context context, ServiceConnection connection) {
context.unbindService(connection);
}
}
public class ChildTestClass extends TestClass {
@Override
public void unbind(Context context, ServiceConnection connection) {
context.unbindService(connection);
}
}
"""
)
.indented(),
*stubs
)
.issues(BindServiceOnMainThreadDetector.ISSUE)
.run()
.expectClean()
}
@Test
fun testWorkerClass() {
lint()
.files(
TestFiles.java(
"""
package test.pkg;
import android.content.Context;
import android.content.ServiceConnection;
import androidx.annotation.WorkerThread;
@WorkerThread
public class TestClass {
public void unbind(Context context, ServiceConnection connection) {
context.unbindService(connection);
}
}
public class ChildTestClass extends TestClass {
@Override
public void unbind(Context context, ServiceConnection connection) {
context.unbindService(connection);
}
public void bind(Context context, ServiceConnection connection) {
context.bind(connection);
}
}
"""
)
.indented(),
*stubs
)
.issues(BindServiceOnMainThreadDetector.ISSUE)
.run()
.expectClean()
}
private val stubs = androidStubs
}

View File

@@ -1,116 +0,0 @@
/*
* 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.checks.infrastructure.LintDetectorTest
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
@Suppress("UnstableApiUsage")
class BindServiceViaContextDetectorTest : LintDetectorTest() {
override fun getDetector(): Detector = BindServiceViaContextDetector()
override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
override fun getIssues(): List<Issue> = listOf(BindServiceViaContextDetector.ISSUE)
private val explanation = "Binding or unbinding services are synchronous calls"
@Test
fun testBindService() {
lint()
.files(
TestFiles.java(
"""
package test.pkg;
import android.content.Context;
public class TestClass1 {
public void bind(Context context) {
Intent intent = new Intent(Intent.ACTION_VIEW);
context.bindService(intent, null, 0);
}
}
"""
)
.indented(),
*stubs
)
.issues(BindServiceViaContextDetector.ISSUE)
.run()
.expectWarningCount(1)
.expectContains(explanation)
}
@Test
fun testBindServiceAsUser() {
lint()
.files(
TestFiles.java(
"""
package test.pkg;
import android.content.Context;
import android.os.UserHandle;
public class TestClass1 {
public void bind(Context context) {
Intent intent = new Intent(Intent.ACTION_VIEW);
context.bindServiceAsUser(intent, null, 0, UserHandle.ALL);
}
}
"""
)
.indented(),
*stubs
)
.issues(BindServiceViaContextDetector.ISSUE)
.run()
.expectWarningCount(1)
.expectContains(explanation)
}
@Test
fun testUnbindService() {
lint()
.files(
TestFiles.java(
"""
package test.pkg;
import android.content.Context;
import android.content.ServiceConnection;
public class TestClass1 {
public void unbind(Context context, ServiceConnection connection) {
context.unbindService(connection);
}
}
"""
)
.indented(),
*stubs
)
.issues(BindServiceViaContextDetector.ISSUE)
.run()
.expectWarningCount(1)
.expectContains(explanation)
}
private val stubs = androidStubs
}

View File

@@ -41,7 +41,7 @@ class BroadcastSentViaContextDetectorTest : LintDetectorTest() {
package test.pkg; package test.pkg;
import android.content.Context; import android.content.Context;
public class TestClass1 { public class TestClass {
public void send(Context context) { public void send(Context context) {
Intent intent = new Intent(Intent.ACTION_VIEW); Intent intent = new Intent(Intent.ACTION_VIEW);
context.sendBroadcast(intent); context.sendBroadcast(intent);
@@ -54,10 +54,13 @@ class BroadcastSentViaContextDetectorTest : LintDetectorTest() {
) )
.issues(BroadcastSentViaContextDetector.ISSUE) .issues(BroadcastSentViaContextDetector.ISSUE)
.run() .run()
.expectWarningCount(1) .expect(
.expectContains( """
"Please don't call sendBroadcast/sendBroadcastAsUser directly on " + src/test/pkg/TestClass.java:7: Warning: Context.sendBroadcast() should be replaced with BroadcastSender.sendBroadcast() [BroadcastSentViaContext]
"Context, use com.android.systemui.broadcast.BroadcastSender instead." context.sendBroadcast(intent);
~~~~~~~~~~~~~
0 errors, 1 warnings
"""
) )
} }
@@ -71,7 +74,7 @@ class BroadcastSentViaContextDetectorTest : LintDetectorTest() {
import android.content.Context; import android.content.Context;
import android.os.UserHandle; import android.os.UserHandle;
public class TestClass1 { public class TestClass {
public void send(Context context) { public void send(Context context) {
Intent intent = new Intent(Intent.ACTION_VIEW); Intent intent = new Intent(Intent.ACTION_VIEW);
context.sendBroadcastAsUser(intent, UserHandle.ALL, "permission"); context.sendBroadcastAsUser(intent, UserHandle.ALL, "permission");
@@ -84,10 +87,13 @@ class BroadcastSentViaContextDetectorTest : LintDetectorTest() {
) )
.issues(BroadcastSentViaContextDetector.ISSUE) .issues(BroadcastSentViaContextDetector.ISSUE)
.run() .run()
.expectWarningCount(1) .expect(
.expectContains( """
"Please don't call sendBroadcast/sendBroadcastAsUser directly on " + src/test/pkg/TestClass.java:8: Warning: Context.sendBroadcastAsUser() should be replaced with BroadcastSender.sendBroadcastAsUser() [BroadcastSentViaContext]
"Context, use com.android.systemui.broadcast.BroadcastSender instead." context.sendBroadcastAsUser(intent, UserHandle.ALL, "permission");
~~~~~~~~~~~~~~~~~~~
0 errors, 1 warnings
"""
) )
} }
@@ -101,7 +107,7 @@ class BroadcastSentViaContextDetectorTest : LintDetectorTest() {
import android.app.Activity; import android.app.Activity;
import android.os.UserHandle; import android.os.UserHandle;
public class TestClass1 { public class TestClass {
public void send(Activity activity) { public void send(Activity activity) {
Intent intent = new Intent(Intent.ACTION_VIEW); Intent intent = new Intent(Intent.ACTION_VIEW);
activity.sendBroadcastAsUser(intent, UserHandle.ALL, "permission"); activity.sendBroadcastAsUser(intent, UserHandle.ALL, "permission");
@@ -115,13 +121,43 @@ class BroadcastSentViaContextDetectorTest : LintDetectorTest() {
) )
.issues(BroadcastSentViaContextDetector.ISSUE) .issues(BroadcastSentViaContextDetector.ISSUE)
.run() .run()
.expectWarningCount(1) .expect(
.expectContains( """
"Please don't call sendBroadcast/sendBroadcastAsUser directly on " + src/test/pkg/TestClass.java:8: Warning: Context.sendBroadcastAsUser() should be replaced with BroadcastSender.sendBroadcastAsUser() [BroadcastSentViaContext]
"Context, use com.android.systemui.broadcast.BroadcastSender instead." activity.sendBroadcastAsUser(intent, UserHandle.ALL, "permission");
~~~~~~~~~~~~~~~~~~~
0 errors, 1 warnings
"""
) )
} }
@Test
fun testSendBroadcastInBroadcastSender() {
lint()
.files(
TestFiles.java(
"""
package com.android.systemui.broadcast;
import android.app.Activity;
import android.os.UserHandle;
public class BroadcastSender {
public void send(Activity activity) {
Intent intent = new Intent(Intent.ACTION_VIEW);
activity.sendBroadcastAsUser(intent, UserHandle.ALL, "permission");
}
}
"""
)
.indented(),
*stubs
)
.issues(BroadcastSentViaContextDetector.ISSUE)
.run()
.expectClean()
}
@Test @Test
fun testNoopIfNoCall() { fun testNoopIfNoCall() {
lint() lint()
@@ -131,7 +167,7 @@ class BroadcastSentViaContextDetectorTest : LintDetectorTest() {
package test.pkg; package test.pkg;
import android.content.Context; import android.content.Context;
public class TestClass1 { public class TestClass {
public void sendBroadcast() { public void sendBroadcast() {
Intent intent = new Intent(Intent.ACTION_VIEW); Intent intent = new Intent(Intent.ACTION_VIEW);
context.startActivity(intent); context.startActivity(intent);

View File

@@ -24,14 +24,12 @@ import com.android.tools.lint.detector.api.Issue
import org.junit.Test import org.junit.Test
@Suppress("UnstableApiUsage") @Suppress("UnstableApiUsage")
class GetMainLooperViaContextDetectorTest : LintDetectorTest() { class NonInjectedMainThreadDetectorTest : LintDetectorTest() {
override fun getDetector(): Detector = GetMainLooperViaContextDetector() override fun getDetector(): Detector = NonInjectedMainThreadDetector()
override fun lint(): TestLintTask = super.lint().allowMissingSdk(true) override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
override fun getIssues(): List<Issue> = listOf(GetMainLooperViaContextDetector.ISSUE) override fun getIssues(): List<Issue> = listOf(NonInjectedMainThreadDetector.ISSUE)
private val explanation = "Please inject a @Main Executor instead."
@Test @Test
fun testGetMainThreadHandler() { fun testGetMainThreadHandler() {
@@ -43,7 +41,7 @@ class GetMainLooperViaContextDetectorTest : LintDetectorTest() {
import android.content.Context; import android.content.Context;
import android.os.Handler; import android.os.Handler;
public class TestClass1 { public class TestClass {
public void test(Context context) { public void test(Context context) {
Handler mainThreadHandler = context.getMainThreadHandler(); Handler mainThreadHandler = context.getMainThreadHandler();
} }
@@ -53,10 +51,16 @@ class GetMainLooperViaContextDetectorTest : LintDetectorTest() {
.indented(), .indented(),
*stubs *stubs
) )
.issues(GetMainLooperViaContextDetector.ISSUE) .issues(NonInjectedMainThreadDetector.ISSUE)
.run() .run()
.expectWarningCount(1) .expect(
.expectContains(explanation) """
src/test/pkg/TestClass.java:7: Warning: Replace with injected @Main Executor. [NonInjectedMainThread]
Handler mainThreadHandler = context.getMainThreadHandler();
~~~~~~~~~~~~~~~~~~~~
0 errors, 1 warnings
"""
)
} }
@Test @Test
@@ -69,7 +73,7 @@ class GetMainLooperViaContextDetectorTest : LintDetectorTest() {
import android.content.Context; import android.content.Context;
import android.os.Looper; import android.os.Looper;
public class TestClass1 { public class TestClass {
public void test(Context context) { public void test(Context context) {
Looper mainLooper = context.getMainLooper(); Looper mainLooper = context.getMainLooper();
} }
@@ -79,10 +83,16 @@ class GetMainLooperViaContextDetectorTest : LintDetectorTest() {
.indented(), .indented(),
*stubs *stubs
) )
.issues(GetMainLooperViaContextDetector.ISSUE) .issues(NonInjectedMainThreadDetector.ISSUE)
.run() .run()
.expectWarningCount(1) .expect(
.expectContains(explanation) """
src/test/pkg/TestClass.java:7: Warning: Replace with injected @Main Executor. [NonInjectedMainThread]
Looper mainLooper = context.getMainLooper();
~~~~~~~~~~~~~
0 errors, 1 warnings
"""
)
} }
@Test @Test
@@ -95,7 +105,7 @@ class GetMainLooperViaContextDetectorTest : LintDetectorTest() {
import android.content.Context; import android.content.Context;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
public class TestClass1 { public class TestClass {
public void test(Context context) { public void test(Context context) {
Executor mainExecutor = context.getMainExecutor(); Executor mainExecutor = context.getMainExecutor();
} }
@@ -105,10 +115,16 @@ class GetMainLooperViaContextDetectorTest : LintDetectorTest() {
.indented(), .indented(),
*stubs *stubs
) )
.issues(GetMainLooperViaContextDetector.ISSUE) .issues(NonInjectedMainThreadDetector.ISSUE)
.run() .run()
.expectWarningCount(1) .expect(
.expectContains(explanation) """
src/test/pkg/TestClass.java:7: Warning: Replace with injected @Main Executor. [NonInjectedMainThread]
Executor mainExecutor = context.getMainExecutor();
~~~~~~~~~~~~~~~
0 errors, 1 warnings
"""
)
} }
private val stubs = androidStubs private val stubs = androidStubs

View File

@@ -39,7 +39,7 @@ class NonInjectedServiceDetectorTest : LintDetectorTest() {
package test.pkg; package test.pkg;
import android.content.Context; import android.content.Context;
public class TestClass1 { public class TestClass {
public void getSystemServiceWithoutDagger(Context context) { public void getSystemServiceWithoutDagger(Context context) {
context.getSystemService("user"); context.getSystemService("user");
} }
@@ -51,8 +51,14 @@ class NonInjectedServiceDetectorTest : LintDetectorTest() {
) )
.issues(NonInjectedServiceDetector.ISSUE) .issues(NonInjectedServiceDetector.ISSUE)
.run() .run()
.expectWarningCount(1) .expect(
.expectContains("Use @Inject to get the handle") """
src/test/pkg/TestClass.java:6: Warning: Use @Inject to get system-level service handles instead of Context.getSystemService() [NonInjectedService]
context.getSystemService("user");
~~~~~~~~~~~~~~~~
0 errors, 1 warnings
"""
)
} }
@Test @Test
@@ -65,7 +71,7 @@ class NonInjectedServiceDetectorTest : LintDetectorTest() {
import android.content.Context; import android.content.Context;
import android.os.UserManager; import android.os.UserManager;
public class TestClass2 { public class TestClass {
public void getSystemServiceWithoutDagger(Context context) { public void getSystemServiceWithoutDagger(Context context) {
context.getSystemService(UserManager.class); context.getSystemService(UserManager.class);
} }
@@ -77,8 +83,46 @@ class NonInjectedServiceDetectorTest : LintDetectorTest() {
) )
.issues(NonInjectedServiceDetector.ISSUE) .issues(NonInjectedServiceDetector.ISSUE)
.run() .run()
.expectWarningCount(1) .expect(
.expectContains("Use @Inject to get the handle") """
src/test/pkg/TestClass.java:7: Warning: Use @Inject to get system-level service handles instead of Context.getSystemService() [NonInjectedService]
context.getSystemService(UserManager.class);
~~~~~~~~~~~~~~~~
0 errors, 1 warnings
"""
)
}
@Test
fun testGetAccountManager() {
lint()
.files(
TestFiles.java(
"""
package test.pkg;
import android.content.Context;
import android.accounts.AccountManager;
public class TestClass {
public void getSystemServiceWithoutDagger(Context context) {
AccountManager.get(context);
}
}
"""
)
.indented(),
*stubs
)
.issues(NonInjectedServiceDetector.ISSUE)
.run()
.expect(
"""
src/test/pkg/TestClass.java:7: Warning: Replace AccountManager.get() with an injected instance of AccountManager [NonInjectedService]
AccountManager.get(context);
~~~
0 errors, 1 warnings
"""
)
} }
private val stubs = androidStubs private val stubs = androidStubs

View File

@@ -31,8 +31,6 @@ class RegisterReceiverViaContextDetectorTest : LintDetectorTest() {
override fun getIssues(): List<Issue> = listOf(RegisterReceiverViaContextDetector.ISSUE) override fun getIssues(): List<Issue> = listOf(RegisterReceiverViaContextDetector.ISSUE)
private val explanation = "BroadcastReceivers should be registered via BroadcastDispatcher."
@Test @Test
fun testRegisterReceiver() { fun testRegisterReceiver() {
lint() lint()
@@ -44,7 +42,7 @@ class RegisterReceiverViaContextDetectorTest : LintDetectorTest() {
import android.content.Context; import android.content.Context;
import android.content.IntentFilter; import android.content.IntentFilter;
public class TestClass1 { public class TestClass {
public void bind(Context context, BroadcastReceiver receiver, public void bind(Context context, BroadcastReceiver receiver,
IntentFilter filter) { IntentFilter filter) {
context.registerReceiver(receiver, filter, 0); context.registerReceiver(receiver, filter, 0);
@@ -57,8 +55,14 @@ class RegisterReceiverViaContextDetectorTest : LintDetectorTest() {
) )
.issues(RegisterReceiverViaContextDetector.ISSUE) .issues(RegisterReceiverViaContextDetector.ISSUE)
.run() .run()
.expectWarningCount(1) .expect(
.expectContains(explanation) """
src/test/pkg/TestClass.java:9: Warning: Register BroadcastReceiver using BroadcastDispatcher instead of Context [RegisterReceiverViaContext]
context.registerReceiver(receiver, filter, 0);
~~~~~~~~~~~~~~~~
0 errors, 1 warnings
"""
)
} }
@Test @Test
@@ -74,7 +78,7 @@ class RegisterReceiverViaContextDetectorTest : LintDetectorTest() {
import android.os.Handler; import android.os.Handler;
import android.os.UserHandle; import android.os.UserHandle;
public class TestClass1 { public class TestClass {
public void bind(Context context, BroadcastReceiver receiver, public void bind(Context context, BroadcastReceiver receiver,
IntentFilter filter, Handler handler) { IntentFilter filter, Handler handler) {
context.registerReceiverAsUser(receiver, UserHandle.ALL, filter, context.registerReceiverAsUser(receiver, UserHandle.ALL, filter,
@@ -88,8 +92,14 @@ class RegisterReceiverViaContextDetectorTest : LintDetectorTest() {
) )
.issues(RegisterReceiverViaContextDetector.ISSUE) .issues(RegisterReceiverViaContextDetector.ISSUE)
.run() .run()
.expectWarningCount(1) .expect(
.expectContains(explanation) """
src/test/pkg/TestClass.java:11: Warning: Register BroadcastReceiver using BroadcastDispatcher instead of Context [RegisterReceiverViaContext]
context.registerReceiverAsUser(receiver, UserHandle.ALL, filter,
~~~~~~~~~~~~~~~~~~~~~~
0 errors, 1 warnings
"""
)
} }
@Test @Test
@@ -105,7 +115,7 @@ class RegisterReceiverViaContextDetectorTest : LintDetectorTest() {
import android.os.Handler; import android.os.Handler;
import android.os.UserHandle; import android.os.UserHandle;
public class TestClass1 { public class TestClass {
public void bind(Context context, BroadcastReceiver receiver, public void bind(Context context, BroadcastReceiver receiver,
IntentFilter filter, Handler handler) { IntentFilter filter, Handler handler) {
context.registerReceiverForAllUsers(receiver, filter, "permission", context.registerReceiverForAllUsers(receiver, filter, "permission",
@@ -119,8 +129,14 @@ class RegisterReceiverViaContextDetectorTest : LintDetectorTest() {
) )
.issues(RegisterReceiverViaContextDetector.ISSUE) .issues(RegisterReceiverViaContextDetector.ISSUE)
.run() .run()
.expectWarningCount(1) .expect(
.expectContains(explanation) """
src/test/pkg/TestClass.java:11: Warning: Register BroadcastReceiver using BroadcastDispatcher instead of Context [RegisterReceiverViaContext]
context.registerReceiverForAllUsers(receiver, filter, "permission",
~~~~~~~~~~~~~~~~~~~~~~~~~~~
0 errors, 1 warnings
"""
)
} }
private val stubs = androidStubs private val stubs = androidStubs

View File

@@ -44,7 +44,7 @@ class SlowUserQueryDetectorTest : LintDetectorTest() {
package test.pkg; package test.pkg;
import android.app.ActivityManager; import android.app.ActivityManager;
public class TestClass1 { public class TestClass {
public void slewlyGetCurrentUser() { public void slewlyGetCurrentUser() {
ActivityManager.getCurrentUser(); ActivityManager.getCurrentUser();
} }
@@ -59,10 +59,13 @@ class SlowUserQueryDetectorTest : LintDetectorTest() {
SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY
) )
.run() .run()
.expectWarningCount(1) .expect(
.expectContains( """
"ActivityManager.getCurrentUser() is slow. " + src/test/pkg/TestClass.java:6: Warning: Use UserTracker.getUserId() instead of ActivityManager.getCurrentUser() [SlowUserIdQuery]
"Use UserTracker.getUserId() instead." ActivityManager.getCurrentUser();
~~~~~~~~~~~~~~
0 errors, 1 warnings
"""
) )
} }
@@ -75,7 +78,7 @@ class SlowUserQueryDetectorTest : LintDetectorTest() {
package test.pkg; package test.pkg;
import android.os.UserManager; import android.os.UserManager;
public class TestClass2 { public class TestClass {
public void slewlyGetUserInfo(UserManager userManager) { public void slewlyGetUserInfo(UserManager userManager) {
userManager.getUserInfo(); userManager.getUserInfo();
} }
@@ -90,9 +93,13 @@ class SlowUserQueryDetectorTest : LintDetectorTest() {
SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY
) )
.run() .run()
.expectWarningCount(1) .expect(
.expectContains( """
"UserManager.getUserInfo() is slow. " + "Use UserTracker.getUserInfo() instead." src/test/pkg/TestClass.java:6: Warning: Use UserTracker.getUserInfo() instead of UserManager.getUserInfo() [SlowUserInfoQuery]
userManager.getUserInfo();
~~~~~~~~~~~
0 errors, 1 warnings
"""
) )
} }
@@ -105,7 +112,7 @@ class SlowUserQueryDetectorTest : LintDetectorTest() {
package test.pkg; package test.pkg;
import com.android.systemui.settings.UserTracker; import com.android.systemui.settings.UserTracker;
public class TestClass3 { public class TestClass {
public void quicklyGetUserId(UserTracker userTracker) { public void quicklyGetUserId(UserTracker userTracker) {
userTracker.getUserId(); userTracker.getUserId();
} }
@@ -132,7 +139,7 @@ class SlowUserQueryDetectorTest : LintDetectorTest() {
package test.pkg; package test.pkg;
import com.android.systemui.settings.UserTracker; import com.android.systemui.settings.UserTracker;
public class TestClass4 { public class TestClass {
public void quicklyGetUserId(UserTracker userTracker) { public void quicklyGetUserId(UserTracker userTracker) {
userTracker.getUserInfo(); userTracker.getUserInfo();
} }

View File

@@ -31,8 +31,6 @@ class SoftwareBitmapDetectorTest : LintDetectorTest() {
override fun getIssues(): List<Issue> = listOf(SoftwareBitmapDetector.ISSUE) override fun getIssues(): List<Issue> = listOf(SoftwareBitmapDetector.ISSUE)
private val explanation = "Usage of Config.HARDWARE is highly encouraged."
@Test @Test
fun testSoftwareBitmap() { fun testSoftwareBitmap() {
lint() lint()
@@ -41,7 +39,7 @@ class SoftwareBitmapDetectorTest : LintDetectorTest() {
""" """
import android.graphics.Bitmap; import android.graphics.Bitmap;
public class TestClass1 { public class TestClass {
public void test() { public void test() {
Bitmap.createBitmap(300, 300, Bitmap.Config.RGB_565); Bitmap.createBitmap(300, 300, Bitmap.Config.RGB_565);
Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888); Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888);
@@ -54,8 +52,17 @@ class SoftwareBitmapDetectorTest : LintDetectorTest() {
) )
.issues(SoftwareBitmapDetector.ISSUE) .issues(SoftwareBitmapDetector.ISSUE)
.run() .run()
.expectWarningCount(2) .expect(
.expectContains(explanation) """
src/android/graphics/Bitmap.java:5: Warning: Replace software bitmap with Config.HARDWARE [SoftwareBitmap]
ARGB_8888,
~~~~~~~~~
src/android/graphics/Bitmap.java:6: Warning: Replace software bitmap with Config.HARDWARE [SoftwareBitmap]
RGB_565,
~~~~~~~
0 errors, 2 warnings
"""
)
} }
@Test @Test
@@ -66,7 +73,7 @@ class SoftwareBitmapDetectorTest : LintDetectorTest() {
""" """
import android.graphics.Bitmap; import android.graphics.Bitmap;
public class TestClass1 { public class TestClass {
public void test() { public void test() {
Bitmap.createBitmap(300, 300, Bitmap.Config.HARDWARE); Bitmap.createBitmap(300, 300, Bitmap.Config.HARDWARE);
} }
@@ -78,7 +85,7 @@ class SoftwareBitmapDetectorTest : LintDetectorTest() {
) )
.issues(SoftwareBitmapDetector.ISSUE) .issues(SoftwareBitmapDetector.ISSUE)
.run() .run()
.expectWarningCount(0) .expectClean()
} }
private val stubs = androidStubs private val stubs = androidStubs

View File

@@ -36,6 +36,7 @@ import android.util.ArraySet;
import android.util.Log; import android.util.Log;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.Main;
@@ -182,6 +183,10 @@ public class TileLifecycleManager extends BroadcastReceiver implements
setBindService(true); setBindService(true);
} }
/**
* Binds or unbinds to IQSService
*/
@WorkerThread
public void setBindService(boolean bind) { public void setBindService(boolean bind) {
if (mBound && mUnbindImmediate) { if (mBound && mUnbindImmediate) {
// If we are already bound and expecting to unbind, this means we should stay bound // If we are already bound and expecting to unbind, this means we should stay bound