1 /* 2 * 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 org.jetbrains.uast.UCallExpression 30 import org.jetbrains.uast.UClass 31 import org.jetbrains.uast.getParentOfType 32 33 /** 34 * Checks if anyone is calling sendBroadcast / sendBroadcastAsUser on a Context (or subclasses) and 35 * directs them to using com.android.systemui.broadcast.BroadcastSender instead. 36 */ 37 @Suppress("UnstableApiUsage") 38 class BroadcastSentViaContextDetector : Detector(), SourceCodeScanner { 39 40 override fun getApplicableMethodNames(): List<String> { 41 return listOf("sendBroadcast", "sendBroadcastAsUser") 42 } 43 44 override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) { 45 if (node.getParentOfType(UClass::class.java)?.qualifiedName == 46 "com.android.systemui.broadcast.BroadcastSender" 47 ) { 48 // Don't warn for class we want the developers to use. 49 return 50 } 51 52 val evaluator = context.evaluator 53 if (evaluator.isMemberInSubClassOf(method, CLASS_CONTEXT)) { 54 context.report( 55 issue = ISSUE, 56 location = context.getNameLocation(node), 57 message = "`Context.${method.name}()` should be replaced with " + 58 "`BroadcastSender.${method.name}()`" 59 ) 60 } 61 } 62 63 companion object { 64 @JvmField 65 val ISSUE: Issue = 66 Issue.create( 67 id = "BroadcastSentViaContext", 68 briefDescription = "Broadcast sent via `Context` instead of `BroadcastSender`", 69 // lint trims indents and converts \ to line continuations 70 explanation = """ 71 Broadcasts sent via `Context.sendBroadcast()` or \ 72 `Context.sendBroadcastAsUser()` will block the main thread and may cause \ 73 missed frames. Instead, use `BroadcastSender.sendBroadcast()` or \ 74 `BroadcastSender.sendBroadcastAsUser()` which will schedule and dispatch \ 75 broadcasts on a background worker thread.""", 76 category = Category.PERFORMANCE, 77 priority = 8, 78 severity = Severity.WARNING, 79 implementation = 80 Implementation(BroadcastSentViaContextDetector::class.java, Scope.JAVA_FILE_SCOPE) 81 ) 82 } 83 } 84