1 /*
2  * Copyright (C) 2023 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.Category
20 import com.android.tools.lint.detector.api.Implementation
21 import com.android.tools.lint.detector.api.Issue
22 import com.android.tools.lint.detector.api.JavaContext
23 import com.android.tools.lint.detector.api.Scope
24 import com.android.tools.lint.detector.api.Severity
25 import org.jetbrains.uast.UBlockExpression
26 import org.jetbrains.uast.UMethod
27 
28 /**
29  * Ensures all AIDL-generated methods are annotated.
30  *
31  * This detector is run on system_server to validate that any method that may
32  * be exposed via an AIDL interface is permission-annotated. That is, it must
33  * have one of the following annotation:
34  *   - @EnforcePermission
35  *   - @RequiresNoPermission
36  *   - @PermissionManuallyEnforced
37  */
38 class PermissionAnnotationDetector : AidlImplementationDetector() {
39 
visitAidlMethodnull40     override fun visitAidlMethod(
41       context: JavaContext,
42       node: UMethod,
43       interfaceName: String,
44       body: UBlockExpression
45     ) {
46         if (!isSystemServicePath(context)) return
47 
48         if (context.evaluator.isAbstract(node)) return
49 
50         val fullyQualifiedInterfaceName =
51             getContainingAidlInterfaceQualified(context, node) ?: return
52         if (exemptAidlInterfaces.contains(fullyQualifiedInterfaceName)) return
53 
54         if (AIDL_PERMISSION_ANNOTATIONS.any { node.hasAnnotation(it) }) return
55 
56         context.report(
57             ISSUE_MISSING_PERMISSION_ANNOTATION,
58             node,
59             context.getLocation(node),
60             """
61                 ${node.name} should be annotated with either @EnforcePermission, \
62                 @RequiresNoPermission or @PermissionManuallyEnforced.
63             """.trimMargin()
64         )
65     }
66 
67     companion object {
68 
69         private val EXPLANATION_MISSING_PERMISSION_ANNOTATION = """
70           Interfaces that are exposed by system_server are required to have an annotation which
71           denotes the type of permission enforced. There are 3 possible options:
72             - @EnforcePermission
73             - @RequiresNoPermission
74             - @PermissionManuallyEnforced
75           See the documentation of each annotation for further details.
76 
77           The annotation on the Java implementation must be the same that the AIDL interface
78           definition. This is verified by a lint in the build system.
79           """.trimIndent()
80 
81         @JvmField
82         val ISSUE_MISSING_PERMISSION_ANNOTATION = Issue.create(
83             id = "MissingPermissionAnnotation",
84             briefDescription = "No permission annotation on exposed AIDL interface.",
85             explanation = EXPLANATION_MISSING_PERMISSION_ANNOTATION,
86             category = Category.CORRECTNESS,
87             priority = 5,
88             severity = Severity.ERROR,
89             implementation = Implementation(
90                 PermissionAnnotationDetector::class.java,
91                 Scope.JAVA_FILE_SCOPE
92             )
93         )
94     }
95 }
96