Merge "Refactor ManualPermissionCheckDetector for @PermissionMethod"

This commit is contained in:
Matt Gilbride
2022-10-19 15:43:05 +00:00
committed by Android (Google) Code Review
5 changed files with 405 additions and 127 deletions

View File

@@ -26,6 +26,7 @@ import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.android.tools.lint.detector.api.getUMethod
import com.google.android.lint.aidl.hasPermissionMethodAnnotation
import com.intellij.psi.PsiType
import org.jetbrains.uast.UAnnotation
import org.jetbrains.uast.UBlockExpression
@@ -149,11 +150,6 @@ class PermissionMethodDetector : Detector(), SourceCodeScanner {
enabledByDefault = false
)
private fun hasPermissionMethodAnnotation(method: UMethod): Boolean = method.annotations
.any {
it.hasQualifiedName(ANNOTATION_PERMISSION_METHOD)
}
private fun isPermissionMethodReturnType(method: UMethod): Boolean =
listOf(PsiType.VOID, PsiType.INT, PsiType.BOOLEAN).contains(method.returnType)

View File

@@ -18,37 +18,54 @@ package com.google.android.lint.aidl
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Location
import com.intellij.psi.PsiVariable
import com.android.tools.lint.detector.api.getUMethod
import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
import org.jetbrains.uast.UCallExpression
import org.jetbrains.uast.ULiteralExpression
import org.jetbrains.uast.UQualifiedReferenceExpression
import org.jetbrains.uast.USimpleNameReferenceExpression
import org.jetbrains.uast.asRecursiveLogString
import org.jetbrains.uast.evaluateString
import org.jetbrains.uast.visitor.AbstractUastVisitor
/**
* Helper ADT class that facilitates the creation of lint auto fixes
* Helper class that facilitates the creation of lint auto fixes
*
* Handles "Single" permission checks that should be migrated to @EnforcePermission(...), as well as consecutive checks
* that should be migrated to @EnforcePermission(allOf={...})
*
* TODO: handle anyOf style annotations
*/
sealed class EnforcePermissionFix {
abstract fun locations(): List<Location>
abstract fun javaAnnotationParameter(): String
fun javaAnnotation(): String = "@$ANNOTATION_ENFORCE_PERMISSION(${javaAnnotationParameter()})"
data class EnforcePermissionFix(
val locations: List<Location>,
val permissionNames: List<String>
) {
val annotation: String
get() {
val quotedPermissions = permissionNames.joinToString(", ") { """"$it"""" }
val annotationParameter =
if (permissionNames.size > 1) "allOf={$quotedPermissions}" else quotedPermissions
return "@$ANNOTATION_ENFORCE_PERMISSION($annotationParameter)"
}
companion object {
fun fromCallExpression(callExpression: UCallExpression, context: JavaContext): SingleFix =
SingleFix(
getPermissionCheckLocation(context, callExpression),
getPermissionCheckArgumentValue(callExpression)
)
/**
* conditionally constructs EnforcePermissionFix from a UCallExpression
* @return EnforcePermissionFix if the called method is annotated with @PermissionMethod, else null
*/
fun fromCallExpression(
context: JavaContext,
callExpression: UCallExpression
): EnforcePermissionFix? =
if (isPermissionMethodCall(callExpression)) {
EnforcePermissionFix(
listOf(getPermissionCheckLocation(context, callExpression)),
getPermissionCheckValues(callExpression)
)
} else null
fun maybeAddManifestPrefix(permissionName: String): String =
if (permissionName.contains(".")) permissionName
else "android.Manifest.permission.$permissionName"
fun compose(individuals: List<EnforcePermissionFix>): EnforcePermissionFix =
EnforcePermissionFix(
individuals.flatMap { it.locations },
individuals.flatMap { it.permissionNames }
)
/**
* Given a permission check, get its proper location
@@ -70,49 +87,51 @@ sealed class EnforcePermissionFix {
}
/**
* Given a permission check and an argument,
* pull out the permission value that is being used
* Given a @PermissionMethod, find arguments annotated with @PermissionName
* and pull out the permission value(s) being used. Also evaluates nested calls
* to @PermissionMethod(s) in the given method's body.
*/
private fun getPermissionCheckArgumentValue(
callExpression: UCallExpression,
argumentPosition: Int = 0
): String {
private fun getPermissionCheckValues(
callExpression: UCallExpression
): List<String> {
if (!isPermissionMethodCall(callExpression)) return emptyList()
val identifier = when (
val argument = callExpression.valueArguments.getOrNull(argumentPosition)
) {
is UQualifiedReferenceExpression -> when (val selector = argument.selector) {
is USimpleNameReferenceExpression ->
((selector.resolve() as PsiVariable).computeConstantValue() as String)
val result = mutableSetOf<String>() // protect against duplicate permission values
val visitedCalls = mutableSetOf<UCallExpression>() // don't visit the same call twice
val bfsQueue = ArrayDeque(listOf(callExpression))
else -> throw RuntimeException(
"Couldn't resolve argument: ${selector.asRecursiveLogString()}"
)
}
// Breadth First Search - evalutaing nested @PermissionMethod(s) in the available
// source code for @PermissionName(s).
while (bfsQueue.isNotEmpty()) {
val current = bfsQueue.removeFirst()
visitedCalls.add(current)
result.addAll(findPermissions(current))
is USimpleNameReferenceExpression -> (
(argument.resolve() as PsiVariable).computeConstantValue() as String)
is ULiteralExpression -> argument.value as String
else -> throw RuntimeException(
"Couldn't resolve argument: ${argument?.asRecursiveLogString()}"
)
current.resolve()?.getUMethod()?.accept(object : AbstractUastVisitor() {
override fun visitCallExpression(node: UCallExpression): Boolean {
if (isPermissionMethodCall(node) && node !in visitedCalls) {
bfsQueue.add(node)
}
return false
}
})
}
return identifier.substringAfterLast(".")
return result.toList()
}
private fun findPermissions(
callExpression: UCallExpression,
): List<String> {
val indices = callExpression.resolve()?.getUMethod()
?.uastParameters
?.filter(::hasPermissionNameAnnotation)
?.mapNotNull { it.sourcePsi?.parameterIndex() }
?: emptyList()
return indices.mapNotNull {
callExpression.getArgumentForParameter(it)?.evaluateString()
}
}
}
}
data class SingleFix(val location: Location, val permissionName: String) : EnforcePermissionFix() {
override fun locations(): List<Location> = listOf(this.location)
override fun javaAnnotationParameter(): String = maybeAddManifestPrefix(this.permissionName)
}
data class AllOfFix(val checks: List<SingleFix>) : EnforcePermissionFix() {
override fun locations(): List<Location> = this.checks.map { it.location }
override fun javaAnnotationParameter(): String =
"allOf={${
this.checks.joinToString(", ") { maybeAddManifestPrefix(it.permissionName) }
}}"
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.lint.aidl
import com.android.tools.lint.detector.api.getUMethod
import com.google.android.lint.ANNOTATION_PERMISSION_METHOD
import com.google.android.lint.ANNOTATION_PERMISSION_NAME
import com.google.android.lint.CLASS_STUB
import com.intellij.psi.PsiAnonymousClass
import org.jetbrains.uast.UCallExpression
import org.jetbrains.uast.UMethod
import org.jetbrains.uast.UParameter
/**
* Given a UMethod, determine if this method is
* an entrypoint to an interface generated by AIDL,
* returning the interface name if so
*/
fun getContainingAidlInterface(node: UMethod): String? {
if (!isInClassCalledStub(node)) return null
for (superMethod in node.findSuperMethods()) {
for (extendsInterface in superMethod.containingClass?.extendsList?.referenceElements
?: continue) {
if (extendsInterface.qualifiedName == IINTERFACE_INTERFACE) {
return superMethod.containingClass?.name
}
}
}
return null
}
private fun isInClassCalledStub(node: UMethod): Boolean {
(node.containingClass as? PsiAnonymousClass)?.let {
return it.baseClassReference.referenceName == CLASS_STUB
}
return node.containingClass?.extendsList?.referenceElements?.any {
it.referenceName == CLASS_STUB
} ?: false
}
fun isPermissionMethodCall(callExpression: UCallExpression): Boolean {
val method = callExpression.resolve()?.getUMethod() ?: return false
return hasPermissionMethodAnnotation(method)
}
fun hasPermissionMethodAnnotation(method: UMethod): Boolean = method.annotations
.any {
it.hasQualifiedName(ANNOTATION_PERMISSION_METHOD)
}
fun hasPermissionNameAnnotation(parameter: UParameter) = parameter.annotations.any {
it.hasQualifiedName(ANNOTATION_PERMISSION_NAME)
}

View File

@@ -25,9 +25,6 @@ import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.google.android.lint.CLASS_STUB
import com.google.android.lint.ENFORCE_PERMISSION_METHODS
import com.intellij.psi.PsiAnonymousClass
import org.jetbrains.uast.UBlockExpression
import org.jetbrains.uast.UCallExpression
import org.jetbrains.uast.UElement
@@ -56,7 +53,7 @@ class ManualPermissionCheckDetector : Detector(), SourceCodeScanner {
val body = (node.uastBody as? UBlockExpression) ?: return
val fix = accumulateSimplePermissionCheckFixes(body) ?: return
val javaRemoveFixes = fix.locations().map {
val javaRemoveFixes = fix.locations.map {
fix()
.replace()
.reformat(true)
@@ -67,7 +64,7 @@ class ManualPermissionCheckDetector : Detector(), SourceCodeScanner {
}
val javaAnnotateFix = fix()
.annotate(fix.javaAnnotation())
.annotate(fix.annotation)
.range(context.getLocation(node))
.autoFix()
.build()
@@ -77,7 +74,7 @@ class ManualPermissionCheckDetector : Detector(), SourceCodeScanner {
context.report(
ISSUE_USE_ENFORCE_PERMISSION_ANNOTATION,
fix.locations().last(),
fix.locations.last(),
message,
fix().composite(*javaRemoveFixes.toTypedArray(), javaAnnotateFix)
)
@@ -97,14 +94,14 @@ class ManualPermissionCheckDetector : Detector(), SourceCodeScanner {
*/
private fun accumulateSimplePermissionCheckFixes(methodBody: UBlockExpression):
EnforcePermissionFix? {
val singleFixes = mutableListOf<SingleFix>()
val singleFixes = mutableListOf<EnforcePermissionFix>()
for (expression in methodBody.expressions) {
singleFixes.add(getPermissionCheckFix(expression) ?: break)
}
return when (singleFixes.size) {
0 -> null
1 -> singleFixes[0]
else -> AllOfFix(singleFixes)
else -> EnforcePermissionFix.compose(singleFixes)
}
}
@@ -113,7 +110,7 @@ class ManualPermissionCheckDetector : Detector(), SourceCodeScanner {
* the helper for creating a lint auto fix to @EnforcePermission
*/
private fun getPermissionCheckFix(startingExpression: UElement?):
SingleFix? {
EnforcePermissionFix? {
return when (startingExpression) {
is UQualifiedReferenceExpression -> getPermissionCheckFix(
startingExpression.selector
@@ -121,11 +118,8 @@ class ManualPermissionCheckDetector : Detector(), SourceCodeScanner {
is UIfExpression -> getPermissionCheckFix(startingExpression.condition)
is UCallExpression -> {
return if (isPermissionCheck(startingExpression))
EnforcePermissionFix.fromCallExpression(startingExpression, context)
else null
}
is UCallExpression -> return EnforcePermissionFix
.fromCallExpression(context, startingExpression)
else -> null
}
@@ -160,40 +154,5 @@ class ManualPermissionCheckDetector : Detector(), SourceCodeScanner {
),
enabledByDefault = false, // TODO: enable once b/241171714 is resolved
)
private fun isPermissionCheck(callExpression: UCallExpression): Boolean {
val method = callExpression.resolve() ?: return false
val className = method.containingClass?.qualifiedName
return ENFORCE_PERMISSION_METHODS.any {
it.clazz == className && it.name == method.name
}
}
/**
* given a UMethod, determine if this method is
* an entrypoint to an interface generated by AIDL,
* returning the interface name if so
*/
fun getContainingAidlInterface(node: UMethod): String? {
if (!isInClassCalledStub(node)) return null
for (superMethod in node.findSuperMethods()) {
for (extendsInterface in superMethod.containingClass?.extendsList?.referenceElements
?: continue) {
if (extendsInterface.qualifiedName == IINTERFACE_INTERFACE) {
return superMethod.containingClass?.name
}
}
}
return null
}
private fun isInClassCalledStub(node: UMethod): Boolean {
(node.containingClass as? PsiAnonymousClass)?.let {
return it.baseClassReference.referenceName == CLASS_STUB
}
return node.containingClass?.extendsList?.referenceElements?.any {
it.referenceName == CLASS_STUB
} ?: false
}
}
}

View File

@@ -19,6 +19,7 @@ package com.google.android.lint.aidl
import com.android.tools.lint.checks.infrastructure.LintDetectorTest
import com.android.tools.lint.checks.infrastructure.TestFile
import com.android.tools.lint.checks.infrastructure.TestLintTask
import com.android.tools.lint.checks.infrastructure.TestMode
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Issue
@@ -42,7 +43,7 @@ class ManualPermissionCheckDetectorTest : LintDetectorTest() {
private Context mContext;
@Override
public void test() throws android.os.RemoteException {
mContext.enforceCallingOrSelfPermission("android.Manifest.permission.READ_CONTACTS", "foo");
mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
}
}
"""
@@ -53,8 +54,8 @@ class ManualPermissionCheckDetectorTest : LintDetectorTest() {
.expect(
"""
src/Foo.java:7: Warning: ITest permission check can be converted to @EnforcePermission annotation [UseEnforcePermissionAnnotation]
mContext.enforceCallingOrSelfPermission("android.Manifest.permission.READ_CONTACTS", "foo");
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0 errors, 1 warnings
"""
)
@@ -62,9 +63,9 @@ class ManualPermissionCheckDetectorTest : LintDetectorTest() {
"""
Fix for src/Foo.java line 7: Annotate with @EnforcePermission:
@@ -5 +5
+ @android.annotation.EnforcePermission(android.Manifest.permission.READ_CONTACTS)
+ @android.annotation.EnforcePermission("android.permission.READ_CONTACTS")
@@ -7 +8
- mContext.enforceCallingOrSelfPermission("android.Manifest.permission.READ_CONTACTS", "foo");
- mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
"""
)
}
@@ -81,7 +82,7 @@ class ManualPermissionCheckDetectorTest : LintDetectorTest() {
@Override
public void test() throws android.os.RemoteException {
mContext.enforceCallingOrSelfPermission(
"android.Manifest.permission.READ_CONTACTS", "foo");
"android.permission.READ_CONTACTS", "foo");
}
};
}
@@ -102,10 +103,49 @@ class ManualPermissionCheckDetectorTest : LintDetectorTest() {
"""
Fix for src/Foo.java line 8: Annotate with @EnforcePermission:
@@ -6 +6
+ @android.annotation.EnforcePermission(android.Manifest.permission.READ_CONTACTS)
+ @android.annotation.EnforcePermission("android.permission.READ_CONTACTS")
@@ -8 +9
- mContext.enforceCallingOrSelfPermission(
- "android.Manifest.permission.READ_CONTACTS", "foo");
- "android.permission.READ_CONTACTS", "foo");
"""
)
}
fun testConstantEvaluation() {
lint().files(
java(
"""
import android.content.Context;
import android.test.ITest;
public class Foo extends ITest.Stub {
private Context mContext;
@Override
public void test() throws android.os.RemoteException {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_CONTACTS, "foo");
}
}
"""
).indented(),
*stubs,
manifestStub
)
.run()
.expect(
"""
src/Foo.java:8: Warning: ITest permission check can be converted to @EnforcePermission annotation [UseEnforcePermissionAnnotation]
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_CONTACTS, "foo");
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0 errors, 1 warnings
"""
)
.expectFixDiffs(
"""
Fix for src/Foo.java line 7: Annotate with @EnforcePermission:
@@ -6 +6
+ @android.annotation.EnforcePermission("android.permission.READ_CONTACTS")
@@ -8 +9
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_CONTACTS, "foo");
"""
)
}
@@ -122,9 +162,9 @@ class ManualPermissionCheckDetectorTest : LintDetectorTest() {
@Override
public void test() throws android.os.RemoteException {
mContext.enforceCallingOrSelfPermission(
"android.Manifest.permission.READ_CONTACTS", "foo");
"android.permission.READ_CONTACTS", "foo");
mContext.enforceCallingOrSelfPermission(
"android.Manifest.permission.WRITE_CONTACTS", "foo");
"android.permission.WRITE_CONTACTS", "foo");
}
};
}
@@ -144,13 +184,13 @@ class ManualPermissionCheckDetectorTest : LintDetectorTest() {
.expectFixDiffs(
"""
Fix for src/Foo.java line 10: Annotate with @EnforcePermission:
@@ -6 +6
+ @android.annotation.EnforcePermission(allOf={android.Manifest.permission.READ_CONTACTS, android.Manifest.permission.WRITE_CONTACTS})
@@ -6 +6
+ @android.annotation.EnforcePermission(allOf={"android.permission.READ_CONTACTS", "android.permission.WRITE_CONTACTS"})
@@ -8 +9
- mContext.enforceCallingOrSelfPermission(
- "android.Manifest.permission.READ_CONTACTS", "foo");
- "android.permission.READ_CONTACTS", "foo");
- mContext.enforceCallingOrSelfPermission(
- "android.Manifest.permission.WRITE_CONTACTS", "foo");
- "android.permission.WRITE_CONTACTS", "foo");
"""
)
}
@@ -166,7 +206,7 @@ class ManualPermissionCheckDetectorTest : LintDetectorTest() {
@Override
public void test() throws android.os.RemoteException {
long uid = Binder.getCallingUid();
mContext.enforceCallingOrSelfPermission("android.Manifest.permission.READ_CONTACTS", "foo");
mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
}
}
"""
@@ -177,6 +217,149 @@ class ManualPermissionCheckDetectorTest : LintDetectorTest() {
.expectClean()
}
fun testPermissionHelper() {
lint().skipTestModes(TestMode.PARENTHESIZED).files(
java(
"""
import android.content.Context;
import android.test.ITest;
public class Foo extends ITest.Stub {
private Context mContext;
@android.content.pm.PermissionMethod
private void helper() {
mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
}
@Override
public void test() throws android.os.RemoteException {
helper();
}
}
"""
).indented(),
*stubs
)
.run()
.expect(
"""
src/Foo.java:14: Warning: ITest permission check can be converted to @EnforcePermission annotation [UseEnforcePermissionAnnotation]
helper();
~~~~~~~~~
0 errors, 1 warnings
"""
)
.expectFixDiffs(
"""
Fix for src/Foo.java line 14: Annotate with @EnforcePermission:
@@ -12 +12
+ @android.annotation.EnforcePermission("android.permission.READ_CONTACTS")
@@ -14 +15
- helper();
"""
)
}
fun testPermissionHelperAllOf() {
lint().skipTestModes(TestMode.PARENTHESIZED).files(
java(
"""
import android.content.Context;
import android.test.ITest;
public class Foo extends ITest.Stub {
private Context mContext;
@android.content.pm.PermissionMethod
private void helper() {
mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
mContext.enforceCallingOrSelfPermission("android.permission.WRITE_CONTACTS", "foo");
}
@Override
public void test() throws android.os.RemoteException {
helper();
mContext.enforceCallingOrSelfPermission("FOO", "foo");
}
}
"""
).indented(),
*stubs
)
.run()
.expect(
"""
src/Foo.java:16: Warning: ITest permission check can be converted to @EnforcePermission annotation [UseEnforcePermissionAnnotation]
mContext.enforceCallingOrSelfPermission("FOO", "foo");
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0 errors, 1 warnings
"""
)
.expectFixDiffs(
"""
Fix for src/Foo.java line 16: Annotate with @EnforcePermission:
@@ -13 +13
+ @android.annotation.EnforcePermission(allOf={"android.permission.READ_CONTACTS", "android.permission.WRITE_CONTACTS", "FOO"})
@@ -15 +16
- helper();
- mContext.enforceCallingOrSelfPermission("FOO", "foo");
"""
)
}
fun testPermissionHelperNested() {
lint().skipTestModes(TestMode.PARENTHESIZED).files(
java(
"""
import android.content.Context;
import android.test.ITest;
public class Foo extends ITest.Stub {
private Context mContext;
@android.content.pm.PermissionMethod
private void helperHelper() {
helper("android.permission.WRITE_CONTACTS");
}
@android.content.pm.PermissionMethod
private void helper(@android.content.pm.PermissionName String extraPermission) {
mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
}
@Override
public void test() throws android.os.RemoteException {
helperHelper();
}
}
"""
).indented(),
*stubs
)
.run()
.expect(
"""
src/Foo.java:19: Warning: ITest permission check can be converted to @EnforcePermission annotation [UseEnforcePermissionAnnotation]
helperHelper();
~~~~~~~~~~~~~~~
0 errors, 1 warnings
"""
)
.expectFixDiffs(
"""
Fix for src/Foo.java line 19: Annotate with @EnforcePermission:
@@ -17 +17
+ @android.annotation.EnforcePermission(allOf={"android.permission.WRITE_CONTACTS", "android.permission.READ_CONTACTS"})
@@ -19 +20
- helperHelper();
"""
)
}
companion object {
private val aidlStub: TestFile = java(
"""
@@ -192,7 +375,8 @@ class ManualPermissionCheckDetectorTest : LintDetectorTest() {
"""
package android.content;
public class Context {
public void enforceCallingOrSelfPermission(String permission, String message) {}
@android.content.pm.PermissionMethod
public void enforceCallingOrSelfPermission(@android.content.pm.PermissionName String permission, String message) {}
}
"""
).indented()
@@ -206,6 +390,59 @@ class ManualPermissionCheckDetectorTest : LintDetectorTest() {
"""
).indented()
val stubs = arrayOf(aidlStub, contextStub, binderStub)
private val permissionMethodStub: TestFile = java(
"""
package android.content.pm;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.CLASS;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Retention(CLASS)
@Target({METHOD})
public @interface PermissionMethod {}
"""
).indented()
private val permissionNameStub: TestFile = java(
"""
package android.content.pm;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.CLASS;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Retention(CLASS)
@Target({PARAMETER, METHOD, LOCAL_VARIABLE, FIELD})
public @interface PermissionName {}
"""
).indented()
private val manifestStub: TestFile = java(
"""
package android;
public final class Manifest {
public static final class permission {
public static final String READ_CONTACTS="android.permission.READ_CONTACTS";
}
}
""".trimIndent()
)
val stubs = arrayOf(
aidlStub,
contextStub,
binderStub,
permissionMethodStub,
permissionNameStub
)
}
}