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.google.android.lint.aidl 18 19 import com.android.tools.lint.detector.api.JavaContext 20 import com.android.tools.lint.detector.api.LintFix 21 import com.android.tools.lint.detector.api.Location 22 import com.intellij.psi.PsiClass 23 import com.intellij.psi.PsiReferenceList 24 import org.jetbrains.uast.UMethod 25 26 /** 27 * Given a UMethod, determine if this method is 28 * the entrypoint to an interface generated by AIDL, 29 * returning the interface name if so, otherwise returning null 30 */ 31 fun getContainingAidlInterface(context: JavaContext, node: UMethod): String? { 32 if (!isContainedInSubclassOfStub(context, node)) return null 33 for (superMethod in node.findSuperMethods()) { 34 for (extendsInterface in superMethod.containingClass?.extendsList?.referenceElements 35 ?: continue) { 36 if (extendsInterface.qualifiedName == IINTERFACE_INTERFACE) { 37 return superMethod.containingClass?.name 38 } 39 } 40 } 41 return null 42 } 43 44 fun isContainedInSubclassOfStub(context: JavaContext, node: UMethod?): Boolean { 45 var superClass = node?.containingClass?.superClass 46 while (superClass != null) { 47 if (isStub(context, superClass)) return true 48 superClass = superClass.superClass 49 } 50 return false 51 } 52 53 fun isStub(context: JavaContext, psiClass: PsiClass?): Boolean { 54 if (psiClass == null) return false 55 if (psiClass.name != "Stub") return false 56 if (!context.evaluator.isStatic(psiClass)) return false 57 if (!context.evaluator.isAbstract(psiClass)) return false 58 59 if (!hasSingleAncestor(psiClass.extendsList, BINDER_CLASS)) return false 60 61 val parent = psiClass.parent as? PsiClass ?: return false 62 if (!hasSingleAncestor(parent.extendsList, IINTERFACE_INTERFACE)) return false 63 64 val parentName = parent.qualifiedName ?: return false 65 if (!hasSingleAncestor(psiClass.implementsList, parentName)) return false 66 67 return true 68 } 69 70 private fun hasSingleAncestor(references: PsiReferenceList?, qualifiedName: String) = 71 references != null && 72 references.referenceElements.size == 1 && 73 references.referenceElements[0].qualifiedName == qualifiedName 74 75 fun getHelperMethodCallSourceString(node: UMethod) = "${node.name}$AIDL_PERMISSION_HELPER_SUFFIX()" 76 77 fun getHelperMethodFix( 78 node: UMethod, 79 manualCheckLocation: Location, 80 prepend: Boolean = true 81 ): LintFix { 82 val helperMethodSource = getHelperMethodCallSourceString(node) 83 val indent = " ".repeat(manualCheckLocation.start?.column ?: 0) 84 val newText = "$helperMethodSource;${if (prepend) "\n\n$indent" else ""}" 85 86 val fix = LintFix.create() 87 .replace() 88 .range(manualCheckLocation) 89 .with(newText) 90 .reformat(true) 91 .autoFix() 92 93 if (prepend) fix.beginning() 94 95 return fix.build() 96 } 97