Merge "AAPT2: Support generating Manifest.java"
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index 275476c..a41d2d7 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -45,9 +45,11 @@
 	ConfigDescription.cpp \
 	Debug.cpp \
 	Flags.cpp \
-	JavaClassGenerator.cpp \
+	java/AnnotationProcessor.cpp \
+	java/JavaClassGenerator.cpp \
+	java/ManifestClassGenerator.cpp \
+	java/ProguardRules.cpp \
 	Locale.cpp \
-	ProguardRules.cpp \
 	Resource.cpp \
 	ResourceParser.cpp \
 	ResourceTable.cpp \
@@ -76,7 +78,8 @@
 	util/StringPiece_test.cpp \
 	util/Util_test.cpp \
 	ConfigDescription_test.cpp \
-	JavaClassGenerator_test.cpp \
+	java/JavaClassGenerator_test.cpp \
+	java/ManifestClassGenerator_test.cpp \
 	Locale_test.cpp \
 	Resource_test.cpp \
 	ResourceParser_test.cpp \
diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp
index 1962f58..34dc1d5 100644
--- a/tools/aapt2/Resource.cpp
+++ b/tools/aapt2/Resource.cpp
@@ -36,7 +36,6 @@
         case ResourceType::kFraction:      return u"fraction";
         case ResourceType::kId:            return u"id";
         case ResourceType::kInteger:       return u"integer";
-        case ResourceType::kIntegerArray:  return u"integer-array";
         case ResourceType::kInterpolator:  return u"interpolator";
         case ResourceType::kLayout:        return u"layout";
         case ResourceType::kMenu:          return u"menu";
@@ -65,7 +64,6 @@
         { u"fraction", ResourceType::kFraction },
         { u"id", ResourceType::kId },
         { u"integer", ResourceType::kInteger },
-        { u"integer-array", ResourceType::kIntegerArray },
         { u"interpolator", ResourceType::kInterpolator },
         { u"layout", ResourceType::kLayout },
         { u"menu", ResourceType::kMenu },
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index 7ef1897..a7afbb5 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -47,7 +47,6 @@
     kFraction,
     kId,
     kInteger,
-    kIntegerArray,
     kInterpolator,
     kLayout,
     kMenu,
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 44710eb..0c7a4d5 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -308,6 +308,9 @@
         } else if (elementName == u"dimen") {
             parsedResource.name.type = ResourceType::kDimen;
             result = parsePrimitive(parser, &parsedResource);
+        } else if (elementName == u"fraction") {
+            parsedResource.name.type = ResourceType::kFraction;
+            result = parsePrimitive(parser, &parsedResource);
         } else if (elementName == u"style") {
             parsedResource.name.type = ResourceType::kStyle;
             result = parseStyle(parser, &parsedResource);
@@ -321,7 +324,7 @@
             parsedResource.name.type = ResourceType::kArray;
             result = parseArray(parser, &parsedResource, android::ResTable_map::TYPE_STRING);
         } else if (elementName == u"integer-array") {
-            parsedResource.name.type = ResourceType::kIntegerArray;
+            parsedResource.name.type = ResourceType::kArray;
             result = parseArray(parser, &parsedResource, android::ResTable_map::TYPE_INTEGER);
         } else if (elementName == u"declare-styleable") {
             parsedResource.name.type = ResourceType::kStyleable;
@@ -464,6 +467,8 @@
         typeMask |= android::ResTable_map::TYPE_INTEGER;
         break;
 
+    case ResourceType::kFraction:
+        // fallthrough
     case ResourceType::kDimen:
         typeMask |= android::ResTable_map::TYPE_DIMENSION
                   | android::ResTable_map::TYPE_FLOAT
@@ -576,6 +581,12 @@
     return mask;
 }
 
+/**
+ * Returns true if the element is <skip> or <eat-comment> and can be safely ignored.
+ */
+static bool shouldIgnoreElement(const StringPiece16& ns, const StringPiece16& name) {
+    return ns.empty() && (name == u"skip" || name == u"eat-comment");
+}
 
 bool ResourceParser::parseAttr(XmlPullParser* parser, ParsedResource* outResource) {
     outResource->source = mSource.withLine(parser->getLineNumber());
@@ -613,25 +624,30 @@
     bool error = false;
     const size_t depth = parser->getDepth();
     while (XmlPullParser::nextChildNode(parser, depth)) {
-        if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
-            // Skip comments and text.
+        if (parser->getEvent() == XmlPullParser::Event::kComment) {
+            comment = util::trimWhitespace(parser->getComment()).toString();
+            continue;
+        } else if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
+            // Skip text.
             continue;
         }
 
+        const Source itemSource = mSource.withLine(parser->getLineNumber());
         const std::u16string& elementNamespace = parser->getElementNamespace();
         const std::u16string& elementName = parser->getElementName();
-        if (elementNamespace == u"" && (elementName == u"flag" || elementName == u"enum")) {
+        if (elementNamespace.empty() && (elementName == u"flag" || elementName == u"enum")) {
             if (elementName == u"enum") {
                 if (typeMask & android::ResTable_map::TYPE_FLAGS) {
-                    mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+                    mDiag->error(DiagMessage(itemSource)
                                  << "can not define an <enum>; already defined a <flag>");
                     error = true;
                     continue;
                 }
                 typeMask |= android::ResTable_map::TYPE_ENUM;
+
             } else if (elementName == u"flag") {
                 if (typeMask & android::ResTable_map::TYPE_ENUM) {
-                    mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+                    mDiag->error(DiagMessage(itemSource)
                                  << "can not define a <flag>; already defined an <enum>");
                     error = true;
                     continue;
@@ -642,21 +658,22 @@
             if (Maybe<Attribute::Symbol> s = parseEnumOrFlagItem(parser, elementName)) {
                 ParsedResource childResource;
                 childResource.name = s.value().symbol.name.value();
-                childResource.source = mSource.withLine(parser->getLineNumber());
+                childResource.source = itemSource;
                 childResource.value = util::make_unique<Id>();
                 outResource->childResources.push_back(std::move(childResource));
+
+                s.value().symbol.setComment(std::move(comment));
+                s.value().symbol.setSource(itemSource);
                 items.push_back(std::move(s.value()));
             } else {
                 error = true;
             }
-        } else if (elementName == u"skip" || elementName == u"eat-comment") {
-            comment = u"";
-
-        } else {
-            mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
-                         << ":" << elementName << ">");
+        } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
+            mDiag->error(DiagMessage(itemSource) << ":" << elementName << ">");
             error = true;
         }
+
+        comment = {};
     }
 
     if (error) {
@@ -716,11 +733,10 @@
         p++;
     }
 
-    return ResourceName{ package.toString(), ResourceType::kAttr,
-        name.empty() ? str.toString() : name.toString() };
+    return ResourceName(package.toString(), ResourceType::kAttr,
+                        name.empty() ? str.toString() : name.toString());
 }
 
-
 bool ResourceParser::parseStyleItem(XmlPullParser* parser, Style* style) {
     const Source source = mSource.withLine(parser->getLineNumber());
 
@@ -783,7 +799,6 @@
     }
 
     bool error = false;
-    std::u16string comment;
     const size_t depth = parser->getDepth();
     while (XmlPullParser::nextChildNode(parser, depth)) {
         if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
@@ -796,11 +811,7 @@
         if (elementNamespace == u"" && elementName == u"item") {
             error |= !parseStyleItem(parser, style.get());
 
-        } else if (elementNamespace.empty() &&
-                (elementName == u"skip" || elementName == u"eat-comment")) {
-            comment = u"";
-
-        } else {
+        } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
             mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
                          << ":" << elementName << ">");
             error = true;
@@ -820,7 +831,6 @@
     const Source source = mSource.withLine(parser->getLineNumber());
     std::unique_ptr<Array> array = util::make_unique<Array>();
 
-    std::u16string comment;
     bool error = false;
     const size_t depth = parser->getDepth();
     while (XmlPullParser::nextChildNode(parser, depth)) {
@@ -839,13 +849,10 @@
                 error = true;
                 continue;
             }
+            item->setSource(itemSource);
             array->items.emplace_back(std::move(item));
 
-        } else if (elementNamespace.empty() &&
-                (elementName == u"skip" || elementName == u"eat-comment")) {
-            comment = u"";
-
-        } else {
+        } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
             mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
                          << "unknown tag <" << elementNamespace << ":" << elementName << ">");
             error = true;
@@ -864,7 +871,6 @@
     const Source source = mSource.withLine(parser->getLineNumber());
     std::unique_ptr<Plural> plural = util::make_unique<Plural>();
 
-    std::u16string comment;
     bool error = false;
     const size_t depth = parser->getDepth();
     while (XmlPullParser::nextChildNode(parser, depth)) {
@@ -873,13 +879,14 @@
             continue;
         }
 
+        const Source itemSource = mSource.withLine(parser->getLineNumber());
         const std::u16string& elementNamespace = parser->getElementNamespace();
         const std::u16string& elementName = parser->getElementName();
         if (elementNamespace.empty() && elementName == u"item") {
             const auto endAttrIter = parser->endAttributes();
             auto attrIter = parser->findAttribute(u"", u"quantity");
             if (attrIter == endAttrIter || attrIter->value.empty()) {
-                mDiag->error(DiagMessage(source) << "<item> in <plurals> requires attribute "
+                mDiag->error(DiagMessage(itemSource) << "<item> in <plurals> requires attribute "
                              << "'quantity'");
                 error = true;
                 continue;
@@ -900,7 +907,7 @@
             } else if (trimmedQuantity == u"other") {
                 index = Plural::Other;
             } else {
-                mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+                mDiag->error(DiagMessage(itemSource)
                              << "<item> in <plural> has invalid value '" << trimmedQuantity
                              << "' for attribute 'quantity'");
                 error = true;
@@ -908,7 +915,7 @@
             }
 
             if (plural->values[index]) {
-                mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+                mDiag->error(DiagMessage(itemSource)
                              << "duplicate quantity '" << trimmedQuantity << "'");
                 error = true;
                 continue;
@@ -918,11 +925,10 @@
                                                    kNoRawString))) {
                 error = true;
             }
-        } else if (elementNamespace.empty() &&
-                (elementName == u"skip" || elementName == u"eat-comment")) {
-            comment = u"";
-        } else {
-            mDiag->error(DiagMessage(source) << "unknown tag <" << elementNamespace << ":"
+            plural->values[index]->setSource(itemSource);
+
+        } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
+            mDiag->error(DiagMessage(itemSource) << "unknown tag <" << elementNamespace << ":"
                          << elementName << ">");
             error = true;
         }
@@ -944,43 +950,52 @@
     bool error = false;
     const size_t depth = parser->getDepth();
     while (XmlPullParser::nextChildNode(parser, depth)) {
-        if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
-            // Ignore text and comments.
+        if (parser->getEvent() == XmlPullParser::Event::kComment) {
+            comment = util::trimWhitespace(parser->getComment()).toString();
+            continue;
+        } else if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
+            // Ignore text.
             continue;
         }
 
+        const Source itemSource = mSource.withLine(parser->getLineNumber());
         const std::u16string& elementNamespace = parser->getElementNamespace();
         const std::u16string& elementName = parser->getElementName();
         if (elementNamespace.empty() && elementName == u"attr") {
             const auto endAttrIter = parser->endAttributes();
             auto attrIter = parser->findAttribute(u"", u"name");
             if (attrIter == endAttrIter || attrIter->value.empty()) {
-                mDiag->error(DiagMessage(source) << "<attr> tag must have a 'name' attribute");
+                mDiag->error(DiagMessage(itemSource) << "<attr> tag must have a 'name' attribute");
                 error = true;
                 continue;
             }
 
+            // Create the ParsedResource that will add the attribute to the table.
             ParsedResource childResource;
             childResource.name = ResourceName({}, ResourceType::kAttr, attrIter->value);
-            childResource.source = mSource.withLine(parser->getLineNumber());
+            childResource.source = itemSource;
+            childResource.comment = std::move(comment);
 
             if (!parseAttrImpl(parser, &childResource, true)) {
                 error = true;
                 continue;
             }
 
-            styleable->entries.push_back(Reference(childResource.name));
+            // Create the reference to this attribute.
+            Reference childRef(childResource.name);
+            childRef.setComment(childResource.comment);
+            childRef.setSource(itemSource);
+            styleable->entries.push_back(std::move(childRef));
+
             outResource->childResources.push_back(std::move(childResource));
 
-        } else if (elementNamespace.empty() &&
-                (elementName == u"skip" || elementName == u"eat-comment")) {
-            comment = u"";
-
-        } else {
-            mDiag->error(DiagMessage(source) << "unknown tag <" << elementNamespace << ":"
+        } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
+            mDiag->error(DiagMessage(itemSource) << "unknown tag <" << elementNamespace << ":"
                          << elementName << ">");
             error = true;
         }
+
+        comment = {};
     }
 
     if (error) {
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index af6bf67..2f5daae 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -414,6 +414,34 @@
     EXPECT_EQ(value->getComment(), u"One");
 }
 
+TEST_F(ResourceParserTest, ParseNestedComments) {
+    // We only care about declare-styleable and enum/flag attributes because comments
+    // from those end up in R.java
+    std::string input = R"EOF(
+        <declare-styleable name="foo">
+          <!-- The name of the bar -->
+          <attr name="barName" format="string|reference" />
+        </declare-styleable>
+
+        <attr name="foo">
+          <!-- The very first -->
+          <enum name="one" value="1" />
+        </attr>)EOF";
+    ASSERT_TRUE(testParse(input));
+
+    Styleable* styleable = test::getValue<Styleable>(&mTable, u"@styleable/foo");
+    ASSERT_NE(nullptr, styleable);
+    ASSERT_EQ(1u, styleable->entries.size());
+
+    EXPECT_EQ(StringPiece16(u"The name of the bar"), styleable->entries.front().getComment());
+
+    Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/foo");
+    ASSERT_NE(nullptr, attr);
+    ASSERT_EQ(1u, attr->symbols.size());
+
+    EXPECT_EQ(StringPiece16(u"The very first"), attr->symbols.front().symbol.getComment());
+}
+
 /*
  * Declaring an ID as public should not require a separate definition
  * (as an ID has no value).
diff --git a/tools/aapt2/Resource_test.cpp b/tools/aapt2/Resource_test.cpp
index d957999..48dc521 100644
--- a/tools/aapt2/Resource_test.cpp
+++ b/tools/aapt2/Resource_test.cpp
@@ -69,10 +69,6 @@
     ASSERT_NE(type, nullptr);
     EXPECT_EQ(*type, ResourceType::kInteger);
 
-    type = parseResourceType(u"integer-array");
-    ASSERT_NE(type, nullptr);
-    EXPECT_EQ(*type, ResourceType::kIntegerArray);
-
     type = parseResourceType(u"interpolator");
     ASSERT_NE(type, nullptr);
     EXPECT_EQ(*type, ResourceType::kInterpolator);
diff --git a/tools/aapt2/XmlDom.cpp b/tools/aapt2/XmlDom.cpp
index d948775..b769c76 100644
--- a/tools/aapt2/XmlDom.cpp
+++ b/tools/aapt2/XmlDom.cpp
@@ -125,7 +125,7 @@
     Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
 
     assert(!stack->nodeStack.empty());
-    stack->nodeStack.top()->comment = std::move(stack->pendingComment);
+    //stack->nodeStack.top()->comment = std::move(stack->pendingComment);
     stack->nodeStack.pop();
 }
 
@@ -194,7 +194,7 @@
 
     XML_ParserFree(parser);
     if (stack.root) {
-        return util::make_unique<XmlResource>(ResourceFile{}, std::move(stack.root));
+        return util::make_unique<XmlResource>(ResourceFile{ {}, {}, source }, std::move(stack.root));
     }
     return {};
 }
@@ -317,6 +317,22 @@
     return util::make_unique<XmlResource>(ResourceFile{}, std::move(root));
 }
 
+Element* findRootElement(Node* node) {
+    if (!node) {
+        return nullptr;
+    }
+
+    Element* el = nullptr;
+    while ((el = nodeCast<Element>(node)) == nullptr) {
+        if (node->children.empty()) {
+            return nullptr;
+        }
+        // We are looking for the first element, and namespaces can only have one child.
+        node = node->children.front().get();
+    }
+    return el;
+}
+
 void Node::addChild(std::unique_ptr<Node> child) {
     child->parent = this;
     children.push_back(std::move(child));
diff --git a/tools/aapt2/XmlDom.h b/tools/aapt2/XmlDom.h
index c095f08..b1987f1 100644
--- a/tools/aapt2/XmlDom.h
+++ b/tools/aapt2/XmlDom.h
@@ -132,6 +132,8 @@
 std::unique_ptr<XmlResource> inflate(const void* data, size_t dataLen, IDiagnostics* diag,
                                      const Source& source);
 
+Element* findRootElement(Node* node);
+
 /**
  * A visitor interface for the different XML Node subtypes. This will not traverse into
  * children. Use Visitor for that.
diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp
new file mode 100644
index 0000000..16440bc
--- /dev/null
+++ b/tools/aapt2/java/AnnotationProcessor.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "java/AnnotationProcessor.h"
+#include "util/Util.h"
+
+#include <algorithm>
+
+namespace aapt {
+
+void AnnotationProcessor::appendCommentLine(const StringPiece16& line) {
+    static const std::string sDeprecated = "@deprecated";
+    static const std::string sSystemApi = "@SystemApi";
+
+    if (line.empty()) {
+        return;
+    }
+
+    std::string comment = util::utf16ToUtf8(line);
+
+    if (comment.find(sDeprecated) != std::string::npos && !mDeprecated) {
+        mDeprecated = true;
+        if (!mAnnotations.empty()) {
+            mAnnotations += "\n";
+        }
+        mAnnotations += mPrefix;
+        mAnnotations += "@Deprecated";
+    }
+
+    if (comment.find(sSystemApi) != std::string::npos && !mSystemApi) {
+        mSystemApi = true;
+        if (!mAnnotations.empty()) {
+            mAnnotations += "\n";
+        }
+        mAnnotations += mPrefix;
+        mAnnotations += "@android.annotations.SystemApi";
+    }
+
+    if (mComment.empty()) {
+        mComment += mPrefix;
+        mComment += "/**";
+    }
+
+    mComment += "\n";
+    mComment += mPrefix;
+    mComment += " * ";
+    mComment += std::move(comment);
+}
+
+void AnnotationProcessor::appendComment(const StringPiece16& comment) {
+    // We need to process line by line to clean-up whitespace and append prefixes.
+    for (StringPiece16 line : util::tokenize(comment, u'\n')) {
+        appendCommentLine(util::trimWhitespace(line));
+    }
+}
+
+std::string AnnotationProcessor::buildComment() {
+    mComment += "\n";
+    mComment += mPrefix;
+    mComment += " */";
+    return std::move(mComment);
+}
+
+std::string AnnotationProcessor::buildAnnotations() {
+    return std::move(mAnnotations);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/java/AnnotationProcessor.h b/tools/aapt2/java/AnnotationProcessor.h
new file mode 100644
index 0000000..b472109
--- /dev/null
+++ b/tools/aapt2/java/AnnotationProcessor.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAPT_JAVA_ANNOTATIONPROCESSOR_H
+#define AAPT_JAVA_ANNOTATIONPROCESSOR_H
+
+#include "util/StringPiece.h"
+
+#include <string>
+
+namespace aapt {
+
+/**
+ * Builds a JavaDoc comment from a set of XML comments.
+ * This will also look for instances of @SystemApi and convert them to
+ * actual Java annotations.
+ *
+ * Example:
+ *
+ * Input XML:
+ *
+ * <!-- This is meant to be hidden because
+ *      It is system api. Also it is @deprecated
+ *      @SystemApi
+ *      -->
+ *
+ * Output JavaDoc:
+ *
+ *  /\*
+ *   * This is meant to be hidden because
+ *   * It is system api. Also it is @deprecated
+ *   * @SystemApi
+ *   *\/
+ *
+ * Output Annotations:
+ *
+ * @Deprecated
+ * @android.annotation.SystemApi
+ *
+ */
+class AnnotationProcessor {
+public:
+    /**
+     * Creates an AnnotationProcessor with a given prefix for each line generated.
+     * This is usually a set of spaces for indentation.
+     */
+    AnnotationProcessor(const StringPiece& prefix) : mPrefix(prefix.toString()) {
+    }
+
+    /**
+     * Adds more comments. Since resources can have various values with different configurations,
+     * we need to collect all the comments.
+     */
+    void appendComment(const StringPiece16& comment);
+
+    /**
+     * Finishes the comment and moves it to the caller. Subsequent calls to buildComment() have
+     * undefined results.
+     */
+    std::string buildComment();
+
+    /**
+     * Finishes the annotation and moves it to the caller. Subsequent calls to buildAnnotations()
+     * have undefined results.
+     */
+    std::string buildAnnotations();
+
+private:
+    std::string mPrefix;
+    std::string mComment;
+    std::string mAnnotations;
+    bool mDeprecated = false;
+    bool mSystemApi = false;
+
+    void appendCommentLine(const StringPiece16& line);
+};
+
+} // namespace aapt
+
+#endif /* AAPT_JAVA_ANNOTATIONPROCESSOR_H */
diff --git a/tools/aapt2/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
similarity index 98%
rename from tools/aapt2/JavaClassGenerator.cpp
rename to tools/aapt2/java/JavaClassGenerator.cpp
index cdf1b6a..0175489 100644
--- a/tools/aapt2/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-#include "JavaClassGenerator.h"
 #include "NameMangler.h"
 #include "Resource.h"
 #include "ResourceTable.h"
 #include "ResourceValues.h"
+#include "java/AnnotationProcessor.h"
+#include "java/JavaClassGenerator.h"
 #include "util/StringPiece.h"
 
 #include <algorithm>
diff --git a/tools/aapt2/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h
similarity index 100%
rename from tools/aapt2/JavaClassGenerator.h
rename to tools/aapt2/java/JavaClassGenerator.h
diff --git a/tools/aapt2/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
similarity index 99%
rename from tools/aapt2/JavaClassGenerator_test.cpp
rename to tools/aapt2/java/JavaClassGenerator_test.cpp
index cc5e981..3625f9c 100644
--- a/tools/aapt2/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "JavaClassGenerator.h"
+#include "java/JavaClassGenerator.h"
 #include "util/Util.h"
 
 #include "test/Builders.h"
diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp
new file mode 100644
index 0000000..c12da64
--- /dev/null
+++ b/tools/aapt2/java/ManifestClassGenerator.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Source.h"
+#include "XmlDom.h"
+
+#include "java/AnnotationProcessor.h"
+#include "java/ManifestClassGenerator.h"
+#include "util/Maybe.h"
+
+#include <algorithm>
+
+namespace aapt {
+
+constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
+
+static Maybe<StringPiece16> extractJavaIdentifier(IDiagnostics* diag, const Source& source,
+                                                  const StringPiece16& value) {
+    const StringPiece16 sep = u".";
+    auto iter = std::find_end(value.begin(), value.end(), sep.begin(), sep.end());
+
+    StringPiece16 result;
+    if (iter != value.end()) {
+        result.assign(iter + sep.size(), value.end() - (iter + sep.size()));
+    } else {
+        result = value;
+    }
+
+    if (result.empty()) {
+        diag->error(DiagMessage(source) << "empty symbol");
+        return {};
+    }
+
+    iter = util::findNonAlphaNumericAndNotInSet(result, u"_");
+    if (iter != result.end()) {
+        diag->error(DiagMessage(source)
+                    << "invalid character '" << StringPiece16(iter, 1)
+                    << "' in '" << result << "'");
+        return {};
+    }
+
+    if (*result.begin() >= u'0' && *result.begin() <= u'9') {
+        diag->error(DiagMessage(source) << "symbol can not start with a digit");
+        return {};
+    }
+
+    return result;
+}
+
+static bool writeSymbol(IDiagnostics* diag, const Source& source, xml::Element* el,
+                        std::ostream* out) {
+    xml::Attribute* attr = el->findAttribute(kSchemaAndroid, u"name");
+    if (!attr) {
+        diag->error(DiagMessage(source) << "<" << el->name << "> must define 'android:name'");
+        return false;
+    }
+
+    Maybe<StringPiece16> result = extractJavaIdentifier(diag, source.withLine(el->lineNumber),
+                                                        attr->value);
+    if (!result) {
+        return false;
+    }
+
+    *out << "\n";
+
+    if (!util::trimWhitespace(el->comment).empty()) {
+        AnnotationProcessor processor("    ");
+        processor.appendComment(el->comment);
+        *out << processor.buildComment() << "\n";
+        std::string annotations = processor.buildAnnotations();
+        if (!annotations.empty()) {
+            *out << annotations << "\n";
+        }
+    }
+    *out << "    public static final String " << result.value() << "=\"" << attr->value << "\";\n";
+    return true;
+}
+
+bool ManifestClassGenerator::generate(IDiagnostics* diag, const StringPiece16& package,
+                                      XmlResource* res, std::ostream* out) {
+    xml::Element* el = xml::findRootElement(res->root.get());
+    if (!el) {
+        return false;
+    }
+
+    if (el->name != u"manifest" && !el->namespaceUri.empty()) {
+        diag->error(DiagMessage(res->file.source) << "no <manifest> root tag defined");
+        return false;
+    }
+
+    *out << "package " << package << ";\n\n"
+         << "public class Manifest {\n";
+
+    bool error = false;
+    std::vector<xml::Element*> children = el->getChildElements();
+
+
+    // First write out permissions.
+    *out << "  public static class permission {\n";
+    for (xml::Element* childEl : children) {
+        if (childEl->namespaceUri.empty() && childEl->name == u"permission") {
+            error |= !writeSymbol(diag, res->file.source, childEl, out);
+        }
+    }
+    *out << "  }\n";
+
+    // Next write out permission groups.
+    *out << "  public static class permission_group {\n";
+    for (xml::Element* childEl : children) {
+        if (childEl->namespaceUri.empty() && childEl->name == u"permission-group") {
+            error |= !writeSymbol(diag, res->file.source, childEl, out);
+        }
+    }
+    *out << "  }\n";
+
+    *out << "}\n";
+    return !error;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/java/ManifestClassGenerator.h b/tools/aapt2/java/ManifestClassGenerator.h
new file mode 100644
index 0000000..0f0998f
--- /dev/null
+++ b/tools/aapt2/java/ManifestClassGenerator.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAPT_JAVA_MANIFESTCLASSGENERATOR_H
+#define AAPT_JAVA_MANIFESTCLASSGENERATOR_H
+
+#include "Diagnostics.h"
+#include "process/IResourceTableConsumer.h"
+#include "util/StringPiece.h"
+
+#include <iostream>
+
+namespace aapt {
+
+struct ManifestClassGenerator {
+    bool generate(IDiagnostics* diag, const StringPiece16& package, XmlResource* res,
+                  std::ostream* out);
+};
+
+} // namespace aapt
+
+#endif /* AAPT_JAVA_MANIFESTCLASSGENERATOR_H */
diff --git a/tools/aapt2/java/ManifestClassGenerator_test.cpp b/tools/aapt2/java/ManifestClassGenerator_test.cpp
new file mode 100644
index 0000000..1b5bc05
--- /dev/null
+++ b/tools/aapt2/java/ManifestClassGenerator_test.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "java/ManifestClassGenerator.h"
+
+#include "test/Builders.h"
+#include "test/Context.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+TEST(ManifestClassGeneratorTest, NameIsProperlyGeneratedFromSymbol) {
+    std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+    std::unique_ptr<XmlResource> manifest = test::buildXmlDom(R"EOF(
+        <manifest xmlns:android="http://schemas.android.com/apk/res/android">
+          <permission android:name="android.permission.ACCESS_INTERNET" />
+          <permission android:name="android.DO_DANGEROUS_THINGS" />
+          <permission android:name="com.test.sample.permission.HUH" />
+          <permission-group android:name="foo.bar.PERMISSION" />
+        </manifest>)EOF");
+
+    std::stringstream out;
+    ManifestClassGenerator generator;
+    ASSERT_TRUE(generator.generate(context->getDiagnostics(), u"android", manifest.get(), &out));
+
+    std::string actual = out.str();
+
+    const size_t permissionClassPos = actual.find("public static class permission {");
+    const size_t permissionGroupClassPos = actual.find("public static class permission_group {");
+    ASSERT_NE(std::string::npos, permissionClassPos);
+    ASSERT_NE(std::string::npos, permissionGroupClassPos);
+
+    //
+    // Make sure these permissions are in the permission class.
+    //
+
+    size_t pos = actual.find("public static final String ACCESS_INTERNET="
+                             "\"android.permission.ACCESS_INTERNET\";");
+    EXPECT_GT(pos, permissionClassPos);
+    EXPECT_LT(pos, permissionGroupClassPos);
+
+    pos = actual.find("public static final String DO_DANGEROUS_THINGS="
+                      "\"android.DO_DANGEROUS_THINGS\";");
+    EXPECT_GT(pos, permissionClassPos);
+    EXPECT_LT(pos, permissionGroupClassPos);
+
+    pos = actual.find("public static final String HUH=\"com.test.sample.permission.HUH\";");
+    EXPECT_GT(pos, permissionClassPos);
+    EXPECT_LT(pos, permissionGroupClassPos);
+
+    //
+    // Make sure these permissions are in the permission_group class
+    //
+
+    pos = actual.find("public static final String PERMISSION="
+                      "\"foo.bar.PERMISSION\";");
+    EXPECT_GT(pos, permissionGroupClassPos);
+    EXPECT_LT(pos, std::string::npos);
+}
+
+TEST(ManifestClassGeneratorTest, CommentsAndAnnotationsArePresent) {
+    std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+    std::unique_ptr<XmlResource> manifest = test::buildXmlDom(R"EOF(
+        <manifest xmlns:android="http://schemas.android.com/apk/res/android">
+          <!-- Required to access the internet.
+               Added in API 1. -->
+          <permission android:name="android.permission.ACCESS_INTERNET" />
+          <!-- @deprecated This permission is for playing outside. -->
+          <permission android:name="android.permission.PLAY_OUTSIDE" />
+          <!-- This is a private permission for system only!
+               @hide
+               @SystemApi -->
+          <permission android:name="android.permission.SECRET" />
+        </manifest>)EOF");
+
+    std::stringstream out;
+    ManifestClassGenerator generator;
+    ASSERT_TRUE(generator.generate(context->getDiagnostics(), u"android", manifest.get(), &out));
+
+    std::string actual = out.str();
+
+    EXPECT_NE(std::string::npos, actual.find(
+R"EOF(    /**
+     * Required to access the internet.
+     * Added in API 1.
+     */
+    public static final String ACCESS_INTERNET="android.permission.ACCESS_INTERNET";)EOF"));
+
+    EXPECT_NE(std::string::npos, actual.find(
+R"EOF(    /**
+     * @deprecated This permission is for playing outside.
+     */
+    @Deprecated
+    public static final String PLAY_OUTSIDE="android.permission.PLAY_OUTSIDE";)EOF"));
+
+    EXPECT_NE(std::string::npos, actual.find(
+R"EOF(    /**
+     * This is a private permission for system only!
+     * @hide
+     * @SystemApi
+     */
+    @android.annotations.SystemApi
+    public static final String SECRET="android.permission.SECRET";)EOF"));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
similarity index 99%
rename from tools/aapt2/ProguardRules.cpp
rename to tools/aapt2/java/ProguardRules.cpp
index 7f4dc91..7683f27 100644
--- a/tools/aapt2/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-#include "ProguardRules.h"
 #include "XmlDom.h"
 
+#include "java/ProguardRules.h"
 #include "util/Util.h"
 
 #include <memory>
diff --git a/tools/aapt2/ProguardRules.h b/tools/aapt2/java/ProguardRules.h
similarity index 100%
rename from tools/aapt2/ProguardRules.h
rename to tools/aapt2/java/ProguardRules.h
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index ad701de..77918ac 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -17,15 +17,16 @@
 #include "AppInfo.h"
 #include "Debug.h"
 #include "Flags.h"
-#include "JavaClassGenerator.h"
 #include "NameMangler.h"
-#include "ProguardRules.h"
 #include "XmlDom.h"
 
 #include "compile/IdAssigner.h"
 #include "flatten/Archive.h"
 #include "flatten/TableFlattener.h"
 #include "flatten/XmlFlattener.h"
+#include "java/JavaClassGenerator.h"
+#include "java/ManifestClassGenerator.h"
+#include "java/ProguardRules.h"
 #include "link/Linkers.h"
 #include "link/TableMerger.h"
 #include "process/IResourceTableConsumer.h"
@@ -354,6 +355,36 @@
         return true;
     }
 
+    bool writeManifestJavaFile(XmlResource* manifestXml) {
+        if (!mOptions.generateJavaClassPath) {
+            return true;
+        }
+
+        std::string outPath = mOptions.generateJavaClassPath.value();
+        file::appendPath(&outPath,
+                         file::packageToPath(util::utf16ToUtf8(mContext.getCompilationPackage())));
+        file::mkdirs(outPath);
+        file::appendPath(&outPath, "Manifest.java");
+
+        std::ofstream fout(outPath, std::ofstream::binary);
+        if (!fout) {
+            mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
+            return false;
+        }
+
+        ManifestClassGenerator generator;
+        if (!generator.generate(mContext.getDiagnostics(), mContext.getCompilationPackage(),
+                                manifestXml, &fout)) {
+            return false;
+        }
+
+        if (!fout) {
+            mContext.getDiagnostics()->error(DiagMessage() << strerror(errno));
+            return false;
+        }
+        return true;
+    }
+
     bool writeProguardFile(const proguard::KeepSet& keepSet) {
         if (!mOptions.generateProguardRulesPath) {
             return true;
@@ -548,6 +579,12 @@
                     error = true;
                 }
 
+                if (mOptions.generateJavaClassPath) {
+                    if (!writeManifestJavaFile(manifestXml.get())) {
+                        error = true;
+                    }
+                }
+
                 if (!flattenXml(manifestXml.get(), "AndroidManifest.xml", {},
                                 archiveWriter.get())) {
                     error = true;
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index 1b510e7..89cd972 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -19,8 +19,8 @@
 
 #include "ResourceTable.h"
 #include "ResourceValues.h"
-#include "util/Util.h"
 #include "XmlDom.h"
+#include "util/Util.h"
 
 #include "test/Common.h"
 
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp
index 314c1e8..30c6091 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp
@@ -678,8 +678,6 @@
             // fallthrough
         case ResourceType::kAttr:
             return parseAttr(name, config, map);
-        case ResourceType::kIntegerArray:
-            // fallthrough
         case ResourceType::kArray:
             return parseArray(name, config, map);
         case ResourceType::kStyleable:
diff --git a/tools/aapt2/util/StringPiece.h b/tools/aapt2/util/StringPiece.h
index 8cbdeae..31deb45 100644
--- a/tools/aapt2/util/StringPiece.h
+++ b/tools/aapt2/util/StringPiece.h
@@ -36,6 +36,7 @@
 class BasicStringPiece {
 public:
     using const_iterator = const TChar*;
+    using difference_type = size_t;
 
     BasicStringPiece();
     BasicStringPiece(const BasicStringPiece<TChar>& str);