summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/aapt2/Diagnostics.h8
-rw-r--r--tools/aapt2/compile/XmlIdCollector.cpp28
-rw-r--r--tools/aapt2/compile/XmlIdCollector_test.cpp10
-rw-r--r--tools/aapt2/link/ManifestFixer.cpp3
-rw-r--r--tools/aapt2/util/Files.cpp15
-rwxr-xr-xtools/aosp/aosp_sha.sh20
-rwxr-xr-xtools/hiddenapi/generate_hiddenapi_lists.py4
-rwxr-xr-xtools/hiddenapi/sort_api.sh6
-rw-r--r--tools/processors/unsupportedappusage/Android.bp25
-rw-r--r--tools/processors/unsupportedappusage/META-INF/services/javax.annotation.processing.Processor1
-rw-r--r--tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java228
-rw-r--r--tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java173
-rw-r--r--tools/stats_log_api_gen/Collation.cpp29
-rw-r--r--tools/stats_log_api_gen/Collation.h2
-rw-r--r--tools/stats_log_api_gen/main.cpp113
-rw-r--r--tools/validatekeymaps/Android.bp1
-rw-r--r--tools/validatekeymaps/Main.cpp7
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;