Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7cdccf9f authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "AAPT2: Split APK by ABI."

parents 68f25c19 5766943f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -85,6 +85,7 @@ cc_library_host_static {
        "compile/Pseudolocalizer.cpp",
        "compile/XmlIdCollector.cpp",
        "configuration/ConfigurationParser.cpp",
        "filter/AbiFilter.cpp",
        "filter/ConfigFilter.cpp",
        "flatten/Archive.cpp",
        "flatten/TableFlattener.cpp",
+13 −0
Original line number Diff line number Diff line
@@ -57,6 +57,12 @@ std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(IAaptContext* context,

bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options,
                               IArchiveWriter* writer) {
  FilterChain empty;
  return WriteToArchive(context, options, &empty, writer);
}

bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options,
                               FilterChain* filters, IArchiveWriter* writer) {
  std::set<std::string> referenced_resources;
  // List the files being referenced in the resource table.
  for (auto& pkg : table_->packages) {
@@ -89,6 +95,13 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOption
      continue;
    }

    if (!filters->Keep(path)) {
      if (context->IsVerbose()) {
        context->GetDiagnostics()->Note(DiagMessage() << "Filtered '" << path << "' from APK.");
      }
      continue;
    }

    // The resource table needs to be re-serialized since it might have changed.
    if (path == "resources.arsc") {
      BigBuffer buffer(4096);
+9 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include "androidfw/StringPiece.h"

#include "ResourceTable.h"
#include "filter/Filter.h"
#include "flatten/Archive.h"
#include "flatten/TableFlattener.h"
#include "io/ZipArchive.h"
@@ -49,6 +50,14 @@ class LoadedApk {
  bool WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options,
                      IArchiveWriter* writer);

  /**
   * Writes the APK on disk at the given path, while also removing the resource
   * files that are not referenced in the resource table. The provided filter
   * chain is applied to each entry in the APK file.
   */
  bool WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options,
                      FilterChain* filters, IArchiveWriter* writer);

  static std::unique_ptr<LoadedApk> LoadApkFromPath(IAaptContext* context,
                                                    const android::StringPiece& path);

+83 −9
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include <memory>
#include <vector>

#include "android-base/stringprintf.h"
#include "androidfw/StringPiece.h"

#include "Diagnostics.h"
@@ -26,6 +27,8 @@
#include "SdkConstants.h"
#include "ValueVisitor.h"
#include "cmd/Util.h"
#include "configuration/ConfigurationParser.h"
#include "filter/AbiFilter.h"
#include "flatten/TableFlattener.h"
#include "flatten/XmlFlattener.h"
#include "io/BigBufferInputStream.h"
@@ -33,14 +36,21 @@
#include "optimize/ResourceDeduper.h"
#include "optimize/VersionCollapser.h"
#include "split/TableSplitter.h"
#include "util/Files.h"

using android::StringPiece;
using ::aapt::configuration::Abi;
using ::aapt::configuration::Artifact;
using ::aapt::configuration::Configuration;
using ::android::StringPiece;
using ::android::base::StringPrintf;

namespace aapt {

struct OptimizeOptions {
  // Path to the output APK.
  std::string output_path;
  Maybe<std::string> output_path;
  // Path to the output APK directory for splits.
  Maybe<std::string> output_dir;

  // Details of the app extracted from the AndroidManifest.xml
  AppInfo app_info;
@@ -55,6 +65,9 @@ struct OptimizeOptions {
  std::vector<SplitConstraints> split_constraints;

  TableFlattenerOptions table_flattener_options;

  // TODO: Come up with a better name for the Configuration struct.
  Maybe<Configuration> configuration;
};

class OptimizeContext : public IAaptContext {
@@ -175,11 +188,53 @@ class OptimizeCommand {
      ++split_constraints_iter;
    }

    if (options_.configuration && options_.output_dir) {
      Configuration& config = options_.configuration.value();

      // For now, just write out the stripped APK since ABI splitting doesn't modify anything else.
      for (const Artifact& artifact : config.artifacts) {
        if (artifact.abi_group) {
          const std::string& group = artifact.abi_group.value();

          auto abi_group = config.abi_groups.find(group);
          // TODO: Remove validation when configuration parser ensures referential integrity.
          if (abi_group == config.abi_groups.end()) {
            context_->GetDiagnostics()->Note(
                DiagMessage() << "could not find referenced ABI group '" << group << "'");
            return 1;
          }
          FilterChain filters;
          filters.AddFilter(AbiFilter::FromAbiList(abi_group->second));

          const std::string& path = apk->GetSource().path;
          const StringPiece ext = file::GetExtension(path);
          const std::string name = path.substr(0, path.rfind(ext.to_string()));

          // Name is hard coded for now since only one split dimension is supported.
          // TODO: Incorporate name generation into the configuration objects.
          const std::string file_name =
              StringPrintf("%s.%s%s", name.c_str(), group.c_str(), ext.data());
          std::string out = options_.output_dir.value();
          file::AppendPath(&out, file_name);

          std::unique_ptr<IArchiveWriter> writer =
              CreateZipFileArchiveWriter(context_->GetDiagnostics(), out);

          if (!apk->WriteToArchive(context_, options_.table_flattener_options, &filters,
                                   writer.get())) {
            return 1;
          }
        }
      }
    }

    if (options_.output_path) {
      std::unique_ptr<IArchiveWriter> writer =
        CreateZipFileArchiveWriter(context_->GetDiagnostics(), options_.output_path);
          CreateZipFileArchiveWriter(context_->GetDiagnostics(), options_.output_path.value());
      if (!apk->WriteToArchive(context_, options_.table_flattener_options, writer.get())) {
        return 1;
      }
    }

    return 0;
  }
@@ -293,13 +348,16 @@ bool ExtractAppDataFromManifest(OptimizeContext* context, LoadedApk* apk,
int Optimize(const std::vector<StringPiece>& args) {
  OptimizeContext context;
  OptimizeOptions options;
  Maybe<std::string> config_path;
  Maybe<std::string> target_densities;
  std::vector<std::string> configs;
  std::vector<std::string> split_args;
  bool verbose = false;
  Flags flags =
      Flags()
          .RequiredFlag("-o", "Path to the output APK.", &options.output_path)
          .OptionalFlag("-o", "Path to the output APK.", &options.output_path)
          .OptionalFlag("-d", "Path to the output directory (for splits).", &options.output_dir)
          .OptionalFlag("-x", "Path to XML configuration file.", &config_path)
          .OptionalFlag(
              "--target-densities",
              "Comma separated list of the screen densities that the APK will be optimized for.\n"
@@ -369,6 +427,22 @@ int Optimize(const std::vector<StringPiece>& args) {
    }
  }

  if (config_path) {
    if (!options.output_dir) {
      context.GetDiagnostics()->Error(
          DiagMessage() << "Output directory is required when using a configuration file");
      return 1;
    }
    std::string& path = config_path.value();
    Maybe<ConfigurationParser> for_path = ConfigurationParser::ForPath(path);
    if (for_path) {
      options.configuration = for_path.value().WithDiagnostics(context.GetDiagnostics()).Parse();
    } else {
      context.GetDiagnostics()->Error(DiagMessage() << "Could not parse config file " << path);
      return 1;
    }
  }

  if (!ExtractAppDataFromManifest(&context, apk.get(), &options)) {
    return 1;
  }
+21 −11
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@

#include <algorithm>
#include <functional>
#include <map>
#include <memory>
#include <utility>

@@ -56,15 +57,15 @@ using ::aapt::xml::XmlActionExecutorPolicy;
using ::aapt::xml::XmlNodeAction;
using ::android::base::ReadFileToString;

const std::unordered_map<std::string, Abi> kAbiMap = {
    {"armeabi", Abi::kArmeV6},
    {"armeabi-v7a", Abi::kArmV7a},
    {"arm64-v8a", Abi::kArm64V8a},
    {"x86", Abi::kX86},
    {"x86_64", Abi::kX86_64},
    {"mips", Abi::kMips},
    {"mips64", Abi::kMips64},
    {"universal", Abi::kUniversal},
const std::unordered_map<std::string, Abi> kStringToAbiMap = {
    {"armeabi", Abi::kArmeV6}, {"armeabi-v7a", Abi::kArmV7a},  {"arm64-v8a", Abi::kArm64V8a},
    {"x86", Abi::kX86},        {"x86_64", Abi::kX86_64},       {"mips", Abi::kMips},
    {"mips64", Abi::kMips64},  {"universal", Abi::kUniversal},
};
const std::map<Abi, std::string> kAbiToStringMap = {
    {Abi::kArmeV6, "armeabi"}, {Abi::kArmV7a, "armeabi-v7a"},  {Abi::kArm64V8a, "arm64-v8a"},
    {Abi::kX86, "x86"},        {Abi::kX86_64, "x86_64"},       {Abi::kMips, "mips"},
    {Abi::kMips64, "mips64"},  {Abi::kUniversal, "universal"},
};

constexpr const char* kAaptXmlNs = "http://schemas.android.com/tools/aapt";
@@ -102,7 +103,13 @@ class NamespaceVisitor : public xml::Visitor {

}  // namespace

namespace configuration {

const std::string& AbiToString(Abi abi) {
  return kAbiToStringMap.find(abi)->second;
}

}  // namespace configuration

/** Returns a ConfigurationParser for the file located at the provided path. */
Maybe<ConfigurationParser> ConfigurationParser::ForPath(const std::string& path) {
@@ -175,6 +182,9 @@ Maybe<Configuration> ConfigurationParser::Parse() {
    return {};
  }

  // TODO: Validate all references in the configuration are valid. It should be safe to assume from
  // this point on that any references from one section to another will be present.

  return {config};
}

@@ -201,7 +211,7 @@ ConfigurationParser::ActionHandler ConfigurationParser::artifact_handler_ =
              DiagMessage() << "Unknown artifact attribute: " << attr.name << " = " << attr.value);
        }
      }
      config->artifacts[artifact.name] = artifact;
      config->artifacts.push_back(artifact);
      return true;
    };

@@ -236,7 +246,7 @@ ConfigurationParser::ActionHandler ConfigurationParser::abi_group_handler_ =
          for (auto& node : child->children) {
            xml::Text* t;
            if ((t = NodeCast<xml::Text>(node.get())) != nullptr) {
              group.push_back(kAbiMap.at(TrimWhitespace(t->text).to_string()));
              group.push_back(kStringToAbiMap.at(TrimWhitespace(t->text).to_string()));
              break;
            }
          }
Loading