summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2017-06-07 07:48:09 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2017-06-07 07:48:09 +0000
commit1b6b717769d4ab8317d292f3b931011c70dee427 (patch)
tree21d0766bd8078537adce479da8bb273f501253ca
parent3ecf15551bfc999bcc0be0e6c5e78540a4fb20ae (diff)
parent4500890636ce6930d9659c5ffa7c029c93569aa6 (diff)
downloadplatform_external_doclava-1b6b717769d4ab8317d292f3b931011c70dee427.tar.gz
platform_external_doclava-1b6b717769d4ab8317d292f3b931011c70dee427.tar.bz2
platform_external_doclava-1b6b717769d4ab8317d292f3b931011c70dee427.zip
release-request-224b654d-e3ad-4f05-a95d-d10d1516b3f6-for-git_oc-release-4075622 snap-temp-L17600000071371496
Change-Id: Ic8d19b59006f70e3b0e31e509dbedd9768ddabfc
-rw-r--r--src/com/google/doclava/Doclava.java37
-rw-r--r--src/com/google/doclava/Stubs.java77
2 files changed, 95 insertions, 19 deletions
diff --git a/src/com/google/doclava/Doclava.java b/src/com/google/doclava/Doclava.java
index 9359930..6d26d80 100644
--- a/src/com/google/doclava/Doclava.java
+++ b/src/com/google/doclava/Doclava.java
@@ -28,6 +28,7 @@ import com.sun.javadoc.*;
import java.util.*;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.io.*;
import java.lang.reflect.Proxy;
import java.lang.reflect.Array;
@@ -118,6 +119,8 @@ public class Doclava {
public static AuxSource auxSource = new EmptyAuxSource();
public static Linter linter = new EmptyLinter();
public static boolean android = false;
+ public static String manifestFile = null;
+ public static Map<String, String> manifestPermissions = new HashMap<>();
public static JSilver jSilver = null;
@@ -352,12 +355,17 @@ public class Doclava {
auxSource = new AndroidAuxSource();
linter = new AndroidLinter();
android = true;
+ } else if (a[0].equals("-manifest")) {
+ manifestFile = a[1];
}
}
if (!readKnownTagsFiles(knownTags, knownTagsFiles)) {
return false;
}
+ if (!readManifest()) {
+ return false;
+ }
// Set up the data structures
Converter.makeInfo(r);
@@ -591,6 +599,32 @@ public class Doclava {
return true;
}
+ private static boolean readManifest() {
+ manifestPermissions.clear();
+ if (manifestFile == null) {
+ return true;
+ }
+ try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(manifestFile));
+ ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ byte[] buffer = new byte[1024];
+ int count;
+ while ((count = in.read(buffer)) != -1) {
+ out.write(buffer, 0, count);
+ }
+ final Matcher m = Pattern.compile("(?s)<permission "
+ + "[^>]*android:name=\"([^\">]+)\""
+ + "[^>]*android:protectionLevel=\"([^\">]+)\"").matcher(out.toString());
+ while (m.find()) {
+ manifestPermissions.put(m.group(1), m.group(2));
+ }
+ } catch (IOException e) {
+ Errors.error(Errors.PARSE_ERROR, (SourcePositionInfo) null,
+ "Failed to parse " + manifestFile + ": " + e);
+ return false;
+ }
+ return true;
+ }
+
public static String escape(String s) {
if (escapeChars.size() == 0) {
return s;
@@ -814,6 +848,9 @@ public class Doclava {
if (option.equals("-android")) {
return 1;
}
+ if (option.equals("-manifest")) {
+ return 2;
+ }
return 0;
}
public static boolean validOptions(String[][] options, DocErrorReporter r) {
diff --git a/src/com/google/doclava/Stubs.java b/src/com/google/doclava/Stubs.java
index added35..fbcff97 100644
--- a/src/com/google/doclava/Stubs.java
+++ b/src/com/google/doclava/Stubs.java
@@ -1296,7 +1296,7 @@ public class Stubs {
// that they're protected with a system permission.
if (Doclava.android && Doclava.showAnnotations.contains("android.annotation.SystemApi")
&& !(predicate instanceof RemovedPredicate)) {
- boolean systemService = false;
+ boolean systemService = "android.content.pm.PackageManager".equals(cl.qualifiedName());
for (AnnotationInstanceInfo a : cl.annotations()) {
if (a.type().qualifiedNameMatches("android", "annotation.SystemService")) {
systemService = true;
@@ -1304,24 +1304,7 @@ public class Stubs {
}
if (systemService) {
for (MethodInfo mi : methods) {
- boolean hasPermission = false;
- for (AnnotationInstanceInfo a : mi.annotations()) {
- if (a.type().qualifiedNameMatches("android", "annotation.RequiresPermission")) {
- hasPermission = true;
- }
- }
- for (ParameterInfo pi : mi.parameters()) {
- for (AnnotationInstanceInfo a : pi.annotations()) {
- if (a.type().qualifiedNameMatches("android", "annotation.RequiresPermission")) {
- hasPermission = true;
- }
- }
- }
- if (!hasPermission) {
- Errors.error(Errors.REQUIRES_PERMISSION, mi,
- "Method '" + mi.name() + "' exposed as @SystemApi must be"
- + " protected with a system permission.");
- }
+ checkSystemPermissions(mi);
}
}
}
@@ -1393,6 +1376,62 @@ public class Stubs {
return hasWrittenPackageHead;
}
+ private static void checkSystemPermissions(MethodInfo mi) {
+ boolean hasAnnotation = false;
+ for (AnnotationInstanceInfo a : mi.annotations()) {
+ if (a.type().qualifiedNameMatches("android", "annotation.RequiresPermission")) {
+ hasAnnotation = true;
+ for (AnnotationValueInfo val : a.elementValues()) {
+ ArrayList<AnnotationValueInfo> values = null;
+ boolean any = false;
+ switch (val.element().name()) {
+ case "value":
+ values = new ArrayList<AnnotationValueInfo>();
+ values.add(val);
+ break;
+ case "allOf":
+ values = (ArrayList<AnnotationValueInfo>) val.value();
+ break;
+ case "anyOf":
+ any = true;
+ values = (ArrayList<AnnotationValueInfo>) val.value();
+ break;
+ }
+
+ ArrayList<String> system = new ArrayList<>();
+ ArrayList<String> nonSystem = new ArrayList<>();
+ for (AnnotationValueInfo value : values) {
+ final String perm = String.valueOf(value.value());
+ final String level = Doclava.manifestPermissions.getOrDefault(perm, null);
+ if (level == null) {
+ Errors.error(Errors.REMOVED_FIELD, mi.position(),
+ "Permission '" + perm + "' is not defined by AndroidManifest.xml.");
+ continue;
+ }
+ if (level.contains("normal") || level.contains("dangerous")
+ || level.contains("ephemeral")) {
+ nonSystem.add(perm);
+ } else {
+ system.add(perm);
+ }
+ }
+
+ if (system.isEmpty() && nonSystem.isEmpty()) {
+ hasAnnotation = false;
+ } else if ((any && !nonSystem.isEmpty()) || (!any && system.isEmpty())) {
+ Errors.error(Errors.REQUIRES_PERMISSION, mi, "Method '" + mi.name()
+ + "' must be protected with a system permission; it currently"
+ + " allows non-system callers holding " + nonSystem.toString());
+ }
+ }
+ }
+ }
+ if (!hasAnnotation) {
+ Errors.error(Errors.REQUIRES_PERMISSION, mi, "Method '" + mi.name()
+ + "' must be protected with a system permission.");
+ }
+ }
+
public static void writeApi(PrintStream apiWriter, Collection<PackageInfo> pkgs) {
final PackageInfo[] packages = pkgs.toArray(new PackageInfo[pkgs.size()]);
Arrays.sort(packages, PackageInfo.comparator);