Merge "Remove class2greylist from frameworks/base."

am: 32979f03fe

Change-Id: I72f8797be98fe75806535da7b9f277ae68db8d57
This commit is contained in:
Mathew Inwood
2018-07-26 11:55:02 -07:00
committed by android-build-merger
10 changed files with 0 additions and 732 deletions

View File

@@ -1,33 +0,0 @@
//
// Copyright (C) 2018 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.
//
java_library_host {
name: "class2greylistlib",
srcs: ["src/**/*.java"],
static_libs: [
"commons-cli-1.2",
"apache-bcel",
],
}
java_binary_host {
name: "class2greylist",
manifest: "src/class2greylist.mf",
static_libs: [
"class2greylistlib",
],
}

View File

@@ -1 +0,0 @@
Main-Class: com.android.class2greylist.Class2Greylist

View File

@@ -1,118 +0,0 @@
/*
* Copyright (C) 2018 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.class2greylist;
import org.apache.bcel.Const;
import org.apache.bcel.classfile.AnnotationEntry;
import org.apache.bcel.classfile.DescendingVisitor;
import org.apache.bcel.classfile.ElementValuePair;
import org.apache.bcel.classfile.EmptyVisitor;
import org.apache.bcel.classfile.Field;
import org.apache.bcel.classfile.FieldOrMethod;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import java.util.Locale;
/**
* Visits a JavaClass instance and pulls out all members annotated with a
* specific annotation. The signatures of such members are passed to {@link
* Status#greylistEntry(String)}. Any errors result in a call to {@link
* Status#error(String)}.
*
* If the annotation has a property "expectedSignature" the generated signature
* will be verified against the one specified there. If it differs, an error
* will be generated.
*/
public class AnnotationVisitor extends EmptyVisitor {
private static final String EXPECTED_SIGNATURE = "expectedSignature";
private final JavaClass mClass;
private final String mAnnotationType;
private final Status mStatus;
private final DescendingVisitor mDescendingVisitor;
public AnnotationVisitor(JavaClass clazz, String annotation, Status d) {
mClass = clazz;
mAnnotationType = annotation;
mStatus = d;
mDescendingVisitor = new DescendingVisitor(clazz, this);
}
public void visit() {
mStatus.debug("Visit class %s", mClass.getClassName());
mDescendingVisitor.visit();
}
private static String getClassDescriptor(JavaClass clazz) {
// JavaClass.getName() returns the Java-style name (with . not /), so we must fetch
// the original class name from the constant pool.
return clazz.getConstantPool().getConstantString(
clazz.getClassNameIndex(), Const.CONSTANT_Class);
}
@Override
public void visitMethod(Method method) {
visitMember(method, "L%s;->%s%s");
}
@Override
public void visitField(Field field) {
visitMember(field, "L%s;->%s:%s");
}
private void visitMember(FieldOrMethod member, String signatureFormatString) {
JavaClass definingClass = (JavaClass) mDescendingVisitor.predecessor();
mStatus.debug("Visit member %s : %s", member.getName(), member.getSignature());
for (AnnotationEntry a : member.getAnnotationEntries()) {
if (mAnnotationType.equals(a.getAnnotationType())) {
mStatus.debug("Method has annotation %s", mAnnotationType);
String signature = String.format(Locale.US, signatureFormatString,
getClassDescriptor(definingClass), member.getName(), member.getSignature());
for (ElementValuePair property : a.getElementValuePairs()) {
switch (property.getNameString()) {
case EXPECTED_SIGNATURE:
String expected = property.getValue().stringifyValue();
if (!signature.equals(expected)) {
error(definingClass, member,
"Expected signature does not match generated:\n"
+ "Expected: %s\n"
+ "Generated: %s", expected, signature);
}
break;
}
}
mStatus.greylistEntry(signature);
}
}
}
private void error(JavaClass clazz, FieldOrMethod member, String message, Object... args) {
StringBuilder error = new StringBuilder();
error.append(clazz.getSourceFileName())
.append(": ")
.append(clazz.getClassName())
.append(".")
.append(member.getName())
.append(": ")
.append(String.format(Locale.US, message, args));
mStatus.error(error.toString());
}
}

View File

@@ -1,99 +0,0 @@
/*
* Copyright (C) 2018 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.class2greylist;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PatternOptionBuilder;
import java.io.IOException;
/**
* Build time tool for extracting a list of members from jar files that have the @UsedByApps
* annotation, for building the greylist.
*/
public class Class2Greylist {
private static final String ANNOTATION_TYPE = "Landroid/annotation/UnsupportedAppUsage ;";
public static void main(String[] args) {
Options options = new Options();
options.addOption(OptionBuilder
.withLongOpt("debug")
.hasArgs(0)
.withDescription("Enable debug")
.create("d"));
options.addOption(OptionBuilder
.withLongOpt("help")
.hasArgs(0)
.withDescription("Show this help")
.create("h"));
CommandLineParser parser = new GnuParser();
CommandLine cmd;
try {
cmd = parser.parse(options, args);
} catch (ParseException e) {
System.err.println(e.getMessage());
help(options);
return;
}
if (cmd.hasOption('h')) {
help(options);
}
String[] jarFiles = cmd.getArgs();
if (jarFiles.length == 0) {
System.err.println("Error: no jar files specified.");
help(options);
}
Status status = new Status(cmd.hasOption('d'));
for (String jarFile : jarFiles) {
status.debug("Processing jar file %s", jarFile);
try {
JarReader reader = new JarReader(status, jarFile);
reader.stream().forEach(clazz -> new AnnotationVisitor(
clazz, ANNOTATION_TYPE, status).visit());
reader.close();
} catch (IOException e) {
status.error(e);
}
}
if (status.ok()) {
System.exit(0);
} else {
System.exit(1);
}
}
private static void help(Options options) {
new HelpFormatter().printHelp(
"class2greylist path/to/classes.jar [classes2.jar ...]",
"Extracts greylist entries from classes jar files given",
options, null, true);
System.exit(1);
}
}

View File

@@ -1,65 +0,0 @@
/*
* Copyright (C) 2018 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.class2greylist;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.JavaClass;
import java.io.IOException;
import java.util.Objects;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* Reads {@link JavaClass} members from a zip/jar file, providing a stream of them for processing.
* Any errors are reported via {@link Status#error(Throwable)}.
*/
public class JarReader {
private final Status mStatus;
private final String mFileName;
private final ZipFile mZipFile;
public JarReader(Status s, String filename) throws IOException {
mStatus = s;
mFileName = filename;
mZipFile = new ZipFile(mFileName);
}
private JavaClass openZipEntry(ZipEntry e) {
try {
mStatus.debug("Reading %s from %s", e.getName(), mFileName);
return new ClassParser(mZipFile.getInputStream(e), e.getName()).parse();
} catch (IOException ioe) {
mStatus.error(ioe);
return null;
}
}
public Stream<JavaClass> stream() {
return mZipFile.stream()
.filter(zipEntry -> zipEntry.getName().endsWith(".class"))
.map(zipEntry -> openZipEntry(zipEntry))
.filter(Objects::nonNull);
}
public void close() throws IOException {
mZipFile.close();
}
}

View File

@@ -1,58 +0,0 @@
/*
* Copyright (C) 2018 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.class2greylist;
import java.util.Locale;
public class Status {
// Highlight "Error:" in red.
private static final String ERROR = "\u001B[31mError: \u001B[0m";
private final boolean mDebug;
private boolean mHasErrors;
public Status(boolean debug) {
mDebug = debug;
}
public void debug(String msg, Object... args) {
if (mDebug) {
System.err.println(String.format(Locale.US, msg, args));
}
}
public void error(Throwable t) {
System.err.print(ERROR);
t.printStackTrace(System.err);
mHasErrors = true;
}
public void error(String message) {
System.err.print(ERROR);
System.err.println(message);
mHasErrors = true;
}
public void greylistEntry(String signature) {
System.out.println(signature);
}
public boolean ok() {
return !mHasErrors;
}
}

View File

@@ -1,32 +0,0 @@
# Copyright (C) 2018 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.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Only compile source java files in this apk.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_MODULE := class2greylisttest
LOCAL_STATIC_JAVA_LIBRARIES := class2greylistlib truth-host-prebuilt mockito-host junit-host
# tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := general-tests
include $(BUILD_HOST_JAVA_LIBRARY)
# Build the test APKs using their own makefiles
include $(call all-makefiles-under,$(LOCAL_PATH))

View File

@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2018 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.
-->
<configuration description="class2greylist tests">
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
<option name="jar" value="class2greylisttest.jar" />
<option name="runtime-hint" value="1m" />
</test>
</configuration>

View File

@@ -1,202 +0,0 @@
/*
* Copyright (C) 2018 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.javac;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.MockitoAnnotations.initMocks;
import com.android.class2greylist.Status;
import com.android.class2greylist.AnnotationVisitor;
import com.google.common.base.Joiner;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import java.io.IOException;
public class AnnotationVisitorTest {
private static final String ANNOTATION = "Lannotation/Anno;";
private Javac mJavac;
@Mock
private Status mStatus;
@Before
public void setup() throws IOException {
initMocks(this);
mJavac = new Javac();
mJavac.addSource("annotation.Anno", Joiner.on('\n').join(
"package annotation;",
"import static java.lang.annotation.RetentionPolicy.CLASS;",
"import java.lang.annotation.Retention;",
"import java.lang.annotation.Target;",
"@Retention(CLASS)",
"public @interface Anno {",
" String expectedSignature() default \"\";",
"}"));
}
private void assertNoErrors() {
verify(mStatus, never()).error(any(Throwable.class));
verify(mStatus, never()).error(any(String.class));
}
@Test
public void testGreylistMethod() throws IOException {
mJavac.addSource("a.b.Class", Joiner.on('\n').join(
"package a.b;",
"import annotation.Anno;",
"public class Class {",
" @Anno",
" public void method() {}",
"}"));
assertThat(mJavac.compile()).isTrue();
new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
.visit();
assertNoErrors();
ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
verify(mStatus, times(1)).greylistEntry(greylist.capture());
assertThat(greylist.getValue()).isEqualTo("La/b/Class;->method()V");
}
@Test
public void testGreylistConstructor() throws IOException {
mJavac.addSource("a.b.Class", Joiner.on('\n').join(
"package a.b;",
"import annotation.Anno;",
"public class Class {",
" @Anno",
" public Class() {}",
"}"));
assertThat(mJavac.compile()).isTrue();
new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
.visit();
assertNoErrors();
ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
verify(mStatus, times(1)).greylistEntry(greylist.capture());
assertThat(greylist.getValue()).isEqualTo("La/b/Class;-><init>()V");
}
@Test
public void testGreylistField() throws IOException {
mJavac.addSource("a.b.Class", Joiner.on('\n').join(
"package a.b;",
"import annotation.Anno;",
"public class Class {",
" @Anno",
" public int i;",
"}"));
assertThat(mJavac.compile()).isTrue();
new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
.visit();
assertNoErrors();
ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
verify(mStatus, times(1)).greylistEntry(greylist.capture());
assertThat(greylist.getValue()).isEqualTo("La/b/Class;->i:I");
}
@Test
public void testGreylistMethodExpectedSignature() throws IOException {
mJavac.addSource("a.b.Class", Joiner.on('\n').join(
"package a.b;",
"import annotation.Anno;",
"public class Class {",
" @Anno(expectedSignature=\"La/b/Class;->method()V\")",
" public void method() {}",
"}"));
assertThat(mJavac.compile()).isTrue();
new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
.visit();
assertNoErrors();
ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
verify(mStatus, times(1)).greylistEntry(greylist.capture());
assertThat(greylist.getValue()).isEqualTo("La/b/Class;->method()V");
}
@Test
public void testGreylistMethodExpectedSignatureWrong() throws IOException {
mJavac.addSource("a.b.Class", Joiner.on('\n').join(
"package a.b;",
"import annotation.Anno;",
"public class Class {",
" @Anno(expectedSignature=\"La/b/Class;->nomethod()V\")",
" public void method() {}",
"}"));
assertThat(mJavac.compile()).isTrue();
new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
.visit();
verify(mStatus, times(1)).error(any(String.class));
}
@Test
public void testGreylistInnerClassMethod() throws IOException {
mJavac.addSource("a.b.Class", Joiner.on('\n').join(
"package a.b;",
"import annotation.Anno;",
"public class Class {",
" public class Inner {",
" @Anno",
" public void method() {}",
" }",
"}"));
assertThat(mJavac.compile()).isTrue();
new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class$Inner"), ANNOTATION,
mStatus).visit();
assertNoErrors();
ArgumentCaptor<String> greylist = ArgumentCaptor.forClass(String.class);
verify(mStatus, times(1)).greylistEntry(greylist.capture());
assertThat(greylist.getValue()).isEqualTo("La/b/Class$Inner;->method()V");
}
@Test
public void testMethodNotGreylisted() throws IOException {
mJavac.addSource("a.b.Class", Joiner.on('\n').join(
"package a.b;",
"public class Class {",
" public void method() {}",
"}"));
assertThat(mJavac.compile()).isTrue();
new AnnotationVisitor(mJavac.getCompiledClass("a.b.Class"), ANNOTATION, mStatus)
.visit();
assertNoErrors();
verify(mStatus, never()).greylistEntry(any(String.class));
}
}

View File

@@ -1,103 +0,0 @@
/*
* Copyright (C) 2018 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.javac;
import com.google.common.io.Files;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.JavaClass;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
/**
* Helper class for compiling snippets of Java source and providing access to the resulting class
* files.
*/
public class Javac {
private final JavaCompiler mJavac;
private final StandardJavaFileManager mFileMan;
private final List<JavaFileObject> mCompilationUnits;
private final File mClassOutDir;
public Javac() throws IOException {
mJavac = ToolProvider.getSystemJavaCompiler();
mFileMan = mJavac.getStandardFileManager(null, Locale.US, null);
mClassOutDir = Files.createTempDir();
mFileMan.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(mClassOutDir));
mFileMan.setLocation(StandardLocation.CLASS_PATH, Arrays.asList(mClassOutDir));
mCompilationUnits = new ArrayList<>();
}
private String classToFileName(String classname) {
return classname.replace('.', '/');
}
public Javac addSource(String classname, String contents) {
JavaFileObject java = new SimpleJavaFileObject(URI.create(
String.format("string:///%s.java", classToFileName(classname))),
JavaFileObject.Kind.SOURCE
){
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
return contents;
}
};
mCompilationUnits.add(java);
return this;
}
public boolean compile() {
JavaCompiler.CompilationTask task = mJavac.getTask(
null,
mFileMan,
null,
null,
null,
mCompilationUnits);
return task.call();
}
public InputStream getClassFile(String classname) throws IOException {
Iterable<? extends JavaFileObject> objs = mFileMan.getJavaFileObjects(
new File(mClassOutDir, String.format("%s.class", classToFileName(classname))));
if (!objs.iterator().hasNext()) {
return null;
}
return objs.iterator().next().openInputStream();
}
public JavaClass getCompiledClass(String classname) throws IOException {
return new ClassParser(getClassFile(classname),
String.format("%s.class", classToFileName(classname))).parse();
}
}