Split ManifestExtractor to Extract and Print methods.

Bug: b/228950123
Test: Dump_test.cpp
Change-Id: I040224f4997d2904e899211898e59e9fc4d58f82
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 2c833e7..64a9441 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -17,17 +17,19 @@
 #include "DumpManifest.h"
 
 #include <algorithm>
+#include <memory>
+#include <set>
+#include <vector>
 
 #include "LoadedApk.h"
 #include "SdkConstants.h"
 #include "ValueVisitor.h"
+#include "androidfw/ConfigDescription.h"
 #include "io/File.h"
 #include "io/FileStream.h"
 #include "process/IResourceTableConsumer.h"
 #include "xml/XmlDom.h"
 
-#include "androidfw/ConfigDescription.h"
-
 using ::android::base::StringPrintf;
 using ::android::ConfigDescription;
 
@@ -113,6 +115,9 @@
 }
 
 class CommonFeatureGroup;
+class Architectures;
+class SupportsScreen;
+class FeatureGroup;
 
 class ManifestExtractor {
  public:
@@ -338,7 +343,8 @@
     return config;
   }
 
-  bool Dump(text::Printer* printer, IDiagnostics* diag);
+  bool Extract(IDiagnostics* diag);
+  bool Dump(text::Printer* printer);
 
   /** Recursively visit the xml element tree and return a processed badging element tree. */
   std::unique_ptr<Element> Visit(xml::Element* element);
@@ -354,7 +360,7 @@
    * Retrieves the default feature group that features are added into when <uses-feature>
    * are not in a <feature-group> element.
    **/
-  CommonFeatureGroup* GetCommonFeatureGroup() {
+  CommonFeatureGroup* common_feature_group() {
     return commonFeatureGroup_.get();
   }
 
@@ -387,11 +393,22 @@
   DumpManifestOptions& options_;
 
  private:
+  std::unique_ptr<xml::XmlResource> doc_;
   std::unique_ptr<CommonFeatureGroup> commonFeatureGroup_ = util::make_unique<CommonFeatureGroup>();
   std::map<std::string, ConfigDescription> locales_;
   std::map<uint16_t, ConfigDescription> densities_;
   std::vector<Element*> parent_stack_;
   int32_t target_sdk_ = 0;
+
+  std::unique_ptr<ManifestExtractor::Element> root_element_;
+  std::vector<std::unique_ptr<ManifestExtractor::Element>> implied_permissions_;
+  std::set<std::string> components_;
+  std::vector<FeatureGroup*> feature_groups_;
+  bool other_activities_ = false;
+  bool other_receivers_ = false;
+  bool other_services_ = false;
+  std::unique_ptr<Architectures> architectures_ = util::make_unique<Architectures>();
+  const SupportsScreen* supports_screen_;
 };
 
 template<typename T> T* ElementCast(ManifestExtractor::Element* element);
@@ -427,6 +444,7 @@
 class Manifest : public ManifestExtractor::Element {
  public:
   Manifest() = default;
+  bool only_package_name;
   std::string package;
   int32_t versionCode;
   std::string versionName;
@@ -463,6 +481,14 @@
   }
 
   void Print(text::Printer* printer) override {
+    if (only_package_name) {
+      printer->Println(StringPrintf("package: %s", package.data()));
+    } else {
+      PrintFull(printer);
+    }
+  }
+
+  void PrintFull(text::Printer* printer) {
     printer->Print(StringPrintf("package: name='%s' ", package.data()));
     printer->Print(StringPrintf("versionCode='%s' ",
                                (versionCode > 0) ? std::to_string(versionCode).data() : ""));
@@ -733,7 +759,7 @@
     }
   }
 
-  void PrintScreens(text::Printer* printer, int32_t target_sdk) {
+  void PrintScreens(text::Printer* printer, int32_t target_sdk) const {
     int32_t small_screen_temp = small_screen;
     int32_t normal_screen_temp  = normal_screen;
     int32_t large_screen_temp  = large_screen;
@@ -1050,7 +1076,7 @@
     // common feature group
     FeatureGroup* feature_group = ElementCast<FeatureGroup>(extractor()->parent_stack()[0]);
     if (!feature_group) {
-      feature_group = extractor()->GetCommonFeatureGroup();
+      feature_group = extractor()->common_feature_group();
     } else {
       // All features in side of <feature-group> elements are required.
       required = true;
@@ -1068,12 +1094,14 @@
 class UsesPermission : public ManifestExtractor::Element {
  public:
   UsesPermission() = default;
+  bool implied;
   std::string name;
   std::vector<std::string> requiredFeatures;
   std::vector<std::string> requiredNotFeatures;
   int32_t required = true;
   int32_t maxSdkVersion = -1;
   int32_t usesPermissionFlags = 0;
+  std::string impliedReason;
 
   void Extract(xml::Element* element) override {
     name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
@@ -1094,7 +1122,7 @@
         FindAttribute(element, USES_PERMISSION_FLAGS_ATTR), 0);
 
     if (!name.empty()) {
-      CommonFeatureGroup* common = extractor()->GetCommonFeatureGroup();
+      CommonFeatureGroup* common = extractor()->common_feature_group();
       common->addImpliedFeaturesForPermission(extractor()->target_sdk(), name, false);
     }
   }
@@ -1126,17 +1154,16 @@
         printer->Print("\n");
       }
     }
-  }
-
-  void PrintImplied(text::Printer* printer, const std::string& reason) {
-    printer->Print(StringPrintf("uses-implied-permission: name='%s'", name.data()));
-    if (maxSdkVersion >= 0) {
-      printer->Print(StringPrintf(" maxSdkVersion='%d'", maxSdkVersion));
+    if (implied) {
+      printer->Print(StringPrintf("uses-implied-permission: name='%s'", name.data()));
+      if (maxSdkVersion >= 0) {
+        printer->Print(StringPrintf(" maxSdkVersion='%d'", maxSdkVersion));
+      }
+      if ((usesPermissionFlags & kNeverForLocation) != 0) {
+        printer->Print(StringPrintf(" usesPermissionFlags='neverForLocation'"));
+      }
+      printer->Print(StringPrintf(" reason='%s'\n", impliedReason.data()));
     }
-    if ((usesPermissionFlags & kNeverForLocation) != 0) {
-      printer->Print(StringPrintf(" usesPermissionFlags='neverForLocation'"));
-    }
-    printer->Print(StringPrintf(" reason='%s'\n", reason.data()));
   }
 };
 
@@ -1184,7 +1211,7 @@
     maxSdkVersion = GetAttributeInteger(FindAttribute(element, MAX_SDK_VERSION_ATTR));
 
     if (name) {
-      CommonFeatureGroup* common = extractor()->GetCommonFeatureGroup();
+      CommonFeatureGroup* common = extractor()->common_feature_group();
       common->addImpliedFeaturesForPermission(extractor()->target_sdk(), *name, true);
     }
   }
@@ -1256,7 +1283,7 @@
 
     auto orientation = GetAttributeInteger(FindAttribute(element, SCREEN_ORIENTATION_ATTR));
     if (orientation) {
-      CommonFeatureGroup* common = extractor()->GetCommonFeatureGroup();
+      CommonFeatureGroup* common = extractor()->common_feature_group();
       int orien = *orientation;
       if (orien == 0 || orien == 6 || orien == 8) {
         // Requests landscape, sensorLandscape, or reverseLandscape.
@@ -1962,6 +1989,29 @@
   }
 };
 
+class Architectures {
+ public:
+  std::set<std::string> architectures;
+  std::set<std::string> alt_architectures;
+
+  void Print(text::Printer* printer) {
+    if (!architectures.empty()) {
+      printer->Print("native-code:");
+      for (auto& arch : architectures) {
+        printer->Print(StringPrintf(" '%s'", arch.data()));
+      }
+      printer->Print("\n");
+    }
+    if (!alt_architectures.empty()) {
+      printer->Print("alt-native-code:");
+      for (auto& arch : alt_architectures) {
+        printer->Print(StringPrintf(" '%s'", arch.data()));
+      }
+      printer->Print("\n");
+    }
+  }
+};
+
 /** Recursively prints the extracted badging element. */
 static void Print(ManifestExtractor::Element* el, text::Printer* printer) {
   el->Print(printer);
@@ -1970,15 +2020,15 @@
   }
 }
 
-bool ManifestExtractor::Dump(text::Printer* printer, IDiagnostics* diag) {
+bool ManifestExtractor::Extract(IDiagnostics* diag) {
   // Load the manifest
-  std::unique_ptr<xml::XmlResource> doc = apk_->LoadXml("AndroidManifest.xml", diag);
-  if (doc == nullptr) {
+  doc_ = apk_->LoadXml("AndroidManifest.xml", diag);
+  if (doc_ == nullptr) {
     diag->Error(DiagMessage() << "failed to find AndroidManifest.xml");
     return false;
   }
 
-  xml::Element* element = doc->root.get();
+  xml::Element* element = doc_->root.get();
   if (element->name != "manifest") {
     diag->Error(DiagMessage() << "manifest does not start with <manifest> tag");
     return false;
@@ -1987,10 +2037,11 @@
   // Print only the <uses-permission>, <uses-permission-sdk23>, and <permission> elements if
   // printing only permission elements is requested
   if (options_.only_permissions) {
-    std::unique_ptr<ManifestExtractor::Element> manifest_element =
-        ManifestExtractor::Element::Inflate(this, element);
+    root_element_ = ManifestExtractor::Element::Inflate(this, element);
 
-    if (auto manifest = ElementCast<Manifest>(manifest_element.get())) {
+    if (auto manifest = ElementCast<Manifest>(root_element_.get())) {
+      manifest->only_package_name = true;
+
       for (xml::Element* child : element->GetChildElements()) {
         if (child->name == "uses-permission" || child->name == "uses-permission-sdk-23"
             || child->name == "permission") {
@@ -1999,15 +2050,8 @@
           manifest->AddChild(permission_element);
         }
       }
-
-      printer->Print(StringPrintf("package: %s\n", manifest->package.data()));
-      ForEachChild(manifest, [&printer](ManifestExtractor::Element* el) -> void {
-        el->Print(printer);
-      });
-
       return true;
     }
-
     return false;
   }
 
@@ -2041,27 +2085,24 @@
   }
 
   // Extract badging information
-  auto root = Visit(element);
+  root_element_ = Visit(element);
 
   // Filter out all "uses-sdk" tags besides the very last tag. The android runtime only uses the
   // attribute values from the last defined tag.
   std::vector<UsesSdkBadging*> filtered_uses_sdk_tags;
-  for (const auto& child : root->children()) {
+  for (const auto& child : root_element_->children()) {
     if (auto uses_sdk = ElementCast<UsesSdkBadging>(child.get())) {
       filtered_uses_sdk_tags.emplace_back(uses_sdk);
     }
   }
   if (filtered_uses_sdk_tags.size() >= 2U) {
     filtered_uses_sdk_tags.pop_back();
-    root->Filter([&](const ManifestExtractor::Element* e) {
+    root_element_->Filter([&](const ManifestExtractor::Element* e) {
       return std::find(filtered_uses_sdk_tags.begin(), filtered_uses_sdk_tags.end(), e) !=
              filtered_uses_sdk_tags.end();
     });
   }
 
-  // Print the elements in order seen
-  Print(root.get(), printer);
-
   /** Recursively checks the extracted elements for the specified permission. **/
   auto FindPermission = [&](ManifestExtractor::Element* root,
                             const std::string& name) -> ManifestExtractor::Element* {
@@ -2073,30 +2114,30 @@
     });
   };
 
-  auto PrintPermission = [&printer](const std::string& name, const std::string& reason,
-                                    int32_t max_sdk_version) -> void {
+  auto AddImpliedPermission = [&](const std::string& name, const std::string& reason,
+                                  int32_t max_sdk_version) -> void {
     auto permission = util::make_unique<UsesPermission>();
     permission->name = name;
     permission->maxSdkVersion = max_sdk_version;
-    permission->Print(printer);
-    permission->PrintImplied(printer, reason);
+    permission->implied = true;
+    permission->impliedReason = reason;
+    implied_permissions_.push_back(std::move(permission));
   };
 
   // Implied permissions
   // Pre-1.6 implicitly granted permission compatibility logic
-  CommonFeatureGroup* common_feature_group = GetCommonFeatureGroup();
   bool insert_write_external = false;
   auto write_external_permission = ElementCast<UsesPermission>(
-      FindPermission(root.get(), "android.permission.WRITE_EXTERNAL_STORAGE"));
+      FindPermission(root_element_.get(), "android.permission.WRITE_EXTERNAL_STORAGE"));
 
   if (target_sdk() < SDK_DONUT) {
     if (!write_external_permission) {
-      PrintPermission("android.permission.WRITE_EXTERNAL_STORAGE", "targetSdkVersion < 4", -1);
+      AddImpliedPermission("android.permission.WRITE_EXTERNAL_STORAGE", "targetSdkVersion < 4", -1);
       insert_write_external = true;
     }
 
-    if (!FindPermission(root.get(), "android.permission.READ_PHONE_STATE")) {
-      PrintPermission("android.permission.READ_PHONE_STATE", "targetSdkVersion < 4", -1);
+    if (!FindPermission(root_element_.get(), "android.permission.READ_PHONE_STATE")) {
+      AddImpliedPermission("android.permission.READ_PHONE_STATE", "targetSdkVersion < 4", -1);
     }
   }
 
@@ -2104,62 +2145,60 @@
   // force them to always take READ_EXTERNAL_STORAGE as well.  We always
   // do this (regardless of target API version) because we can't have
   // an app with write permission but not read permission.
-  auto read_external = FindPermission(root.get(), "android.permission.READ_EXTERNAL_STORAGE");
+  auto read_external =
+      FindPermission(root_element_.get(), "android.permission.READ_EXTERNAL_STORAGE");
   if (!read_external && (insert_write_external || write_external_permission)) {
-    PrintPermission("android.permission.READ_EXTERNAL_STORAGE",
-                    "requested WRITE_EXTERNAL_STORAGE",
-                    (write_external_permission) ? write_external_permission->maxSdkVersion : -1);
+    AddImpliedPermission(
+        "android.permission.READ_EXTERNAL_STORAGE", "requested WRITE_EXTERNAL_STORAGE",
+        (write_external_permission) ? write_external_permission->maxSdkVersion : -1);
   }
 
   // Pre-JellyBean call log permission compatibility.
   if (target_sdk() < SDK_JELLY_BEAN) {
-    if (!FindPermission(root.get(), "android.permission.READ_CALL_LOG")
-        && FindPermission(root.get(), "android.permission.READ_CONTACTS")) {
-      PrintPermission("android.permission.READ_CALL_LOG",
-                      "targetSdkVersion < 16 and requested READ_CONTACTS", -1);
+    if (!FindPermission(root_element_.get(), "android.permission.READ_CALL_LOG") &&
+        FindPermission(root_element_.get(), "android.permission.READ_CONTACTS")) {
+      AddImpliedPermission("android.permission.READ_CALL_LOG",
+                           "targetSdkVersion < 16 and requested READ_CONTACTS", -1);
     }
 
-    if (!FindPermission(root.get(), "android.permission.WRITE_CALL_LOG")
-        && FindPermission(root.get(), "android.permission.WRITE_CONTACTS")) {
-      PrintPermission("android.permission.WRITE_CALL_LOG",
-                      "targetSdkVersion < 16 and requested WRITE_CONTACTS", -1);
+    if (!FindPermission(root_element_.get(), "android.permission.WRITE_CALL_LOG") &&
+        FindPermission(root_element_.get(), "android.permission.WRITE_CONTACTS")) {
+      AddImpliedPermission("android.permission.WRITE_CALL_LOG",
+                           "targetSdkVersion < 16 and requested WRITE_CONTACTS", -1);
     }
   }
 
   // If the app hasn't declared the touchscreen as a feature requirement (either
   // directly or implied, required or not), then the faketouch feature is implied.
-  if (!common_feature_group->HasFeature("android.hardware.touchscreen")) {
-    common_feature_group->addImpliedFeature("android.hardware.faketouch",
-                                            "default feature for all apps", false);
+  if (!common_feature_group()->HasFeature("android.hardware.touchscreen")) {
+    common_feature_group()->addImpliedFeature("android.hardware.faketouch",
+                                              "default feature for all apps", false);
   }
 
   // Only print the common feature group if no feature group is defined
   std::vector<FeatureGroup*> feature_groups;
-  ForEachChild(root.get(), [&feature_groups](ManifestExtractor::Element* el) -> void {
+  ForEachChild(root_element_.get(), [&feature_groups](ManifestExtractor::Element* el) -> void {
     if (auto feature_group = ElementCast<FeatureGroup>(el)) {
       feature_groups.push_back(feature_group);
     }
   });
 
   if (feature_groups.empty()) {
-    common_feature_group->PrintGroup(printer);
+    feature_groups_.push_back(common_feature_group());
   } else {
     // Merge the common feature group into the feature group
     for (auto& feature_group : feature_groups) {
-      feature_group->open_gles_version  = std::max(feature_group->open_gles_version,
-                                                   common_feature_group->open_gles_version);
-      feature_group->Merge(common_feature_group);
-      feature_group->PrintGroup(printer);
+      feature_group->Merge(common_feature_group());
+      feature_groups_.push_back(feature_group);
     }
   };
 
   // Collect the component types of the application
-  std::set<std::string> components;
-  ForEachChild(root.get(), [&components](ManifestExtractor::Element* el) -> void {
+  ForEachChild(root_element_.get(), [&](ManifestExtractor::Element* el) -> void {
     if (ElementCast<Action>(el)) {
       auto action = ElementCast<Action>(el);
       if (!action->component.empty()) {
-        components.insert(action->component);
+        components_.insert(action->component);
         return;
       }
     }
@@ -2167,15 +2206,14 @@
     if (ElementCast<Category>(el)) {
       auto category = ElementCast<Category>(el);
       if (!category->component.empty()) {
-        components.insert(category->component);
+        components_.insert(category->component);
         return;
       }
     }
   });
 
   // Check for the payment component
-  auto apk = apk_;
-  ForEachChild(root.get(), [&apk, &components, &diag](ManifestExtractor::Element* el) -> void {
+  ForEachChild(root_element_.get(), [this, &diag](ManifestExtractor::Element* el) -> void {
     if (auto service = ElementCast<Service>(el)) {
       auto host_apdu_action = ElementCast<Action>(FindElement(service,
         [&](ManifestExtractor::Element* el) -> bool {
@@ -2193,44 +2231,162 @@
            return false;
       }));
 
-      ForEachChild(service, [&apk, &components, &diag, &host_apdu_action,
-          &offhost_apdu_action](ManifestExtractor::Element* el) -> void {
-        if (auto meta_data = ElementCast<MetaData>(el)) {
-          if ((meta_data->name == "android.nfc.cardemulation.host_apdu_service" && host_apdu_action)
-              || (meta_data->name == "android.nfc.cardemulation.off_host_apdu_service"
-                  && offhost_apdu_action)) {
+      ForEachChild(service,
+                   [this, &diag, &host_apdu_action,
+                    &offhost_apdu_action](ManifestExtractor::Element* el) -> void {
+                     if (auto meta_data = ElementCast<MetaData>(el)) {
+                       if ((meta_data->name == "android.nfc.cardemulation.host_apdu_service" &&
+                            host_apdu_action) ||
+                           (meta_data->name == "android.nfc.cardemulation.off_host_apdu_service" &&
+                            offhost_apdu_action)) {
+                         // Attempt to load the resource file
+                         if (!meta_data->resource.empty()) {
+                           return;
+                         }
+                         auto resource = this->apk_->LoadXml(meta_data->resource, diag);
+                         if (!resource) {
+                           return;
+                         }
 
-            // Attempt to load the resource file
-            if (!meta_data->resource.empty()) {
-              return;
-            }
-            auto resource = apk->LoadXml(meta_data->resource, diag);
-            if (!resource) {
-              return;
-            }
-
-            // Look for the payment category on an <aid-group> element
-            auto& root = resource.get()->root;
-            if ((host_apdu_action && root->name == "host-apdu-service")
-                || (offhost_apdu_action && root->name == "offhost-apdu-service")) {
-
-              for (auto& child : root->GetChildElements()) {
-                if (child->name == "aid-group") {
-                  auto category = FindAttribute(child, CATEGORY_ATTR);
-                  if (category && category->value == "payment") {
-                    components.insert("payment");
-                    return;
-                  }
-                }
-              }
-            }
-          }
-        }
-      });
+                         // Look for the payment category on an <aid-group> element
+                         auto& root = resource.get()->root;
+                         if ((host_apdu_action && root->name == "host-apdu-service") ||
+                             (offhost_apdu_action && root->name == "offhost-apdu-service")) {
+                           for (auto& child : root->GetChildElements()) {
+                             if (child->name == "aid-group") {
+                               auto category = FindAttribute(child, CATEGORY_ATTR);
+                               if (category && category->value == "payment") {
+                                 this->components_.insert("payment");
+                                 return;
+                               }
+                             }
+                           }
+                         }
+                       }
+                     }
+                   });
     }
   });
 
+  // Print presence of activities, receivers, and services with no special components
+  FindElement(root_element_.get(), [&](ManifestExtractor::Element* el) -> bool {
+    if (auto activity = ElementCast<Activity>(el)) {
+      if (!activity->has_component_) {
+        other_activities_ = true;
+        return true;
+      }
+    }
+    return false;
+  });
+
+  FindElement(root_element_.get(), [&](ManifestExtractor::Element* el) -> bool {
+    if (auto receiver = ElementCast<Receiver>(el)) {
+      if (!receiver->has_component) {
+        other_receivers_ = true;
+        return true;
+      }
+    }
+    return false;
+  });
+
+  FindElement(root_element_.get(), [&](ManifestExtractor::Element* el) -> bool {
+    if (auto service = ElementCast<Service>(el)) {
+      if (!service->has_component) {
+        other_services_ = true;
+        return true;
+      }
+    }
+    return false;
+  });
+
+  // Gather the supported screens
+  const static SupportsScreen default_screens{};
+  SupportsScreen* screen = ElementCast<SupportsScreen>(
+      FindElement(root_element_.get(), [&](ManifestExtractor::Element* el) -> bool {
+        return ElementCast<SupportsScreen>(el) != nullptr;
+      }));
+  supports_screen_ = screen ? screen : &default_screens;
+
+  // Gather the supported architectures_ of the app
+  std::set<std::string> architectures_from_apk;
+  auto it = apk_->GetFileCollection()->Iterator();
+  while (it->HasNext()) {
+    auto file_path = it->Next()->GetSource().path;
+    size_t pos = file_path.find("lib/");
+    if (pos != std::string::npos) {
+      file_path = file_path.substr(pos + 4);
+      pos = file_path.find('/');
+      if (pos != std::string::npos) {
+        file_path = file_path.substr(0, pos);
+      }
+
+      architectures_from_apk.insert(file_path);
+    }
+  }
+
+  // Determine if the application has multiArch supports
+  auto has_multi_arch =
+      FindElement(root_element_.get(), [&](ManifestExtractor::Element* el) -> bool {
+        if (auto application = ElementCast<Application>(el)) {
+          return application->has_multi_arch;
+        }
+        return false;
+      });
+
+  bool output_alt_native_code = false;
+  // A multiArch package is one that contains 64-bit and
+  // 32-bit versions of native code and expects 3rd-party
+  // apps to load these native code libraries. Since most
+  // 64-bit systems also support 32-bit apps, the apps
+  // loading this multiArch package's code may be either
+  if (has_multi_arch) {
+    // If this is a multiArch package, report the 64-bit
+    // version only. Then as a separate entry, report the
+    // rest.
+    //
+    // If we report the 32-bit architecture, this APK will
+    // be installed on a 32-bit device, causing a large waste
+    // of bandwidth and disk space. This assumes that
+    // the developer of the multiArch package has also
+    // made a version that is 32-bit only.
+    const std::string kIntel64 = "x86_64";
+    const std::string kArm64 = "arm64-v8a";
+
+    auto arch = architectures_from_apk.find(kIntel64);
+    if (arch == architectures_from_apk.end()) {
+      arch = architectures_from_apk.find(kArm64);
+    }
+
+    if (arch != architectures_from_apk.end()) {
+      architectures_->architectures.insert(*arch);
+      architectures_from_apk.erase(arch);
+      output_alt_native_code = true;
+    }
+  }
+  for (auto& arch : architectures_from_apk) {
+    if (output_alt_native_code) {
+      architectures_->alt_architectures.insert(arch);
+    } else {
+      architectures_->architectures.insert(arch);
+    }
+  }
+  return true;
+}
+
+bool ManifestExtractor::Dump(text::Printer* printer) {
+  Print(root_element_.get(), printer);
+  if (options_.only_permissions) {
+    return true;
+  }
+
+  for (auto& implied_permission : implied_permissions_) {
+    implied_permission->Print(printer);
+  }
+  for (auto& feature_group : feature_groups_) {
+    feature_group->PrintGroup(printer);
+  }
   // Print the components types if they are present
+  auto& components = components_;
   auto PrintComponent = [&components, &printer](const std::string& component) -> void {
     if (components.find(component) != components.end()) {
       printer->Print(StringPrintf("provides-component:'%s'\n", component.data()));
@@ -2257,50 +2413,16 @@
     printer->Print("main\n");
   }
 
-  // Print presence of activities, recivers, and services with no special components
-  FindElement(root.get(), [&printer](ManifestExtractor::Element* el) -> bool {
-    if (auto activity = ElementCast<Activity>(el)) {
-      if (!activity->has_component_) {
-        printer->Print("other-activities\n");
-        return true;
-      }
-    }
-    return false;
-  });
-
-  FindElement(root.get(), [&printer](ManifestExtractor::Element* el) -> bool {
-    if (auto receiver = ElementCast<Receiver>(el)) {
-      if (!receiver->has_component) {
-        printer->Print("other-receivers\n");
-        return true;
-      }
-    }
-    return false;
-  });
-
-  FindElement(root.get(), [&printer](ManifestExtractor::Element* el) -> bool {
-    if (auto service = ElementCast<Service>(el)) {
-      if (!service->has_component) {
-        printer->Print("other-services\n");
-        return true;
-      }
-    }
-    return false;
-  });
-
-  // Print the supported screens
-  SupportsScreen* screen = ElementCast<SupportsScreen>(FindElement(root.get(),
-      [&](ManifestExtractor::Element* el) -> bool {
-    return ElementCast<SupportsScreen>(el) != nullptr;
-  }));
-
-  if (screen) {
-    screen->PrintScreens(printer, target_sdk_);
-  } else {
-    // Print the default supported screens
-    SupportsScreen default_screens;
-    default_screens.PrintScreens(printer, target_sdk_);
+  if (other_activities_) {
+    printer->Print("other-activities\n");
   }
+  if (other_receivers_) {
+    printer->Print("other-receivers\n");
+  }
+  if (other_services_) {
+    printer->Print("other-services\n");
+  }
+  supports_screen_->PrintScreens(printer, target_sdk_);
 
   // Print all the unique locales of the apk
   printer->Print("locales:");
@@ -2320,75 +2442,7 @@
   }
   printer->Print("\n");
 
-  // Print the supported architectures of the app
-  std::set<std::string> architectures;
-  auto it = apk_->GetFileCollection()->Iterator();
-  while (it->HasNext()) {
-    auto file_path = it->Next()->GetSource().path;
-
-
-    size_t pos = file_path.find("lib/");
-    if (pos != std::string::npos) {
-      file_path = file_path.substr(pos + 4);
-      pos = file_path.find('/');
-      if (pos != std::string::npos) {
-        file_path = file_path.substr(0, pos);
-      }
-
-      architectures.insert(file_path);
-    }
-  }
-
-  // Determine if the application has multiArch supports
-  auto has_multi_arch = FindElement(root.get(), [&](ManifestExtractor::Element* el) -> bool {
-    if (auto application = ElementCast<Application>(el)) {
-      return application->has_multi_arch;
-    }
-    return false;
-  });
-
-  bool output_alt_native_code = false;
-  // A multiArch package is one that contains 64-bit and
-  // 32-bit versions of native code and expects 3rd-party
-  // apps to load these native code libraries. Since most
-  // 64-bit systems also support 32-bit apps, the apps
-  // loading this multiArch package's code may be either
-  if (has_multi_arch) {
-    // If this is a multiArch package, report the 64-bit
-    // version only. Then as a separate entry, report the
-    // rest.
-    //
-    // If we report the 32-bit architecture, this APK will
-    // be installed on a 32-bit device, causing a large waste
-    // of bandwidth and disk space. This assumes that
-    // the developer of the multiArch package has also
-    // made a version that is 32-bit only.
-    const std::string kIntel64 = "x86_64";
-    const std::string kArm64 = "arm64-v8a";
-
-    auto arch = architectures.find(kIntel64);
-    if (arch == architectures.end()) {
-      arch = architectures.find(kArm64);
-    }
-
-    if (arch != architectures.end()) {
-      printer->Print(StringPrintf("native-code: '%s'\n", arch->data()));
-      architectures.erase(arch);
-      output_alt_native_code = true;
-    }
-  }
-
-  if (architectures.size() > 0) {
-    if (output_alt_native_code) {
-      printer->Print("alt-");
-    }
-    printer->Print("native-code:");
-    for (auto& arch : architectures) {
-      printer->Print(StringPrintf(" '%s'", arch.data()));
-    }
-    printer->Print("\n");
-  }
-
+  architectures_->Print(printer);
   return true;
 }
 
@@ -2531,7 +2585,10 @@
 int DumpManifest(LoadedApk* apk, DumpManifestOptions& options, text::Printer* printer,
                  IDiagnostics* diag) {
   ManifestExtractor extractor(apk, options);
-  return extractor.Dump(printer, diag) ? 0 : 1;
+  if (!extractor.Extract(diag)) {
+    return 0;
+  }
+  return extractor.Dump(printer) ? 0 : 1;
 }
 
 } // namespace aapt