• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.systemui.lint
18 
19 import com.android.SdkConstants.CLASS_CONTEXT
20 import com.android.tools.lint.detector.api.Category
21 import com.android.tools.lint.detector.api.Detector
22 import com.android.tools.lint.detector.api.Implementation
23 import com.android.tools.lint.detector.api.Issue
24 import com.android.tools.lint.detector.api.JavaContext
25 import com.android.tools.lint.detector.api.Scope
26 import com.android.tools.lint.detector.api.Severity
27 import com.android.tools.lint.detector.api.SourceCodeScanner
28 import com.intellij.psi.PsiMethod
29 import com.intellij.psi.PsiModifierListOwner
30 import org.jetbrains.uast.UCallExpression
31 import org.jetbrains.uast.UClass
32 import org.jetbrains.uast.UMethod
33 import org.jetbrains.uast.getParentOfType
34 
35 /**
36  * Warns if {@code Context.bindService}, {@code Context.bindServiceAsUser}, or {@code
37  * Context.unbindService} is not called on a {@code WorkerThread}
38  */
39 @Suppress("UnstableApiUsage")
40 class BindServiceOnMainThreadDetector : Detector(), SourceCodeScanner {
41 
42     override fun getApplicableMethodNames(): List<String> {
43         return listOf("bindService", "bindServiceAsUser", "unbindService")
44     }
45 
46     private fun hasWorkerThreadAnnotation(
47         context: JavaContext,
48         annotated: PsiModifierListOwner?
49     ): Boolean {
50         return context.evaluator.getAnnotations(annotated, inHierarchy = true).any { uAnnotation ->
51             uAnnotation.qualifiedName == "androidx.annotation.WorkerThread"
52         }
53     }
54 
55     override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
56         if (context.evaluator.isMemberInSubClassOf(method, CLASS_CONTEXT)) {
57             if (
58                 !hasWorkerThreadAnnotation(context, node.getParentOfType(UMethod::class.java)) &&
59                     !hasWorkerThreadAnnotation(context, node.getParentOfType(UClass::class.java))
60             ) {
61                 context.report(
62                     issue = ISSUE,
63                     location = context.getLocation(node),
64                     message =
65                         "This method should be annotated with `@WorkerThread` because " +
66                             "it calls ${method.name}",
67                 )
68             }
69         }
70     }
71 
72     companion object {
73         @JvmField
74         val ISSUE: Issue =
75             Issue.create(
76                 id = "BindServiceOnMainThread",
77                 briefDescription = "Service bound or unbound on main thread",
78                 explanation =
79                     """
80                     Binding and unbinding services are synchronous calls to `ActivityManager`. \
81                     They usually take multiple milliseconds to complete. If called on the main \
82                     thread, it will likely cause missed frames. To fix it, use a `@Background \
83                     Executor` and annotate the calling method with `@WorkerThread`.
84                     """,
85                 category = Category.PERFORMANCE,
86                 priority = 8,
87                 severity = Severity.WARNING,
88                 implementation =
89                     Implementation(
90                         BindServiceOnMainThreadDetector::class.java,
91                         Scope.JAVA_FILE_SCOPE
92                     )
93             )
94     }
95 }
96