1 /*
2  * Copyright (C) 2021 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
18 
19 import com.android.tools.lint.detector.api.Category
20 import com.android.tools.lint.detector.api.Detector
21 import com.android.tools.lint.detector.api.Implementation
22 import com.android.tools.lint.detector.api.Issue
23 import com.android.tools.lint.detector.api.JavaContext
24 import com.android.tools.lint.detector.api.Scope
25 import com.android.tools.lint.detector.api.Severity
26 import com.android.tools.lint.detector.api.SourceCodeScanner
27 import com.intellij.psi.PsiMethod
28 import org.jetbrains.uast.UCallExpression
29 
30 /**
31  * Lint Detector that finds issues with improper usages of the non-user getter methods of Settings
32  */
33 @Suppress("UnstableApiUsage")
34 class CallingSettingsNonUserGetterMethodsDetector : Detector(), SourceCodeScanner {
35     override fun getApplicableMethodNames(): List<String> = listOf(
36             "getString",
37             "getInt",
38             "getLong",
39             "getFloat"
40     )
41 
42     override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
43         val evaluator = context.evaluator
44         if (evaluator.isMemberInClass(method, "android.provider.Settings.Secure") ||
45                 evaluator.isMemberInClass(method, "android.provider.Settings.System")
46         ) {
47             val message = getIncidentMessageNonUserGetterMethods(getMethodSignature(method))
48             context.report(ISSUE_NON_USER_GETTER_CALLED, node, context.getNameLocation(node),
49                     message)
50         }
51     }
52 
53     private fun getMethodSignature(method: PsiMethod) =
54             method.containingClass
55                     ?.qualifiedName
56                     ?.let { "$it#${method.name}" }
57                     ?: method.name
58 
59     companion object {
60         @JvmField
61         val ISSUE_NON_USER_GETTER_CALLED: Issue = Issue.create(
62                 id = "NonUserGetterCalled",
63                 briefDescription = "Non-ForUser Getter Method called to Settings",
64                 explanation = """
65                     System process should not call the non-ForUser getter methods of \
66                     `Settings.Secure` or `Settings.System`. For example, instead of \
67                     `Settings.Secure.getInt()`, use `Settings.Secure.getIntForUser()` instead. \
68                     This will make sure that the correct Settings value is retrieved.
69                     """,
70                 category = Category.CORRECTNESS,
71                 priority = 6,
72                 severity = Severity.ERROR,
73                 implementation = Implementation(
74                         CallingSettingsNonUserGetterMethodsDetector::class.java,
75                         Scope.JAVA_FILE_SCOPE
76                 )
77         )
78 
79         fun getIncidentMessageNonUserGetterMethods(methodSignature: String) =
80                 "`$methodSignature()` called from system process. " +
81                         "Please call `${methodSignature}ForUser()` instead. "
82     }
83 }
84