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

Commit f267a404 authored by Pierre Lecesne's avatar Pierre Lecesne
Browse files

AAPT2: Use manifest parsing to determine format of APK.

This makes it possible to load APKs that don't have a resource table.

Bug: 69355482
Test: Manual
Change-Id: I8471dbe068e836b4beea9e6934d18dd16b70ef02
parent 031a2f1a
Loading
Loading
Loading
Loading
+46 −6
Original line number Diff line number Diff line
@@ -43,14 +43,17 @@ std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(const StringPiece& path, I
    return {};
  }

  if (apk->FindFile("resources.arsc") != nullptr) {
  ApkFormat apkFormat = DetermineApkFormat(apk.get());
  switch (apkFormat) {
    case ApkFormat::kBinary:
      return LoadBinaryApkFromFileCollection(source, std::move(apk), diag);
  } else if (apk->FindFile("resources.pb") != nullptr) {
    case ApkFormat::kProto:
      return LoadProtoApkFromFileCollection(source, std::move(apk), diag);
  }
  diag->Error(DiagMessage(path) << "no resource table found");
    default:
      diag->Error(DiagMessage(path) << "could not identify format of APK");
      return {};
  }
}

std::unique_ptr<LoadedApk> LoadedApk::LoadProtoApkFromFileCollection(
    const Source& source, unique_ptr<io::IFileCollection> collection, IDiagnostics* diag) {
@@ -243,4 +246,41 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, ResourceTable* split_table
  return true;
}

ApkFormat LoadedApk::DetermineApkFormat(io::IFileCollection* apk) {
  if (apk->FindFile("resources.arsc") != nullptr) {
    return ApkFormat::kBinary;
  } else if (apk->FindFile("resources.pb") != nullptr) {
    return ApkFormat::kProto;
  } else {
    // If the resource table is not present, attempt to read the manifest.
    io::IFile* manifest_file = apk->FindFile(kAndroidManifestPath);
    if (manifest_file == nullptr) {
      return ApkFormat::kUnknown;
    }

    // First try in proto format.
    std::unique_ptr<io::InputStream> manifest_in = manifest_file->OpenInputStream();
    if (manifest_in != nullptr) {
      pb::XmlNode pb_node;
      io::ZeroCopyInputAdaptor manifest_adaptor(manifest_in.get());
      if (pb_node.ParseFromZeroCopyStream(&manifest_adaptor)) {
        return ApkFormat::kProto;
      }
    }

    // If it didn't work, try in binary format.
    std::unique_ptr<io::IData> manifest_data = manifest_file->OpenAsData();
    if (manifest_data != nullptr) {
      std::string error;
      std::unique_ptr<xml::XmlResource> manifest =
          xml::Inflate(manifest_data->data(), manifest_data->size(), &error);
      if (manifest != nullptr) {
        return ApkFormat::kBinary;
      }
    }

    return ApkFormat::kUnknown;
  }
}

}  // namespace aapt
+8 −0
Original line number Diff line number Diff line
@@ -33,6 +33,12 @@ constexpr static const char kApkResourceTablePath[] = "resources.arsc";
constexpr static const char kProtoResourceTablePath[] = "resources.pb";
constexpr static const char kAndroidManifestPath[] = "AndroidManifest.xml";

enum ApkFormat {
  kUnknown,
  kBinary,
  kProto,
};

// Info about an APK loaded in memory.
class LoadedApk {
 public:
@@ -104,6 +110,8 @@ class LoadedApk {
  std::unique_ptr<io::IFileCollection> apk_;
  std::unique_ptr<ResourceTable> table_;
  std::unique_ptr<xml::XmlResource> manifest_;

  static ApkFormat DetermineApkFormat(io::IFileCollection* apk);
};

}  // namespace aapt