Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (C) 2015 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.android.tools.lint.checks;
import static com.android.SdkConstants.CLASS_INTENT;
import static com.android.tools.lint.checks.SupportAnnotationDetector.PERMISSION_ANNOTATION;
import static com.android.tools.lint.checks.SupportAnnotationDetector.PERMISSION_ANNOTATION_READ;
import static com.android.tools.lint.checks.SupportAnnotationDetector.PERMISSION_ANNOTATION_WRITE;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.lint.detector.api.JavaContext;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiAssignmentExpression;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiConditionalExpression;
import com.intellij.psi.PsiDeclarationStatement;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiExpressionList;
import com.intellij.psi.PsiExpressionStatement;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiJavaCodeReferenceElement;
import com.intellij.psi.PsiLiteral;
import com.intellij.psi.PsiLocalVariable;
import com.intellij.psi.PsiModifierList;
import com.intellij.psi.PsiNameValuePair;
import com.intellij.psi.PsiNewExpression;
import com.intellij.psi.PsiParenthesizedExpression;
import com.intellij.psi.PsiReferenceExpression;
import com.intellij.psi.PsiStatement;
import com.intellij.psi.PsiTypeCastExpression;
import com.intellij.psi.util.PsiTreeUtil;
/**
* Utility for locating permissions required by an intent or content resolver
*/
public class PermissionFinder {
/**
* Operation that has a permission requirement -- such as a method call,
* a content resolver read or write operation, an intent, etc.
*/
public enum Operation {
CALL, ACTION, READ, WRITE;
/** Prefix to use when describing a name with a permission requirement */
public String prefix() {
switch (this) {
case ACTION:
return "by intent";
case READ:
return "to read";
case WRITE:
return "to write";
case CALL:
default:
return "by";
}
}
}
/** A permission requirement given a name and operation */
public static class Result {
@NonNull public final PermissionRequirement requirement;
@NonNull public final String name;
@NonNull public final Operation operation;
public Result(
@NonNull Operation operation,
@NonNull PermissionRequirement requirement,
@NonNull String name) {
this.operation = operation;
this.requirement = requirement;
this.name = name;
}
}
/**
* Searches for a permission requirement for the given parameter in the given call
*
* @param operation the operation to look up
* @param context the context to use for lookup
* @param parameter the parameter which contains the value which implies the permission
* @return the result with the permission requirement, or null if nothing is found
*/
@Nullable
public static Result findRequiredPermissions(
@NonNull Operation operation,
@NonNull JavaContext context,
@NonNull PsiElement parameter) {
// To find the permission required by an intent, we proceed in 3 steps:
// (1) Locate the parameter in the start call that corresponds to
// the Intent
//
// (2) Find the place where the intent is initialized, and figure
// out the action name being passed to it.
//
// (3) Find the place where the action is defined, and look for permission
// annotations on that action declaration!
return new PermissionFinder(context, operation).search(parameter);
}
private PermissionFinder(@NonNull JavaContext context, @NonNull Operation operation) {
mContext = context;
mOperation = operation;
}
@NonNull private final JavaContext mContext;
@NonNull private final Operation mOperation;
@Nullable
public Result search(@NonNull PsiElement node) {
if (node instanceof PsiLiteral && "null".equals(node.getText())) {
return null;
} else if (node instanceof PsiConditionalExpression) {
PsiConditionalExpression expression = (PsiConditionalExpression) node;
if (expression.getThenExpression() != null) {
Result result = search(expression.getThenExpression());
if (result != null) {
return result;
}
}
if (expression.getElseExpression() != null) {
Result result = search(expression.getElseExpression());
if (result != null) {
return result;
}
}
} else if (node instanceof PsiTypeCastExpression) {
PsiTypeCastExpression cast = (PsiTypeCastExpression) node;
PsiExpression operand = cast.getOperand();
if (operand != null) {
return search(operand);
}
} else if (node instanceof PsiParenthesizedExpression) {
PsiParenthesizedExpression parens = (PsiParenthesizedExpression) node;
PsiExpression expression = parens.getExpression();
if (expression != null) {
return search(expression);
}
} else if (node instanceof PsiNewExpression && mOperation == Operation.ACTION) {
// Identifies "new Intent(argument)" calls and, if found, continues
// resolving the argument instead looking for the action definition
PsiNewExpression call = (PsiNewExpression) node;
PsiJavaCodeReferenceElement classReference = call.getClassReference();
String type = classReference != null ? classReference.getQualifiedName() : null;
if (CLASS_INTENT.equals(type)) {
PsiExpressionList argumentList = call.getArgumentList();
if (argumentList != null) {
PsiExpression[] expressions = argumentList.getExpressions();
if (expressions.length > 0) {
PsiExpression action = expressions[0];
if (action != null) {
return search(action);
}
}
}
}
return null;
} else if (node instanceof PsiReferenceExpression) {
PsiElement resolved = ((PsiReferenceExpression) node).resolve();
if (resolved instanceof PsiField) {
PsiField field = (PsiField) resolved;
if (mOperation == Operation.ACTION) {
PsiModifierList modifierList = field.getModifierList();
PsiAnnotation annotation = modifierList != null
? modifierList.findAnnotation(PERMISSION_ANNOTATION) : null;
if (annotation != null) {
return getPermissionRequirement(field, annotation);
}
} else if (mOperation == Operation.READ || mOperation == Operation.WRITE) {
String fqn = mOperation == Operation.READ
? PERMISSION_ANNOTATION_READ : PERMISSION_ANNOTATION_WRITE;
PsiModifierList modifierList = field.getModifierList();
PsiAnnotation annotation = modifierList != null
? modifierList.findAnnotation(fqn) : null;
if (annotation != null) {
PsiNameValuePair[] attributes = annotation.getParameterList().getAttributes();
PsiNameValuePair o = attributes.length == 1 ? attributes[0] : null;
if (o != null && o.getValue() instanceof PsiAnnotation) {
annotation = (PsiAnnotation) o.getValue();
if (PERMISSION_ANNOTATION.equals(annotation.getQualifiedName())) {
return getPermissionRequirement(field, annotation);
}
} else {
// The complex annotations used for read/write cannot be
// expressed in the external annotations format, so they're inlined.
// (See Extractor.AnnotationData#write).
//
// Instead we've inlined the fields of the annotation on the
// outer one:
return getPermissionRequirement(field, annotation);
}
}
} else {
assert false : mOperation;
}
} else if (resolved instanceof PsiLocalVariable) {
PsiLocalVariable variable = (PsiLocalVariable) resolved;
String targetName = variable.getName();
PsiStatement statement = PsiTreeUtil.getParentOfType(node, PsiStatement.class, false);
if (statement != null && targetName != null) {
PsiStatement prev = PsiTreeUtil.getPrevSiblingOfType(statement,
PsiStatement.class);
while (prev != null) {
if (prev instanceof PsiDeclarationStatement) {
for (PsiElement element : ((PsiDeclarationStatement) prev)
.getDeclaredElements()) {
if (variable.equals(element)) {
if (variable.getInitializer() != null) {
return search(variable.getInitializer());
} else {
break;
}
}
}
} else if (prev instanceof PsiExpressionStatement) {
PsiExpression expression = ((PsiExpressionStatement) prev)
.getExpression();
if (expression instanceof PsiAssignmentExpression) {
PsiAssignmentExpression assign
= (PsiAssignmentExpression) expression;
PsiExpression lhs = assign.getLExpression();
if (lhs instanceof PsiReferenceExpression) {
PsiReferenceExpression reference = (PsiReferenceExpression) lhs;
if (targetName.equals(reference.getReferenceName()) &&
reference.getQualifier() == null) {
if (assign.getRExpression() != null) {
return search(assign.getRExpression());
} else {
break;
}
}
}
}
}
prev = PsiTreeUtil.getPrevSiblingOfType(prev,
PsiStatement.class);
}
}
}
}
return null;
}
@NonNull
private Result getPermissionRequirement(
@NonNull PsiField field,
@NonNull PsiAnnotation annotation) {
PermissionRequirement requirement = PermissionRequirement.create(mContext, annotation);
PsiClass containingClass = field.getContainingClass();
String name = containingClass != null
? containingClass.getName() + "." + field.getName()
: field.getName();
assert name != null;
return new Result(mOperation, requirement, name);
}
}