Merge "Update linter formatting, add new tests" into tm-qpr-dev
This commit is contained in:
@@ -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
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
}
|
||||||
"""
|
"""
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user