AssetManager2: Implement IDMAP support
This enables RRO (runtime resource overlays) with AssetManager2
Test: make libandroidfw_tests
Test: out/host/<platform>/nativetest64/libandroidfw_tests/libandroidfw_tests --testdata=frameworks/base/libs/androidfw/tests/data
Change-Id: Id8079104faefbfaa3f4017d8f7ee1a8968f151a2
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 0e577d1..158df13 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -20,64 +20,126 @@
#include <algorithm>
+#include "android-base/errors.h"
+#include "android-base/file.h"
#include "android-base/logging.h"
+#include "android-base/unique_fd.h"
+#include "android-base/utf8.h"
+#include "utils/Compat.h"
#include "utils/FileMap.h"
#include "utils/Trace.h"
#include "ziparchive/zip_archive.h"
#include "androidfw/Asset.h"
+#include "androidfw/Idmap.h"
+#include "androidfw/ResourceTypes.h"
#include "androidfw/Util.h"
namespace android {
-ApkAssets::ApkAssets() : zip_handle_(nullptr, ::CloseArchive) {}
+using base::SystemErrorCodeToString;
+using base::unique_fd;
+
+static const std::string kResourcesArsc("resources.arsc");
+
+ApkAssets::ApkAssets(void* unmanaged_handle, const std::string& path)
+ : zip_handle_(unmanaged_handle, ::CloseArchive), path_(path) {
+}
std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system) {
- return ApkAssets::LoadImpl(path, system, false /*load_as_shared_library*/);
+ return ApkAssets::LoadImpl(path, nullptr, nullptr, system, false /*load_as_shared_library*/);
}
std::unique_ptr<const ApkAssets> ApkAssets::LoadAsSharedLibrary(const std::string& path,
bool system) {
- return ApkAssets::LoadImpl(path, system, true /*load_as_shared_library*/);
+ return ApkAssets::LoadImpl(path, nullptr, nullptr, system, true /*load_as_shared_library*/);
}
-std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(const std::string& path, bool system,
- bool load_as_shared_library) {
+std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
+ bool system) {
+ std::unique_ptr<Asset> idmap_asset = CreateAssetFromFile(idmap_path);
+ if (idmap_asset == nullptr) {
+ return {};
+ }
+
+ const StringPiece idmap_data(
+ reinterpret_cast<const char*>(idmap_asset->getBuffer(true /*wordAligned*/)),
+ static_cast<size_t>(idmap_asset->getLength()));
+ std::unique_ptr<const LoadedIdmap> loaded_idmap = LoadedIdmap::Load(idmap_data);
+ if (loaded_idmap == nullptr) {
+ LOG(ERROR) << "failed to load IDMAP " << idmap_path;
+ return {};
+ }
+ return LoadImpl(loaded_idmap->OverlayApkPath(), std::move(idmap_asset), std::move(loaded_idmap),
+ system, false /*load_as_shared_library*/);
+}
+
+std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) {
+ unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
+ if (fd == -1) {
+ LOG(ERROR) << "Failed to open file '" << path << "': " << SystemErrorCodeToString(errno);
+ return {};
+ }
+
+ const off64_t file_len = lseek64(fd, 0, SEEK_END);
+ if (file_len < 0) {
+ LOG(ERROR) << "Failed to get size of file '" << path << "': " << SystemErrorCodeToString(errno);
+ return {};
+ }
+
+ std::unique_ptr<FileMap> file_map = util::make_unique<FileMap>();
+ if (!file_map->create(path.c_str(), fd, 0, static_cast<size_t>(file_len), true /*readOnly*/)) {
+ LOG(ERROR) << "Failed to mmap file '" << path << "': " << SystemErrorCodeToString(errno);
+ return {};
+ }
+ return Asset::createFromUncompressedMap(std::move(file_map), Asset::AccessMode::ACCESS_RANDOM);
+}
+
+std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
+ const std::string& path, std::unique_ptr<Asset> idmap_asset,
+ std::unique_ptr<const LoadedIdmap> loaded_idmap, bool system, bool load_as_shared_library) {
ATRACE_CALL();
::ZipArchiveHandle unmanaged_handle;
int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle);
if (result != 0) {
- LOG(ERROR) << ::ErrorCodeString(result);
+ LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result);
return {};
}
// Wrap the handle in a unique_ptr so it gets automatically closed.
- std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets());
- loaded_apk->zip_handle_.reset(unmanaged_handle);
+ std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(unmanaged_handle, path));
- ::ZipString entry_name("resources.arsc");
+ // Find the resource table.
+ ::ZipString entry_name(kResourcesArsc.c_str());
::ZipEntry entry;
result = ::FindEntry(loaded_apk->zip_handle_.get(), entry_name, &entry);
if (result != 0) {
- LOG(ERROR) << ::ErrorCodeString(result);
- return {};
+ // There is no resources.arsc, so create an empty LoadedArsc and return.
+ loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
+ return std::move(loaded_apk);
}
if (entry.method == kCompressDeflated) {
- LOG(WARNING) << "resources.arsc is compressed.";
+ LOG(WARNING) << kResourcesArsc << " in APK '" << path << "' is compressed.";
}
- loaded_apk->path_ = path;
- loaded_apk->resources_asset_ =
- loaded_apk->Open("resources.arsc", Asset::AccessMode::ACCESS_BUFFER);
+ // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open.
+ loaded_apk->resources_asset_ = loaded_apk->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER);
if (loaded_apk->resources_asset_ == nullptr) {
+ LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << path << "'.";
return {};
}
+ // Must retain ownership of the IDMAP Asset so that all pointers to its mmapped data remain valid.
+ loaded_apk->idmap_asset_ = std::move(idmap_asset);
+
+ const StringPiece data(
+ reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
+ loaded_apk->resources_asset_->getLength());
loaded_apk->loaded_arsc_ =
- LoadedArsc::Load(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/),
- loaded_apk->resources_asset_->getLength(), system, load_as_shared_library);
+ LoadedArsc::Load(data, loaded_idmap.get(), system, load_as_shared_library);
if (loaded_apk->loaded_arsc_ == nullptr) {
+ LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'.";
return {};
}
@@ -93,7 +155,6 @@
::ZipEntry entry;
int32_t result = ::FindEntry(zip_handle_.get(), name, &entry);
if (result != 0) {
- LOG(ERROR) << "No entry '" << path << "' found in APK '" << path_ << "'";
return {};
}