diff options
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/aapt2/Diagnostics.h | 8 | ||||
| -rw-r--r-- | tools/aapt2/compile/XmlIdCollector.cpp | 28 | ||||
| -rw-r--r-- | tools/aapt2/compile/XmlIdCollector_test.cpp | 10 | ||||
| -rw-r--r-- | tools/aapt2/link/ManifestFixer.cpp | 3 | ||||
| -rw-r--r-- | tools/aapt2/util/Files.cpp | 15 | ||||
| -rwxr-xr-x | tools/aosp/aosp_sha.sh | 20 | ||||
| -rwxr-xr-x | tools/hiddenapi/generate_hiddenapi_lists.py | 4 | ||||
| -rwxr-xr-x | tools/hiddenapi/sort_api.sh | 6 | ||||
| -rw-r--r-- | tools/processors/unsupportedappusage/Android.bp | 25 | ||||
| -rw-r--r-- | tools/processors/unsupportedappusage/META-INF/services/javax.annotation.processing.Processor | 1 | ||||
| -rw-r--r-- | tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java | 228 | ||||
| -rw-r--r-- | tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java | 173 | ||||
| -rw-r--r-- | tools/stats_log_api_gen/Collation.cpp | 29 | ||||
| -rw-r--r-- | tools/stats_log_api_gen/Collation.h | 2 | ||||
| -rw-r--r-- | tools/stats_log_api_gen/main.cpp | 113 | ||||
| -rw-r--r-- | tools/validatekeymaps/Android.bp | 1 | ||||
| -rw-r--r-- | tools/validatekeymaps/Main.cpp | 7 |
17 files changed, 629 insertions, 44 deletions
diff --git a/tools/aapt2/Diagnostics.h b/tools/aapt2/Diagnostics.h index 50e8b33ab9b1..30deb5555b21 100644 --- a/tools/aapt2/Diagnostics.h +++ b/tools/aapt2/Diagnostics.h @@ -133,11 +133,19 @@ class SourcePathDiagnostics : public IDiagnostics { void Log(Level level, DiagMessageActual& actual_msg) override { actual_msg.source.path = source_.path; diag_->Log(level, actual_msg); + if (level == Level::Error) { + error = true; + } + } + + bool HadError() { + return error; } private: Source source_; IDiagnostics* diag_; + bool error = false; DISALLOW_COPY_AND_ASSIGN(SourcePathDiagnostics); }; diff --git a/tools/aapt2/compile/XmlIdCollector.cpp b/tools/aapt2/compile/XmlIdCollector.cpp index d61a15af0d85..2199d003bccb 100644 --- a/tools/aapt2/compile/XmlIdCollector.cpp +++ b/tools/aapt2/compile/XmlIdCollector.cpp @@ -21,6 +21,7 @@ #include "ResourceUtils.h" #include "ResourceValues.h" +#include "text/Unicode.h" #include "xml/XmlDom.h" namespace aapt { @@ -35,8 +36,9 @@ struct IdCollector : public xml::Visitor { public: using xml::Visitor::Visit; - explicit IdCollector(std::vector<SourcedResourceName>* out_symbols) - : out_symbols_(out_symbols) {} + explicit IdCollector(std::vector<SourcedResourceName>* out_symbols, + SourcePathDiagnostics* source_diag) : out_symbols_(out_symbols), + source_diag_(source_diag) {} void Visit(xml::Element* element) override { for (xml::Attribute& attr : element->attributes) { @@ -44,12 +46,16 @@ struct IdCollector : public xml::Visitor { bool create = false; if (ResourceUtils::ParseReference(attr.value, &name, &create, nullptr)) { if (create && name.type == ResourceType::kId) { - auto iter = std::lower_bound(out_symbols_->begin(), - out_symbols_->end(), name, cmp_name); - if (iter == out_symbols_->end() || iter->name != name) { - out_symbols_->insert(iter, - SourcedResourceName{name.ToResourceName(), - element->line_number}); + if (!text::IsValidResourceEntryName(name.entry)) { + source_diag_->Error(DiagMessage(element->line_number) + << "id '" << name << "' has an invalid entry name"); + } else { + auto iter = std::lower_bound(out_symbols_->begin(), + out_symbols_->end(), name, cmp_name); + if (iter == out_symbols_->end() || iter->name != name) { + out_symbols_->insert(iter, SourcedResourceName{name.ToResourceName(), + element->line_number}); + } } } } @@ -60,15 +66,17 @@ struct IdCollector : public xml::Visitor { private: std::vector<SourcedResourceName>* out_symbols_; + SourcePathDiagnostics* source_diag_; }; } // namespace bool XmlIdCollector::Consume(IAaptContext* context, xml::XmlResource* xmlRes) { xmlRes->file.exported_symbols.clear(); - IdCollector collector(&xmlRes->file.exported_symbols); + SourcePathDiagnostics source_diag(xmlRes->file.source, context->GetDiagnostics()); + IdCollector collector(&xmlRes->file.exported_symbols, &source_diag); xmlRes->root->Accept(&collector); - return true; + return !source_diag.HadError(); } } // namespace aapt diff --git a/tools/aapt2/compile/XmlIdCollector_test.cpp b/tools/aapt2/compile/XmlIdCollector_test.cpp index 98da56d03ae3..d49af3bf903c 100644 --- a/tools/aapt2/compile/XmlIdCollector_test.cpp +++ b/tools/aapt2/compile/XmlIdCollector_test.cpp @@ -64,4 +64,14 @@ TEST(XmlIdCollectorTest, DontCollectNonIds) { EXPECT_TRUE(doc->file.exported_symbols.empty()); } +TEST(XmlIdCollectorTest, ErrorOnInvalidIds) { + std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); + + std::unique_ptr<xml::XmlResource> doc = + test::BuildXmlDom("<View foo=\"@+id/foo$bar\"/>"); + + XmlIdCollector collector; + ASSERT_FALSE(collector.Consume(context.get(), doc.get())); +} + } // namespace aapt diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index c5c78d9d3827..fa6538d7b4e7 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -252,6 +252,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, xml::XmlNodeAction component_action; component_action.Action(RequiredNameIsJavaClassName); component_action["intent-filter"] = intent_filter_action; + component_action["preferred"] = intent_filter_action; component_action["meta-data"] = meta_data_action; // Manifest actions. @@ -414,6 +415,8 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, application_action["provider"]["grant-uri-permission"]; application_action["provider"]["path-permission"]; + manifest_action["package"] = manifest_action; + return true; } diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp index 7cd023bca369..73105e16559b 100644 --- a/tools/aapt2/util/Files.cpp +++ b/tools/aapt2/util/Files.cpp @@ -102,12 +102,25 @@ FileType GetFileType(const std::string& path) { #endif bool mkdirs(const std::string& path) { - constexpr const mode_t mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP; + #ifdef _WIN32 + // Start after the drive path if present. Calling mkdir with only the drive will cause an error. + size_t current_pos = 1u; + if (path.size() >= 3 && path[1] == ':' && + (path[2] == '\\' || path[2] == '/')) { + current_pos = 3u; + } + #else // Start after the first character so that we don't consume the root '/'. // This is safe to do with unicode because '/' will never match with a continuation character. size_t current_pos = 1u; + #endif + constexpr const mode_t mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP; while ((current_pos = path.find(sDirSep, current_pos)) != std::string::npos) { std::string parent_path = path.substr(0, current_pos); + if (parent_path.empty()) { + continue; + } + int result = ::android::base::utf8::mkdir(parent_path.c_str(), mode); if (result < 0 && errno != EEXIST) { return false; diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh index 29bf74c7a8b9..e50c70d0656a 100755 --- a/tools/aosp/aosp_sha.sh +++ b/tools/aosp/aosp_sha.sh @@ -1,18 +1,24 @@ #!/bin/bash -LOCAL_DIR="$( dirname ${BASH_SOURCE} )" +LOCAL_DIR="$( dirname "${BASH_SOURCE}" )" -if git branch -vv | grep "^*" | grep "\[aosp/master" > /dev/null; then +if git branch -vv | grep -q -P "^\*[^\[]+\[aosp/"; then # Change appears to be in AOSP exit 0 else # Change appears to be non-AOSP; search for files - git show --name-only --pretty=format: $1 | grep $2 | while read file; do - echo + count=0 + while read -r file ; do + if (( count == 0 )); then + echo + fi echo -e "\033[0;31mThe source of truth for '$file' is in AOSP.\033[0m" + (( count++ )) + done < <(git show --name-only --pretty=format: $1 | grep -- "$2") + if (( count != 0 )); then echo - echo "If your change contains no confidential details, please upload and merge" - echo "this change at https://android-review.googlesource.com/." + echo "If your change contains no confidential details (such as security fixes), please" + echo "upload and merge this change at https://android-review.googlesource.com/." echo exit 77 - done + fi fi diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py index 4a0931a149af..6c46e67be63d 100755 --- a/tools/hiddenapi/generate_hiddenapi_lists.py +++ b/tools/hiddenapi/generate_hiddenapi_lists.py @@ -59,6 +59,8 @@ def get_args(): def read_lines(filename): """Reads entire file and return it as a list of lines. + Lines which begin with a hash are ignored. + Args: filename (string): Path to the file to read from. @@ -66,7 +68,7 @@ def read_lines(filename): list: Lines of the loaded file as a list of strings. """ with open(filename, 'r') as f: - return f.readlines() + return filter(lambda line: not line.startswith('#'), f.readlines()) def write_lines(filename, lines): """Writes list of lines into a file, overwriting the file it it exists. diff --git a/tools/hiddenapi/sort_api.sh b/tools/hiddenapi/sort_api.sh index 1c6eb1b286b1..bdcc8076dde1 100755 --- a/tools/hiddenapi/sort_api.sh +++ b/tools/hiddenapi/sort_api.sh @@ -11,8 +11,14 @@ fi readarray A < "$source_list" # Sort IFS=$'\n' +# Stash away comments +C=( $(grep -E '^#' <<< "${A[*]}") ) +A=( $(grep -v -E '^#' <<< "${A[*]}") ) +# Sort entries A=( $(LC_COLLATE=C sort -f <<< "${A[*]}") ) A=( $(uniq <<< "${A[*]}") ) +# Concatenate comments and entries +A=( ${C[*]} ${A[*]} ) unset IFS # Dump array back into the file printf '%s\n' "${A[@]}" > "$dest_list" diff --git a/tools/processors/unsupportedappusage/Android.bp b/tools/processors/unsupportedappusage/Android.bp new file mode 100644 index 000000000000..1aca3edfab88 --- /dev/null +++ b/tools/processors/unsupportedappusage/Android.bp @@ -0,0 +1,25 @@ + +java_library_host { + name: "unsupportedappusage-annotation-processor", + java_resources: [ + "META-INF/**/*", + ], + srcs: [ + "src/**/*.java", + ], + static_libs: [ + "guava", + "unsupportedappusage-annotation" + ], + openjdk9: { + javacflags: [ + "--add-modules=jdk.compiler", + "--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", + "--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED", + "--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", + "--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", + ], + }, + + use_tools_jar: true, +} diff --git a/tools/processors/unsupportedappusage/META-INF/services/javax.annotation.processing.Processor b/tools/processors/unsupportedappusage/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 000000000000..4a969d319070 --- /dev/null +++ b/tools/processors/unsupportedappusage/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +android.processor.unsupportedappusage.UnsupportedAppUsageProcessor diff --git a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java new file mode 100644 index 000000000000..ef2914610f80 --- /dev/null +++ b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java @@ -0,0 +1,228 @@ +/* + * 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 android.processor.unsupportedappusage; + +import static javax.lang.model.element.ElementKind.PACKAGE; +import static javax.tools.Diagnostic.Kind.ERROR; +import static javax.tools.Diagnostic.Kind.WARNING; + +import android.annotation.UnsupportedAppUsage; + +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableMap; +import com.sun.tools.javac.code.Type; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import javax.annotation.processing.Messager; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; + +/** + * Builds a dex signature for a given method or field. + */ +public class SignatureBuilder { + + private static final Map<TypeKind, String> TYPE_MAP = ImmutableMap.<TypeKind, String>builder() + .put(TypeKind.BOOLEAN, "Z") + .put(TypeKind.BYTE, "B") + .put(TypeKind.CHAR, "C") + .put(TypeKind.DOUBLE, "D") + .put(TypeKind.FLOAT, "F") + .put(TypeKind.INT, "I") + .put(TypeKind.LONG, "J") + .put(TypeKind.SHORT, "S") + .put(TypeKind.VOID, "V") + .build(); + + private final Messager mMessager; + + /** + * Exception used internally when we can't build a signature. Whenever this is thrown, an error + * will also be written to the Messager. + */ + private class SignatureBuilderException extends Exception { + public SignatureBuilderException(String message) { + super(message); + } + public void report(Element offendingElement) { + mMessager.printMessage(ERROR, getMessage(), offendingElement); + } + } + + public SignatureBuilder(Messager messager) { + mMessager = messager; + } + + /** + * Returns a list of enclosing elements for the given element, with the package first, and + * excluding the element itself. + */ + private List<Element> getEnclosingElements(Element e) { + List<Element> enclosing = new ArrayList<>(); + e = e.getEnclosingElement(); // don't include the element itself. + while (e != null) { + enclosing.add(e); + e = e.getEnclosingElement(); + } + Collections.reverse(enclosing); + return enclosing; + } + + /** + * Get the dex signature for a clazz, in format "Lpackage/name/Outer$Inner;" + */ + private String getClassSignature(TypeElement clazz) { + StringBuilder sb = new StringBuilder("L"); + for (Element enclosing : getEnclosingElements(clazz)) { + if (enclosing.getKind() == PACKAGE) { + sb.append(((PackageElement) enclosing) + .getQualifiedName() + .toString() + .replace('.', '/')); + sb.append('/'); + } else { + sb.append(enclosing.getSimpleName()).append('$'); + } + + } + return sb + .append(clazz.getSimpleName()) + .append(";") + .toString(); + } + + /** + * Returns the type signature for a given type. For primitive types, a single character. + * For classes, the class signature. For arrays, a "[" preceeding the component type. + */ + private String getTypeSignature(TypeMirror type) throws SignatureBuilderException { + String sig = TYPE_MAP.get(type.getKind()); + if (sig != null) { + return sig; + } + switch (type.getKind()) { + case ARRAY: + return "[" + getTypeSignature(((ArrayType) type).getComponentType()); + case DECLARED: + Element declaring = ((DeclaredType) type).asElement(); + if (!(declaring instanceof TypeElement)) { + throw new SignatureBuilderException( + "Can't handle declared type of kind " + declaring.getKind()); + } + return getClassSignature((TypeElement) declaring); + case TYPEVAR: + Type.TypeVar typeVar = (Type.TypeVar) type; + if (typeVar.getLowerBound().getKind() != TypeKind.NULL) { + return getTypeSignature(typeVar.getLowerBound()); + } else if (typeVar.getUpperBound().getKind() != TypeKind.NULL) { + return getTypeSignature(typeVar.getUpperBound()); + } else { + throw new SignatureBuilderException("Can't handle typevar with no bound"); + } + + default: + throw new SignatureBuilderException("Can't handle type of kind " + type.getKind()); + } + } + + /** + * Get the signature for an executable, either a method or a constructor. + * + * @param name "<init>" for constructor, else the method name + * @param method The executable element in question. + */ + private String getExecutableSignature(CharSequence name, ExecutableElement method) + throws SignatureBuilderException { + StringBuilder sig = new StringBuilder(); + sig.append(getClassSignature((TypeElement) method.getEnclosingElement())) + .append("->") + .append(name) + .append("("); + for (VariableElement param : method.getParameters()) { + sig.append(getTypeSignature(param.asType())); + } + sig.append(")") + .append(getTypeSignature(method.getReturnType())); + return sig.toString(); + } + + private String buildMethodSignature(ExecutableElement method) throws SignatureBuilderException { + return getExecutableSignature(method.getSimpleName(), method); + } + + private String buildConstructorSignature(ExecutableElement cons) + throws SignatureBuilderException { + return getExecutableSignature("<init>", cons); + } + + private String buildFieldSignature(VariableElement field) throws SignatureBuilderException { + StringBuilder sig = new StringBuilder(); + sig.append(getClassSignature((TypeElement) field.getEnclosingElement())) + .append("->") + .append(field.getSimpleName()) + .append(":") + .append(getTypeSignature(field.asType())) + ; + return sig.toString(); + } + + public String buildSignature(Element element) { + UnsupportedAppUsage uba = element.getAnnotation(UnsupportedAppUsage.class); + try { + String signature; + switch (element.getKind()) { + case METHOD: + signature = buildMethodSignature((ExecutableElement) element); + break; + case CONSTRUCTOR: + signature = buildConstructorSignature((ExecutableElement) element); + break; + case FIELD: + signature = buildFieldSignature((VariableElement) element); + break; + default: + return null; + } + // if we have an expected signature on the annotation, warn if it doesn't match. + if (!Strings.isNullOrEmpty(uba.expectedSignature())) { + if (!signature.equals(uba.expectedSignature())) { + mMessager.printMessage( + WARNING, + String.format("Expected signature doesn't match generated signature.\n" + + " Expected: %s\n Generated: %s", + uba.expectedSignature(), signature), + element); + } + } + return signature; + } catch (SignatureBuilderException problem) { + problem.report(element); + return null; + } + } +} diff --git a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java new file mode 100644 index 000000000000..1d4c435939db --- /dev/null +++ b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java @@ -0,0 +1,173 @@ +/* + * 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 android.processor.unsupportedappusage; + +import static javax.tools.StandardLocation.CLASS_OUTPUT; + +import android.annotation.UnsupportedAppUsage; + +import com.google.common.base.Joiner; +import com.sun.tools.javac.model.JavacElements; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.util.Pair; +import com.sun.tools.javac.util.Position; + +import java.io.IOException; +import java.io.PrintStream; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.stream.Stream; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; + +/** + * Annotation processor for {@link UnsupportedAppUsage} annotations. + * + * This processor currently outputs two things: + * 1. A greylist.txt containing dex signatures of all annotated elements. + * 2. A CSV file with a mapping of dex signatures to corresponding source positions. + * + * The first will be used at a later stage of the build to add access flags to the dex file. The + * second is used for automating updates to the annotations themselves. + */ +@SupportedAnnotationTypes({"android.annotation.UnsupportedAppUsage"}) +public class UnsupportedAppUsageProcessor extends AbstractProcessor { + + // Package name for writing output. Output will be written to the "class output" location within + // this package. + private static final String PACKAGE = "unsupportedappusage"; + private static final String INDEX_CSV = "unsupportedappusage_index.csv"; + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + + /** + * Write the contents of a stream to a text file, with one line per item. + */ + private void writeToFile(String name, + String headerLine, + Stream<?> contents) throws IOException { + PrintStream out = new PrintStream(processingEnv.getFiler().createResource( + CLASS_OUTPUT, + PACKAGE, + name) + .openOutputStream()); + out.println(headerLine); + contents.forEach(o -> out.println(o)); + if (out.checkError()) { + throw new IOException("Error when writing to " + name); + } + out.close(); + } + + /** + * Find the annotation mirror for the @UnsupportedAppUsage annotation on the given element. + */ + private AnnotationMirror getUnsupportedAppUsageAnnotationMirror(Element e) { + for (AnnotationMirror m : e.getAnnotationMirrors()) { + TypeElement type = (TypeElement) m.getAnnotationType().asElement(); + if (type.getQualifiedName().toString().equals( + UnsupportedAppUsage.class.getCanonicalName())) { + return m; + } + } + return null; + } + + /** + * Returns a CSV header line for the columns returned by + * {@link #getAnnotationIndex(String, Element)}. + */ + private String getCsvHeaders() { + return Joiner.on(',').join( + "signature", + "file", + "startline", + "startcol", + "endline", + "endcol" + ); + } + + /** + * Maps an annotated element to the source position of the @UnsupportedAppUsage annotation + * attached to it. It returns CSV in the format: + * dex-signature,filename,start-line,start-col,end-line,end-col + * + * The positions refer to the annotation itself, *not* the annotated member. This can therefore + * be used to read just the annotation from the file, and to perform in-place edits on it. + * + * @param signature the dex signature for the element. + * @param annotatedElement The annotated element + * @return A single line of CSV text + */ + private String getAnnotationIndex(String signature, Element annotatedElement) { + JavacElements javacElem = (JavacElements) processingEnv.getElementUtils(); + AnnotationMirror unsupportedAppUsage = + getUnsupportedAppUsageAnnotationMirror(annotatedElement); + Pair<JCTree, JCTree.JCCompilationUnit> pair = + javacElem.getTreeAndTopLevel(annotatedElement, unsupportedAppUsage, null); + Position.LineMap lines = pair.snd.lineMap; + return Joiner.on(",").join( + signature, + pair.snd.getSourceFile().getName(), + lines.getLineNumber(pair.fst.pos().getStartPosition()), + lines.getColumnNumber(pair.fst.pos().getStartPosition()), + lines.getLineNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions)), + lines.getColumnNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions))); + } + + /** + * This is the main entry point in the processor, called by the compiler. + */ + @Override + public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { + Set<? extends Element> annotated = roundEnv.getElementsAnnotatedWith( + UnsupportedAppUsage.class); + if (annotated.size() == 0) { + return true; + } + // build signatures for each annotated member, and put them in a map of signature to member + Map<String, Element> signatureMap = new TreeMap<>(); + SignatureBuilder sb = new SignatureBuilder(processingEnv.getMessager()); + for (Element e : annotated) { + String sig = sb.buildSignature(e); + if (sig != null) { + signatureMap.put(sig, e); + } + } + try { + writeToFile(INDEX_CSV, + getCsvHeaders(), + signatureMap.entrySet() + .stream() + .map(e -> getAnnotationIndex(e.getKey() ,e.getValue()))); + } catch (IOException e) { + throw new RuntimeException("Failed to write output", e); + } + return true; + } +} diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp index 40ee490fc186..4245700ed90d 100644 --- a/tools/stats_log_api_gen/Collation.cpp +++ b/tools/stats_log_api_gen/Collation.cpp @@ -184,16 +184,6 @@ int collate_atom(const Descriptor *atom, AtomDecl *atomDecl, expectedNumber++; } - // Skips the key value pair atom. - for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin(); - it != fields.end(); it++) { - const FieldDescriptor *field = it->second; - java_type_t javaType = java_type(field); - if (javaType == JAVA_TYPE_KEY_VALUE_PAIR) { - return 0; - } - } - // Check that only allowed types are present. Remove any invalid ones. for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin(); it != fields.end(); it++) { @@ -205,9 +195,11 @@ int collate_atom(const Descriptor *atom, AtomDecl *atomDecl, print_error(field, "Unkown type for field: %s\n", field->name().c_str()); errorCount++; continue; - } else if (javaType == JAVA_TYPE_OBJECT) { + } else if (javaType == JAVA_TYPE_OBJECT && + atomDecl->code < PULL_ATOM_START_ID) { // Allow attribution chain, but only at position 1. - print_error(field, "Message type not allowed for field: %s\n", + print_error(field, + "Message type not allowed for field in pushed atoms: %s\n", field->name().c_str()); errorCount++; continue; @@ -243,12 +235,19 @@ int collate_atom(const Descriptor *atom, AtomDecl *atomDecl, java_type_t javaType = java_type(field); AtomField atField(field->name(), javaType); + // Generate signature for pushed atoms + if (atomDecl->code < PULL_ATOM_START_ID) { + if (javaType == JAVA_TYPE_ENUM) { + // All enums are treated as ints when it comes to function signatures. + signature->push_back(JAVA_TYPE_INT); + collate_enums(*field->enum_type(), &atField); + } else { + signature->push_back(javaType); + } + } if (javaType == JAVA_TYPE_ENUM) { // All enums are treated as ints when it comes to function signatures. - signature->push_back(JAVA_TYPE_INT); collate_enums(*field->enum_type(), &atField); - } else { - signature->push_back(javaType); } atomDecl->fields.push_back(atField); diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h index ccdd1458f656..31b8b07472cc 100644 --- a/tools/stats_log_api_gen/Collation.h +++ b/tools/stats_log_api_gen/Collation.h @@ -34,6 +34,8 @@ using std::vector; using google::protobuf::Descriptor; using google::protobuf::FieldDescriptor; +const int PULL_ATOM_START_ID = 10000; + /** * The types for atom parameters. */ diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp index 8038a3a4f44e..991547916919 100644 --- a/tools/stats_log_api_gen/main.cpp +++ b/tools/stats_log_api_gen/main.cpp @@ -18,8 +18,6 @@ using namespace std; namespace android { namespace stats_log_api_gen { -const int PULL_ATOM_START_ID = 1000; - int maxPushedAtomId = 2; using android::os::statsd::Atom; @@ -235,6 +233,10 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms, chainField.name.c_str(), chainField.name.c_str()); } } + } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { + fprintf(out, ", const std::map<int, int64_t>& arg%d_1, " + "const std::map<int, char const*>& arg%d_2, " + "const std::map<int, float>& arg%d_3", argIndex, argIndex, argIndex); } else { fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex); } @@ -277,6 +279,30 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms, fprintf(out, " event.end();\n"); fprintf(out, " }\n"); fprintf(out, " event.end();\n\n"); + } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { + fprintf(out, " event.begin();\n\n"); + fprintf(out, " for (const auto& it : arg%d_1) {\n", argIndex); + fprintf(out, " event.begin();\n"); + fprintf(out, " event << it.first;\n"); + fprintf(out, " event << it.second;\n"); + fprintf(out, " event.end();\n"); + fprintf(out, " }\n"); + + fprintf(out, " for (const auto& it : arg%d_2) {\n", argIndex); + fprintf(out, " event.begin();\n"); + fprintf(out, " event << it.first;\n"); + fprintf(out, " event << it.second;\n"); + fprintf(out, " event.end();\n"); + fprintf(out, " }\n"); + + fprintf(out, " for (const auto& it : arg%d_3) {\n", argIndex); + fprintf(out, " event.begin();\n"); + fprintf(out, " event << it.first;\n"); + fprintf(out, " event << it.second;\n"); + fprintf(out, " event.end();\n"); + fprintf(out, " }\n"); + + fprintf(out, " event.end();\n\n"); } else { if (*arg == JAVA_TYPE_STRING) { fprintf(out, " if (arg%d == NULL) {\n", argIndex); @@ -300,7 +326,7 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms, signature != atoms.signatures.end(); signature++) { int argIndex; - fprintf(out, "int \n"); + fprintf(out, "int\n"); fprintf(out, "stats_write(int32_t code"); argIndex = 1; for (vector<java_type_t>::const_iterator arg = signature->begin(); @@ -317,6 +343,10 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms, chainField.name.c_str(), chainField.name.c_str()); } } + } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { + fprintf(out, ", const std::map<int, int64_t>& arg%d_1, " + "const std::map<int, char const*>& arg%d_2, " + "const std::map<int, float>& arg%d_3", argIndex, argIndex, argIndex); } else { fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex); } @@ -343,6 +373,8 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms, chainField.name.c_str(), chainField.name.c_str()); } } + } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { + fprintf(out, ", arg%d_1, arg%d_2, arg%d_3", argIndex, argIndex, argIndex); } else { fprintf(out, ", arg%d", argIndex); } @@ -496,6 +528,11 @@ static void write_cpp_usage( chainField.name.c_str(), chainField.name.c_str()); } } + } else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) { + fprintf(out, ", const std::map<int, int64_t>& %s_int" + ", const std::map<int, char const*>& %s_str" + ", const std::map<int, float>& %s_float", + field->name.c_str(), field->name.c_str(), field->name.c_str()); } else { fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str()); } @@ -508,7 +545,7 @@ static void write_cpp_method_header( const AtomDecl &attributionDecl) { for (set<vector<java_type_t>>::const_iterator signature = signatures.begin(); signature != signatures.end(); signature++) { - fprintf(out, "int %s(int32_t code ", method_name.c_str()); + fprintf(out, "int %s(int32_t code", method_name.c_str()); int argIndex = 1; for (vector<java_type_t>::const_iterator arg = signature->begin(); arg != signature->end(); arg++) { @@ -523,6 +560,10 @@ static void write_cpp_method_header( chainField.name.c_str(), chainField.name.c_str()); } } + } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { + fprintf(out, ", const std::map<int, int64_t>& arg%d_1, " + "const std::map<int, char const*>& arg%d_2, " + "const std::map<int, float>& arg%d_3", argIndex, argIndex, argIndex); } else { fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex); } @@ -637,6 +678,8 @@ static void write_java_usage(FILE* out, const string& method_name, const string& field != atom.fields.end(); field++) { if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) { fprintf(out, ", android.os.WorkSource workSource"); + } else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) { + fprintf(out, ", SparseArray<Object> value_map"); } else { fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str()); } @@ -658,6 +701,8 @@ static void write_java_method( fprintf(out, ", %s[] %s", java_type_name(chainField.javaType), chainField.name.c_str()); } + } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { + fprintf(out, ", SparseArray<Object> value_map"); } else { fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex); } @@ -746,6 +791,7 @@ write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionD fprintf(out, "package android.util;\n"); fprintf(out, "\n"); fprintf(out, "import android.os.WorkSource;\n"); + fprintf(out, "import android.util.SparseArray;\n"); fprintf(out, "import java.util.ArrayList;\n"); fprintf(out, "\n"); fprintf(out, "\n"); @@ -837,6 +883,8 @@ jni_array_type_name(java_type_t type) switch (type) { case JAVA_TYPE_INT: return "jintArray"; + case JAVA_TYPE_FLOAT: + return "jfloatArray"; case JAVA_TYPE_STRING: return "jobjectArray"; default: @@ -873,6 +921,9 @@ jni_function_name(const string& method_name, const vector<java_type_t>& signatur case JAVA_TYPE_ATTRIBUTION_CHAIN: result += "_AttributionChain"; break; + case JAVA_TYPE_KEY_VALUE_PAIR: + result += "_KeyValuePairs"; + break; default: result += "_UNKNOWN"; break; @@ -914,6 +965,8 @@ jni_function_signature(const vector<java_type_t>& signature, const AtomDecl &att result += "["; result += java_type_signature(chainField.javaType); } + } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { + result += "Landroid/util/SparseArray;"; } else { result += java_type_signature(*arg); } @@ -922,6 +975,43 @@ jni_function_signature(const vector<java_type_t>& signature, const AtomDecl &att return result; } +static void write_key_value_map_jni(FILE* out) { + fprintf(out, " std::map<int, int64_t> int64_t_map;\n"); + fprintf(out, " std::map<int, float> float_map;\n"); + fprintf(out, " std::map<int, char const*> string_map;\n\n"); + + fprintf(out, " jclass jmap_class = env->FindClass(\"android/util/SparseArray\");\n"); + + fprintf(out, " jmethodID jget_size_method = env->GetMethodID(jmap_class, \"size\", \"()I\");\n"); + fprintf(out, " jmethodID jget_key_method = env->GetMethodID(jmap_class, \"keyAt\", \"(I)I\");\n"); + fprintf(out, " jmethodID jget_value_method = env->GetMethodID(jmap_class, \"valueAt\", \"(I)Ljava/lang/Object;\");\n\n"); + + + fprintf(out, " std::vector<std::unique_ptr<ScopedUtfChars>> scoped_ufs;\n\n"); + + fprintf(out, " jclass jlong_class = env->FindClass(\"java/lang/Long\");\n"); + fprintf(out, " jclass jfloat_class = env->FindClass(\"java/lang/Float\");\n"); + fprintf(out, " jclass jstring_class = env->FindClass(\"java/lang/String\");\n"); + fprintf(out, " jmethodID jget_long_method = env->GetMethodID(jlong_class, \"longValue\", \"()J\");\n"); + fprintf(out, " jmethodID jget_float_method = env->GetMethodID(jfloat_class, \"floatValue\", \"()F\");\n\n"); + + fprintf(out, " jint jsize = env->CallIntMethod(value_map, jget_size_method);\n"); + fprintf(out, " for(int i = 0; i < jsize; i++) {\n"); + fprintf(out, " jint key = env->CallIntMethod(value_map, jget_key_method, i);\n"); + fprintf(out, " jobject jvalue_obj = env->CallObjectMethod(value_map, jget_value_method, i);\n"); + fprintf(out, " if (jvalue_obj == NULL) { continue; }\n"); + fprintf(out, " if (env->IsInstanceOf(jvalue_obj, jlong_class)) {\n"); + fprintf(out, " int64_t_map[key] = env->CallLongMethod(jvalue_obj, jget_long_method);\n"); + fprintf(out, " } else if (env->IsInstanceOf(jvalue_obj, jfloat_class)) {\n"); + fprintf(out, " float_map[key] = env->CallFloatMethod(jvalue_obj, jget_float_method);\n"); + fprintf(out, " } else if (env->IsInstanceOf(jvalue_obj, jstring_class)) {\n"); + fprintf(out, " std::unique_ptr<ScopedUtfChars> utf(new ScopedUtfChars(env, (jstring)jvalue_obj));\n"); + fprintf(out, " if (utf->c_str() != NULL) { string_map[key] = utf->c_str(); }\n"); + fprintf(out, " scoped_ufs.push_back(std::move(utf));\n"); + fprintf(out, " }\n"); + fprintf(out, " }\n"); +} + static int write_stats_log_jni(FILE* out, const string& java_method_name, const string& cpp_method_name, const set<vector<java_type_t>>& signatures, const AtomDecl &attributionDecl) @@ -942,6 +1032,8 @@ write_stats_log_jni(FILE* out, const string& java_method_name, const string& cpp fprintf(out, ", %s %s", jni_array_type_name(chainField.javaType), chainField.name.c_str()); } + } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { + fprintf(out, ", jobject value_map"); } else { fprintf(out, ", %s arg%d", jni_type_name(*arg), argIndex); } @@ -954,6 +1046,7 @@ write_stats_log_jni(FILE* out, const string& java_method_name, const string& cpp // Prepare strings argIndex = 1; bool hadStringOrChain = false; + bool isKeyValuePairAtom = false; for (vector<java_type_t>::const_iterator arg = signature->begin(); arg != signature->end(); arg++) { if (*arg == JAVA_TYPE_STRING) { @@ -1006,18 +1099,23 @@ write_stats_log_jni(FILE* out, const string& java_method_name, const string& cpp } fprintf(out, "\n"); } + } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { + isKeyValuePairAtom = true; } argIndex++; } // Emit this to quiet the unused parameter warning if there were no strings or attribution // chains. - if (!hadStringOrChain) { + if (!hadStringOrChain && !isKeyValuePairAtom) { fprintf(out, " (void)env;\n"); } + if (isKeyValuePairAtom) { + write_key_value_map_jni(out); + } // stats_write call argIndex = 1; - fprintf(out, " int ret = android::util::%s(code", cpp_method_name.c_str()); + fprintf(out, "\n int ret = android::util::%s(code", cpp_method_name.c_str()); for (vector<java_type_t>::const_iterator arg = signature->begin(); arg != signature->end(); arg++) { if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) { @@ -1030,6 +1128,8 @@ write_stats_log_jni(FILE* out, const string& java_method_name, const string& cpp fprintf(out, ", %s_vec", chainField.name.c_str()); } } + } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { + fprintf(out, ", int64_t_map, string_map, float_map"); } else { const char *argName = (*arg == JAVA_TYPE_STRING) ? "str" : "arg"; fprintf(out, ", (%s)%s%d", cpp_type_name(*arg), argName, argIndex); @@ -1063,6 +1163,7 @@ write_stats_log_jni(FILE* out, const string& java_method_name, const string& cpp } argIndex++; } + fprintf(out, " return ret;\n"); fprintf(out, "}\n"); diff --git a/tools/validatekeymaps/Android.bp b/tools/validatekeymaps/Android.bp index 6fb278c83f0a..819e75ba1fed 100644 --- a/tools/validatekeymaps/Android.bp +++ b/tools/validatekeymaps/Android.bp @@ -15,6 +15,7 @@ cc_binary_host { ], static_libs: [ + "libbase", "libinput", "libutils", "libcutils", diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp index bbfcba6272b2..f31f771004bd 100644 --- a/tools/validatekeymaps/Main.cpp +++ b/tools/validatekeymaps/Main.cpp @@ -18,7 +18,6 @@ #include <input/KeyLayoutMap.h> #include <input/VirtualKeyMap.h> #include <utils/PropertyMap.h> -#include <utils/String8.h> #include <stdarg.h> #include <stdio.h> @@ -98,7 +97,7 @@ static bool validateFile(const char* filename) { case FILETYPE_KEYLAYOUT: { sp<KeyLayoutMap> map; - status_t status = KeyLayoutMap::load(String8(filename), &map); + status_t status = KeyLayoutMap::load(filename, &map); if (status) { error("Error %d parsing key layout file.\n\n", status); return false; @@ -108,7 +107,7 @@ static bool validateFile(const char* filename) { case FILETYPE_KEYCHARACTERMAP: { sp<KeyCharacterMap> map; - status_t status = KeyCharacterMap::load(String8(filename), + status_t status = KeyCharacterMap::load(filename, KeyCharacterMap::FORMAT_ANY, &map); if (status) { error("Error %d parsing key character map file.\n\n", status); @@ -130,7 +129,7 @@ static bool validateFile(const char* filename) { case FILETYPE_VIRTUALKEYDEFINITION: { VirtualKeyMap* map; - status_t status = VirtualKeyMap::load(String8(filename), &map); + status_t status = VirtualKeyMap::load(filename, &map); if (status) { error("Error %d parsing virtual key definition file.\n\n", status); return false; |