Add fabricated RRO generation to libidmap2

Fabricated Runtime Resource Overlays are overlays that are generated
at runtime and are stored in the data/ partition.

The system can fabricate RROs at runtime to dynamically theme the
device. Idmaps can now be created from APK RROs and fabricated RROs.

Rather than operating on ApkAssets, libidmap2 now operates on abstract
resource "containers" that supply resource values. Target resource
containers implement methods needed to query overlayable and target
overlay information. Currently only APKs can be loaded as target
resource containers. Overlay resource containers implement methods to
supply the mapping of target resource to overlay value and other
overlay information.

The format of a fabricated RRO is as follows:
0x00 - 0x04 : fabricated overlay magic (always FRRO)
0x04 - 0x08 : file format version
0x08 - 0x0c : crc of version + proto data
0x0c - EOF  : proto fabricated overlay data

The magic is used to quickly detect if the file is a fabricated overlay.
The version is incremented whenever backwards incompatible changes are
made to the proto file format. Idmap must always be able to upgrade
fabricated overlays from previous versions to new versions, so all
previous versions must be checked into the tree.

Bug: 172471315
Test: libidmap2_tests && libandroidfw_tests
Change-Id: I4c9f29da278672e5695fb57d131a44c11a835180
This commit is contained in:
Ryan Mitchell
2021-01-08 13:34:28 -08:00
parent 5c8ba79373
commit 2ed8bfa7fd
49 changed files with 1903 additions and 1272 deletions

View File

@@ -51,13 +51,18 @@ cc_library {
static: {
enabled: false,
},
static_libs: [
"libidmap2_protos",
],
shared_libs: [
"libandroidfw",
"libbase",
"libcutils",
"libutils",
"libziparchive",
"libidmap2_policies",
"libprotobuf-cpp-lite",
"libutils",
"libz",
"libziparchive",
],
},
host: {
@@ -68,14 +73,29 @@ cc_library {
"libandroidfw",
"libbase",
"libcutils",
"libutils",
"libziparchive",
"libidmap2_policies",
"libidmap2_protos",
"libprotobuf-cpp-lite",
"libutils",
"libz",
"libziparchive",
],
},
},
}
cc_library {
name: "libidmap2_protos",
srcs: [
"libidmap2/proto/*.proto",
],
host_supported: true,
proto: {
type: "lite",
export_proto_headers: true,
},
}
cc_library {
name: "libidmap2_policies",
defaults: [
@@ -88,9 +108,6 @@ cc_library {
enabled: true,
},
android: {
static: {
enabled: false,
},
shared_libs: [
"libandroidfw",
],
@@ -119,6 +136,7 @@ cc_test {
srcs: [
"tests/BinaryStreamVisitorTests.cpp",
"tests/CommandLineOptionsTests.cpp",
"tests/FabricatedOverlayTests.cpp",
"tests/FileUtilsTests.cpp",
"tests/Idmap2BinaryTests.cpp",
"tests/IdmapTests.cpp",
@@ -130,20 +148,27 @@ cc_test {
"tests/ResourceUtilsTests.cpp",
"tests/ResultTests.cpp",
"tests/XmlParserTests.cpp",
"tests/ZipFileTests.cpp",
],
static_libs: ["libgmock"],
required: [
"idmap2",
],
static_libs: [
"libgmock",
"libidmap2_protos",
],
target: {
android: {
shared_libs: [
"libandroidfw",
"libbase",
"libidmap2",
"libidmap2_policies",
"liblog",
"libprotobuf-cpp-lite",
"libutils",
"libz",
"libz",
"libziparchive",
"libidmap2_policies",
],
},
host: {
@@ -152,10 +177,11 @@ cc_test {
"libbase",
"libcutils",
"libidmap2",
"libidmap2_policies",
"liblog",
"libprotobuf-cpp-lite",
"libutils",
"libziparchive",
"libidmap2_policies",
],
shared_libs: [
"libz",
@@ -189,6 +215,9 @@ cc_binary {
"idmap2/Lookup.cpp",
"idmap2/Main.cpp",
],
static_libs: [
"libidmap2_protos",
],
target: {
android: {
shared_libs: [
@@ -196,9 +225,11 @@ cc_binary {
"libbase",
"libcutils",
"libidmap2",
"libutils",
"libziparchive",
"libidmap2_policies",
"libprotobuf-cpp-lite",
"libutils",
"libz",
"libziparchive",
],
},
host: {
@@ -207,10 +238,11 @@ cc_binary {
"libbase",
"libcutils",
"libidmap2",
"libidmap2_policies",
"liblog",
"libprotobuf-cpp-lite",
"libutils",
"libziparchive",
"libidmap2_policies",
],
shared_libs: [
"libz",
@@ -236,11 +268,13 @@ cc_binary {
"libbinder",
"libcutils",
"libidmap2",
"libidmap2_policies",
"libprotobuf-cpp-lite",
"libutils",
"libziparchive",
"libidmap2_policies",
],
static_libs: [
"libidmap2_protos",
"libidmap2daidl",
],
init_rc: ["idmap2d/idmap2d.rc"],

View File

@@ -25,7 +25,9 @@
using android::idmap2::Error;
using android::idmap2::IdmapHeader;
using android::idmap2::OverlayResourceContainer;
using android::idmap2::Result;
using android::idmap2::TargetResourceContainer;
using android::idmap2::Unit;
Result<Unit> Verify(const std::string& idmap_path, const std::string& target_path,
@@ -39,11 +41,20 @@ Result<Unit> Verify(const std::string& idmap_path, const std::string& target_pat
return Error("failed to parse idmap header");
}
const auto header_ok = header->IsUpToDate(target_path, overlay_path, overlay_name,
fulfilled_policies, enforce_overlayable);
auto target = TargetResourceContainer::FromPath(target_path);
if (!target) {
return Error("failed to load target '%s'", target_path.c_str());
}
auto overlay = OverlayResourceContainer::FromPath(overlay_path);
if (!overlay) {
return Error("failed to load overlay '%s'", overlay_path.c_str());
}
const auto header_ok = header->IsUpToDate(**target, **overlay, overlay_name, fulfilled_policies,
enforce_overlayable);
if (!header_ok) {
return Error(header_ok.GetError(), "idmap not up to date");
}
return Unit{};
}

View File

@@ -20,7 +20,6 @@
#include <fstream>
#include <memory>
#include <ostream>
#include <string>
#include <vector>
#include "androidfw/ResourceTypes.h"
@@ -31,12 +30,13 @@
#include "idmap2/PolicyUtils.h"
#include "idmap2/SysTrace.h"
using android::ApkAssets;
using android::idmap2::BinaryStreamVisitor;
using android::idmap2::CommandLineOptions;
using android::idmap2::Error;
using android::idmap2::Idmap;
using android::idmap2::OverlayResourceContainer;
using android::idmap2::Result;
using android::idmap2::TargetResourceContainer;
using android::idmap2::Unit;
using android::idmap2::utils::kIdmapFilePermissionMask;
using android::idmap2::utils::PoliciesToBitmaskResult;
@@ -93,18 +93,18 @@ Result<Unit> Create(const std::vector<std::string>& args) {
fulfilled_policies |= PolicyFlags::PUBLIC;
}
const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
if (!target_apk) {
return Error("failed to load apk %s", target_apk_path.c_str());
const auto target = TargetResourceContainer::FromPath(target_apk_path);
if (!target) {
return Error("failed to load target '%s'", target_apk_path.c_str());
}
const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
if (!overlay_apk) {
return Error("failed to load apk %s", overlay_apk_path.c_str());
const auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
if (!overlay) {
return Error("failed to load apk overlay '%s'", overlay_apk_path.c_str());
}
const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, overlay_name,
fulfilled_policies, !ignore_overlayable);
const auto idmap = Idmap::FromContainers(**target, **overlay, overlay_name, fulfilled_policies,
!ignore_overlayable);
if (!idmap) {
return Error(idmap.GetError(), "failed to create idmap");
}
@@ -112,13 +112,14 @@ Result<Unit> Create(const std::vector<std::string>& args) {
umask(kIdmapFilePermissionMask);
std::ofstream fout(idmap_path);
if (fout.fail()) {
return Error("failed to open idmap path %s", idmap_path.c_str());
return Error("failed to open idmap path '%s'", idmap_path.c_str());
}
BinaryStreamVisitor visitor(fout);
(*idmap)->accept(&visitor);
fout.close();
if (fout.fail()) {
return Error("failed to write to idmap path %s", idmap_path.c_str());
return Error("failed to write to idmap path '%s'", idmap_path.c_str());
}
return Unit{};

View File

@@ -34,13 +34,14 @@
#include "idmap2/PolicyUtils.h"
#include "idmap2/SysTrace.h"
using android::ApkAssets;
using android::base::StringPrintf;
using android::idmap2::BinaryStreamVisitor;
using android::idmap2::CommandLineOptions;
using android::idmap2::Error;
using android::idmap2::Idmap;
using android::idmap2::OverlayResourceContainer;
using android::idmap2::Result;
using android::idmap2::TargetResourceContainer;
using android::idmap2::Unit;
using android::idmap2::utils::kIdmapCacheDir;
using android::idmap2::utils::kIdmapFilePermissionMask;
@@ -91,9 +92,9 @@ Result<Unit> CreateMultiple(const std::vector<std::string>& args) {
fulfilled_policies |= PolicyFlags::PUBLIC;
}
const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
if (!target_apk) {
return Error("failed to load apk %s", target_apk_path.c_str());
const auto target = TargetResourceContainer::FromPath(target_apk_path);
if (!target) {
return Error("failed to load target '%s'", target_apk_path.c_str());
}
std::vector<std::string> idmap_paths;
@@ -108,14 +109,14 @@ Result<Unit> CreateMultiple(const std::vector<std::string>& args) {
// TODO(b/175014391): Support multiple overlay tags in OverlayConfig
if (!Verify(idmap_path, target_apk_path, overlay_apk_path, "", fulfilled_policies,
!ignore_overlayable)) {
const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
if (!overlay_apk) {
const auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
if (!overlay) {
LOG(WARNING) << "failed to load apk " << overlay_apk_path.c_str();
continue;
}
const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, "", fulfilled_policies,
!ignore_overlayable);
const auto idmap =
Idmap::FromContainers(**target, **overlay, "", fulfilled_policies, !ignore_overlayable);
if (!idmap) {
LOG(WARNING) << "failed to create idmap";
continue;

View File

@@ -37,7 +37,6 @@
#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
#include "idmap2/XmlParser.h"
#include "idmap2/ZipFile.h"
#include "utils/String16.h"
#include "utils/String8.h"
@@ -52,10 +51,10 @@ using android::base::StringPrintf;
using android::idmap2::CommandLineOptions;
using android::idmap2::Error;
using android::idmap2::IdmapHeader;
using android::idmap2::OverlayResourceContainer;
using android::idmap2::ResourceId;
using android::idmap2::Result;
using android::idmap2::Unit;
using android::idmap2::utils::ExtractOverlayManifestInfo;
namespace {
@@ -195,12 +194,17 @@ Result<Unit> Lookup(const std::vector<std::string>& args) {
}
apk_assets.push_back(std::move(target_apk));
auto manifest_info = ExtractOverlayManifestInfo(idmap_header->GetOverlayPath(),
idmap_header->GetOverlayName());
auto overlay = OverlayResourceContainer::FromPath(idmap_header->GetOverlayPath());
if (!overlay) {
return overlay.GetError();
}
auto manifest_info = (*overlay)->FindOverlayInfo(idmap_header->GetOverlayName());
if (!manifest_info) {
return manifest_info.GetError();
}
target_package_name = manifest_info->target_package;
target_package_name = (*manifest_info).target_package;
} else if (target_path != idmap_header->GetTargetPath()) {
return Error("different target APKs (expected target APK %s but %s has target APK %s)",
target_path.c_str(), idmap_path.c_str(), idmap_header->GetTargetPath().c_str());

View File

@@ -35,17 +35,16 @@
#include "idmap2/Idmap.h"
#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
#include "idmap2/ZipFile.h"
#include "utils/String8.h"
using android::IPCThreadState;
using android::base::StringPrintf;
using android::binder::Status;
using android::idmap2::BinaryStreamVisitor;
using android::idmap2::GetPackageCrc;
using android::idmap2::Idmap;
using android::idmap2::IdmapHeader;
using android::idmap2::ZipFile;
using android::idmap2::OverlayResourceContainer;
using android::idmap2::TargetResourceContainer;
using android::idmap2::utils::kIdmapCacheDir;
using android::idmap2::utils::kIdmapFilePermissionMask;
using android::idmap2::utils::UidHasWriteAccessToPath;
@@ -68,40 +67,24 @@ Status error(const std::string& msg) {
PolicyBitmask ConvertAidlArgToPolicyBitmask(int32_t arg) {
return static_cast<PolicyBitmask>(arg);
}
Status GetCrc(const std::string& apk_path, uint32_t* out_crc) {
const auto zip = ZipFile::Open(apk_path);
if (!zip) {
return error(StringPrintf("failed to open apk %s", apk_path.c_str()));
}
const auto crc = GetPackageCrc(*zip);
if (!crc) {
return error(crc.GetErrorMessage());
}
*out_crc = *crc;
return ok();
}
} // namespace
namespace android::os {
Status Idmap2Service::getIdmapPath(const std::string& overlay_apk_path,
Status Idmap2Service::getIdmapPath(const std::string& overlay_path,
int32_t user_id ATTRIBUTE_UNUSED, std::string* _aidl_return) {
assert(_aidl_return);
SYSTRACE << "Idmap2Service::getIdmapPath " << overlay_apk_path;
*_aidl_return = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
SYSTRACE << "Idmap2Service::getIdmapPath " << overlay_path;
*_aidl_return = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_path);
return ok();
}
Status Idmap2Service::removeIdmap(const std::string& overlay_apk_path,
int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) {
Status Idmap2Service::removeIdmap(const std::string& overlay_path, int32_t user_id ATTRIBUTE_UNUSED,
bool* _aidl_return) {
assert(_aidl_return);
SYSTRACE << "Idmap2Service::removeIdmap " << overlay_apk_path;
SYSTRACE << "Idmap2Service::removeIdmap " << overlay_path;
const uid_t uid = IPCThreadState::self()->getCallingUid();
const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_path);
if (!UidHasWriteAccessToPath(uid, idmap_path)) {
*_aidl_return = false;
return error(base::StringPrintf("failed to unlink %s: calling uid %d lacks write access",
@@ -115,85 +98,79 @@ Status Idmap2Service::removeIdmap(const std::string& overlay_apk_path,
return ok();
}
Status Idmap2Service::verifyIdmap(const std::string& target_apk_path,
const std::string& overlay_apk_path, int32_t fulfilled_policies,
bool enforce_overlayable, int32_t user_id ATTRIBUTE_UNUSED,
bool* _aidl_return) {
SYSTRACE << "Idmap2Service::verifyIdmap " << overlay_apk_path;
Status Idmap2Service::verifyIdmap(const std::string& target_path, const std::string& overlay_path,
int32_t fulfilled_policies, bool enforce_overlayable,
int32_t user_id ATTRIBUTE_UNUSED, bool* _aidl_return) {
SYSTRACE << "Idmap2Service::verifyIdmap " << overlay_path;
assert(_aidl_return);
const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_path);
std::ifstream fin(idmap_path);
const std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(fin);
fin.close();
if (!header) {
*_aidl_return = false;
return error("failed to parse idmap header");
LOG(WARNING) << "failed to parse idmap header of '" << idmap_path << "'";
return ok();
}
uint32_t target_crc;
if (target_apk_path == kFrameworkPath && android_crc_) {
target_crc = *android_crc_;
} else {
auto target_crc_status = GetCrc(target_apk_path, &target_crc);
if (!target_crc_status.isOk()) {
*_aidl_return = false;
return target_crc_status;
}
// Loading the framework zip can take several milliseconds. Cache the crc of the framework
// resource APK to reduce repeated work during boot.
if (target_apk_path == kFrameworkPath) {
android_crc_ = target_crc;
}
}
uint32_t overlay_crc;
auto overlay_crc_status = GetCrc(overlay_apk_path, &overlay_crc);
if (!overlay_crc_status.isOk()) {
const auto target = GetTargetContainer(target_path);
if (!target) {
*_aidl_return = false;
return overlay_crc_status;
LOG(WARNING) << "failed to load target '" << target_path << "'";
return ok();
}
const auto overlay = OverlayResourceContainer::FromPath(overlay_path);
if (!overlay) {
*_aidl_return = false;
LOG(WARNING) << "failed to load overlay '" << overlay_path << "'";
return ok();
}
// TODO(162841629): Support passing overlay name to idmap2d verify
auto up_to_date =
header->IsUpToDate(target_apk_path, overlay_apk_path, "", target_crc, overlay_crc,
header->IsUpToDate(*GetPointer(*target), **overlay, "",
ConvertAidlArgToPolicyBitmask(fulfilled_policies), enforce_overlayable);
*_aidl_return = static_cast<bool>(up_to_date);
return *_aidl_return ? ok() : error(up_to_date.GetErrorMessage());
if (!up_to_date) {
LOG(WARNING) << "idmap '" << idmap_path
<< "' not up to date : " << up_to_date.GetErrorMessage();
}
return ok();
}
Status Idmap2Service::createIdmap(const std::string& target_apk_path,
const std::string& overlay_apk_path, int32_t fulfilled_policies,
bool enforce_overlayable, int32_t user_id ATTRIBUTE_UNUSED,
Status Idmap2Service::createIdmap(const std::string& target_path, const std::string& overlay_path,
int32_t fulfilled_policies, bool enforce_overlayable,
int32_t user_id ATTRIBUTE_UNUSED,
std::optional<std::string>* _aidl_return) {
assert(_aidl_return);
SYSTRACE << "Idmap2Service::createIdmap " << target_apk_path << " " << overlay_apk_path;
SYSTRACE << "Idmap2Service::createIdmap " << target_path << " " << overlay_path;
_aidl_return->reset();
const PolicyBitmask policy_bitmask = ConvertAidlArgToPolicyBitmask(fulfilled_policies);
const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_apk_path);
const std::string idmap_path = Idmap::CanonicalIdmapPathFor(kIdmapCacheDir, overlay_path);
const uid_t uid = IPCThreadState::self()->getCallingUid();
if (!UidHasWriteAccessToPath(uid, idmap_path)) {
return error(base::StringPrintf("will not write to %s: calling uid %d lacks write accesss",
idmap_path.c_str(), uid));
}
const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
if (!target_apk) {
return error("failed to load apk " + target_apk_path);
const auto target = GetTargetContainer(target_path);
if (!target) {
return error("failed to load target '%s'" + target_path);
}
const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
if (!overlay_apk) {
return error("failed to load apk " + overlay_apk_path);
const auto overlay = OverlayResourceContainer::FromPath(overlay_path);
if (!overlay) {
return error("failed to load apk overlay '%s'" + overlay_path);
}
// TODO(162841629): Support passing overlay name to idmap2d create
const auto idmap =
Idmap::FromApkAssets(*target_apk, *overlay_apk, "", policy_bitmask, enforce_overlayable);
const auto idmap = Idmap::FromContainers(*GetPointer(*target), **overlay, "", policy_bitmask,
enforce_overlayable);
if (!idmap) {
return error(idmap.GetErrorMessage());
}
@@ -220,4 +197,25 @@ Status Idmap2Service::createIdmap(const std::string& target_apk_path,
return ok();
}
idmap2::Result<Idmap2Service::TargetResourceContainerPtr> Idmap2Service::GetTargetContainer(
const std::string& target_path) {
if (target_path == kFrameworkPath) {
if (framework_apk_cache_ == nullptr) {
// Initialize the framework APK cache.
auto target = TargetResourceContainer::FromPath(target_path);
if (!target) {
return target.GetError();
}
framework_apk_cache_ = std::move(*target);
}
return {framework_apk_cache_.get()};
}
auto target = TargetResourceContainer::FromPath(target_path);
if (!target) {
return target.GetError();
}
return {std::move(*target)};
}
} // namespace android::os

View File

@@ -18,12 +18,13 @@
#define IDMAP2_IDMAP2D_IDMAP2SERVICE_H_
#include <android-base/unique_fd.h>
#include <android/os/BnIdmap2.h>
#include <binder/BinderService.h>
#include <idmap2/ResourceContainer.h>
#include <idmap2/Result.h>
#include <string>
#include "android/os/BnIdmap2.h"
namespace android::os {
class Idmap2Service : public BinderService<Idmap2Service>, public BnIdmap2 {
@@ -32,27 +33,44 @@ class Idmap2Service : public BinderService<Idmap2Service>, public BnIdmap2 {
return "idmap";
}
binder::Status getIdmapPath(const std::string& overlay_apk_path, int32_t user_id,
binder::Status getIdmapPath(const std::string& overlay_path, int32_t user_id,
std::string* _aidl_return) override;
binder::Status removeIdmap(const std::string& overlay_apk_path, int32_t user_id,
binder::Status removeIdmap(const std::string& overlay_path, int32_t user_id,
bool* _aidl_return) override;
binder::Status verifyIdmap(const std::string& target_apk_path,
const std::string& overlay_apk_path, int32_t fulfilled_policies,
bool enforce_overlayable, int32_t user_id,
binder::Status verifyIdmap(const std::string& target_path, const std::string& overlay_path,
int32_t fulfilled_policies, bool enforce_overlayable, int32_t user_id,
bool* _aidl_return) override;
binder::Status createIdmap(const std::string& target_apk_path,
const std::string& overlay_apk_path, int32_t fulfilled_policies,
bool enforce_overlayable, int32_t user_id,
binder::Status createIdmap(const std::string& target_path, const std::string& overlay_path,
int32_t fulfilled_policies, bool enforce_overlayable, int32_t user_id,
std::optional<std::string>* _aidl_return) override;
private:
// Cache the crc of the android framework package since the crc cannot change without a reboot.
std::optional<uint32_t> android_crc_;
// idmap2d is killed after a period of inactivity, so any information stored on this class should
// be able to be recalculated if idmap2 dies and restarts.
std::unique_ptr<idmap2::TargetResourceContainer> framework_apk_cache_;
template <typename T>
using MaybeUniquePtr = std::variant<std::unique_ptr<T>, T*>;
using TargetResourceContainerPtr = MaybeUniquePtr<idmap2::TargetResourceContainer>;
idmap2::Result<TargetResourceContainerPtr> GetTargetContainer(const std::string& target_path);
template <typename T>
WARN_UNUSED static const T* GetPointer(const MaybeUniquePtr<T>& ptr);
};
template <typename T>
const T* Idmap2Service::GetPointer(const MaybeUniquePtr<T>& ptr) {
auto u = std::get_if<T*>(&ptr);
if (u != nullptr) {
return *u;
}
return std::get<std::unique_ptr<T>>(ptr).get();
}
} // namespace android::os
#endif // IDMAP2_IDMAP2D_IDMAP2SERVICE_H_

View File

@@ -0,0 +1,103 @@
/*
* Copyright (C) 2021 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 IDMAP2_INCLUDE_IDMAP2_FABRICATEDOVERLAY_H
#define IDMAP2_INCLUDE_IDMAP2_FABRICATEDOVERLAY_H
#include <libidmap2/proto/fabricated_v1.pb.h>
#include <iostream>
#include <map>
#include <memory>
#include <unordered_map>
#include "idmap2/ResourceContainer.h"
#include "idmap2/Result.h"
namespace android::idmap2 {
struct FabricatedOverlay {
struct Builder {
Builder(const std::string& package_name, const std::string& name,
const std::string& target_package_name);
Builder& SetOverlayable(const std::string& name);
Builder& SetResourceValue(const std::string& resource_name, uint8_t data_type,
uint32_t data_value);
WARN_UNUSED Result<FabricatedOverlay> Build();
private:
struct Entry {
std::string resource_name;
DataType data_type;
DataValue data_value;
};
std::string package_name_;
std::string name_;
std::string target_package_name_;
std::string target_overlayable_;
std::vector<Entry> entries_;
};
Result<Unit> ToBinaryStream(std::ostream& stream);
static Result<FabricatedOverlay> FromBinaryStream(std::istream& stream);
private:
struct SerializedData {
std::unique_ptr<uint8_t[]> data;
size_t data_size;
uint32_t crc;
};
Result<SerializedData*> InitializeData() const;
Result<uint32_t> GetCrc() const;
FabricatedOverlay(pb::FabricatedOverlay&& overlay, std::optional<uint32_t> crc_from_disk = {});
pb::FabricatedOverlay overlay_pb_;
std::optional<uint32_t> crc_from_disk_;
mutable std::optional<SerializedData> data_;
friend struct FabricatedOverlayContainer;
};
struct FabricatedOverlayContainer : public OverlayResourceContainer {
static Result<std::unique_ptr<FabricatedOverlayContainer>> FromPath(std::string path);
static std::unique_ptr<FabricatedOverlayContainer> FromOverlay(FabricatedOverlay&& overlay);
// inherited from OverlayResourceContainer
WARN_UNUSED Result<OverlayManifestInfo> FindOverlayInfo(const std::string& name) const override;
WARN_UNUSED Result<OverlayData> GetOverlayData(const OverlayManifestInfo& info) const override;
// inherited from ResourceContainer
WARN_UNUSED Result<uint32_t> GetCrc() const override;
WARN_UNUSED const std::string& GetPath() const override;
WARN_UNUSED Result<std::string> GetResourceName(ResourceId id) const override;
~FabricatedOverlayContainer() override;
private:
FabricatedOverlayContainer(FabricatedOverlay&& overlay, std::string&& path);
FabricatedOverlay overlay_;
std::string path_;
};
} // namespace android::idmap2
#endif // IDMAP2_INCLUDE_IDMAP2_FABRICATEDOVERLAY_H

View File

@@ -23,8 +23,8 @@
* debug_info
* data := data_header target_entry* target_inline_entry* overlay_entry*
* string_pool
* data_header := target_package_id overlay_package_id padding(2) target_entry_count
* target_inline_entry_count overlay_entry_count string_pool_index
* data_header := target_entry_count target_inline_entry_count overlay_entry_count
* string_pool_index
* target_entry := target_id overlay_id
* target_inline_entry := target_id Res_value::size padding(1) Res_value::type
* Res_value::value
@@ -68,11 +68,10 @@
#include <vector>
#include "android-base/macros.h"
#include "androidfw/ApkAssets.h"
#include "androidfw/ResourceTypes.h"
#include "androidfw/StringPiece.h"
#include "idmap2/ResourceContainer.h"
#include "idmap2/ResourceMapping.h"
#include "idmap2/ZipFile.h"
namespace android::idmap2 {
@@ -85,9 +84,6 @@ static constexpr const uint32_t kIdmapMagic = android::kIdmapMagic;
// current version of the idmap binary format; must be incremented when the format is changed
static constexpr const uint32_t kIdmapCurrentVersion = android::kIdmapCurrentVersion;
// Retrieves a crc generated using all of the files within the zip that can affect idmap generation.
Result<uint32_t> GetPackageCrc(const ZipFile& zip_info);
class IdmapHeader {
public:
static std::unique_ptr<const IdmapHeader> FromBinaryStream(std::istream& stream);
@@ -135,9 +131,9 @@ class IdmapHeader {
// Invariant: anytime the idmap data encoding is changed, the idmap version
// field *must* be incremented. Because of this, we know that if the idmap
// header is up-to-date the entire file is up-to-date.
Result<Unit> IsUpToDate(const std::string& target_path, const std::string& overlay_path,
const std::string& overlay_name, PolicyBitmask fulfilled_policies,
bool enforce_overlayable) const;
Result<Unit> IsUpToDate(const TargetResourceContainer& target,
const OverlayResourceContainer& overlay, const std::string& overlay_name,
PolicyBitmask fulfilled_policies, bool enforce_overlayable) const;
Result<Unit> IsUpToDate(const std::string& target_path, const std::string& overlay_path,
const std::string& overlay_name, uint32_t target_crc,
@@ -169,14 +165,6 @@ class IdmapData {
public:
static std::unique_ptr<const Header> FromBinaryStream(std::istream& stream);
inline PackageId GetTargetPackageId() const {
return target_package_id_;
}
inline PackageId GetOverlayPackageId() const {
return overlay_package_id_;
}
inline uint32_t GetTargetEntryCount() const {
return target_entry_count;
}
@@ -196,8 +184,6 @@ class IdmapData {
void accept(Visitor* v) const;
private:
PackageId target_package_id_;
PackageId overlay_package_id_;
uint32_t target_entry_count;
uint32_t target_entry_inline_count;
uint32_t overlay_entry_count;
@@ -275,11 +261,10 @@ class Idmap {
// file is used; change this in the next version of idmap to use a named
// package instead; also update FromApkAssets to take additional parameters:
// the target and overlay package names
static Result<std::unique_ptr<const Idmap>> FromApkAssets(const ApkAssets& target_apk_assets,
const ApkAssets& overlay_apk_assets,
const std::string& overlay_name,
const PolicyBitmask& fulfilled_policies,
bool enforce_overlayable);
static Result<std::unique_ptr<const Idmap>> FromContainers(
const TargetResourceContainer& target, const OverlayResourceContainer& overlay,
const std::string& overlay_name, const PolicyBitmask& fulfilled_policies,
bool enforce_overlayable);
const std::unique_ptr<const IdmapHeader>& GetHeader() const {
return header_;

View File

@@ -41,9 +41,8 @@ class PrettyPrintVisitor : public Visitor {
private:
std::ostream& stream_;
AssetManager2 target_am_;
AssetManager2 overlay_am_;
std::vector<std::unique_ptr<const ApkAssets>> apk_assets_;
std::unique_ptr<TargetResourceContainer> target_;
std::unique_ptr<OverlayResourceContainer> overlay_;
};
} // namespace idmap2

View File

@@ -49,10 +49,9 @@ class RawPrintVisitor : public Visitor {
void pad(size_t padding);
std::ostream& stream_;
std::vector<std::unique_ptr<const ApkAssets>> apk_assets_;
AssetManager2 target_am_;
AssetManager2 overlay_am_;
size_t offset_;
std::unique_ptr<TargetResourceContainer> target_;
std::unique_ptr<OverlayResourceContainer> overlay_;
};
} // namespace idmap2

View File

@@ -0,0 +1,105 @@
/*
* Copyright (C) 2021 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 IDMAP2_INCLUDE_IDMAP2_RESOURCECONTAINER_H
#define IDMAP2_INCLUDE_IDMAP2_RESOURCECONTAINER_H
#include <string>
#include <variant>
#include <vector>
#include "idmap2/Policies.h"
#include "idmap2/ResourceUtils.h"
namespace android::idmap2 {
struct ResourceContainer {
WARN_UNUSED virtual Result<uint32_t> GetCrc() const = 0;
WARN_UNUSED virtual const std::string& GetPath() const = 0;
WARN_UNUSED virtual Result<std::string> GetResourceName(ResourceId id) const = 0;
virtual ~ResourceContainer() = default;
};
struct TargetResourceContainer : public ResourceContainer {
static Result<std::unique_ptr<TargetResourceContainer>> FromPath(std::string path);
WARN_UNUSED virtual Result<bool> DefinesOverlayable() const = 0;
WARN_UNUSED virtual Result<const android::OverlayableInfo*> GetOverlayableInfo(
ResourceId id) const = 0;
WARN_UNUSED virtual Result<ResourceId> GetResourceId(const std::string& name) const = 0;
~TargetResourceContainer() override = default;
};
struct OverlayManifestInfo {
std::string name; // NOLINT(misc-non-private-member-variables-in-classes)
std::string target_package; // NOLINT(misc-non-private-member-variables-in-classes)
std::string target_name; // NOLINT(misc-non-private-member-variables-in-classes)
ResourceId resource_mapping; // NOLINT(misc-non-private-member-variables-in-classes)
};
struct OverlayData {
struct ResourceIdValue {
// The overlay resource id.
ResourceId overlay_id;
// Whether or not references to the overlay resource id should be rewritten to its corresponding
// target id during resource resolution.
bool rewrite_id;
};
struct Value {
std::string resource_name;
std::variant<ResourceIdValue, TargetValue> value;
};
struct InlineStringPoolData {
// The binary data of the android::ResStringPool string pool.
std::unique_ptr<uint8_t[]> data;
// The length of the binary data.
uint32_t data_length;
// The offset added to TargetValue#data_value (the index of the string in the inline string
// pool) in order to prevent the indices of the overlay resource table string pool from
// colliding with the inline string pool indices.
uint32_t string_pool_offset;
};
// The overlay's mapping of target resource name to overlaid value. Use a vector to enforce that
// the overlay pairs are inserted into the ResourceMapping in the specified ordered.
std::vector<Value> pairs;
// If the overlay maps a target resource to a string literal (not a string resource), then the
// this field contains information about the string pool in which the string literal resides so it
// can be inlined into an idmap.
std::optional<InlineStringPoolData> string_pool_data;
};
struct OverlayResourceContainer : public ResourceContainer {
static Result<std::unique_ptr<OverlayResourceContainer>> FromPath(std::string path);
WARN_UNUSED virtual Result<OverlayManifestInfo> FindOverlayInfo(
const std::string& name) const = 0;
WARN_UNUSED virtual Result<OverlayData> GetOverlayData(const OverlayManifestInfo& info) const = 0;
~OverlayResourceContainer() override = default;
};
} // namespace android::idmap2
#endif // IDMAP2_INCLUDE_IDMAP2_RESOURCECONTAINER_H

View File

@@ -17,30 +17,22 @@
#ifndef IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_
#define IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_
#include <androidfw/ApkAssets.h>
#include <map>
#include <memory>
#include <utility>
#include "androidfw/ApkAssets.h"
#include "idmap2/FabricatedOverlay.h"
#include "idmap2/LogInfo.h"
#include "idmap2/Policies.h"
#include "idmap2/ResourceUtils.h"
#include "idmap2/Result.h"
#include "idmap2/XmlParser.h"
using android::idmap2::utils::OverlayManifestInfo;
using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
namespace android::idmap2 {
struct TargetValue {
typedef uint8_t DataType;
typedef uint32_t DataValue;
DataType data_type;
DataValue data_value;
};
using TargetResourceMap = std::map<ResourceId, std::variant<ResourceId, TargetValue>>;
using OverlayResourceMap = std::map<ResourceId, ResourceId>;
@@ -49,94 +41,60 @@ class ResourceMapping {
// Creates a ResourceMapping using the target and overlay APKs. Setting enforce_overlayable to
// `false` disables all overlayable and policy enforcement: this is intended for backwards
// compatibility pre-Q and unit tests.
static Result<ResourceMapping> FromApkAssets(const ApkAssets& target_apk_assets,
const ApkAssets& overlay_apk_assets,
const OverlayManifestInfo& overlay_info,
const PolicyBitmask& fulfilled_policies,
bool enforce_overlayable, LogInfo& log_info);
static Result<ResourceMapping> FromContainers(const TargetResourceContainer& target,
const OverlayResourceContainer& overlay,
const OverlayManifestInfo& overlay_info,
const PolicyBitmask& fulfilled_policies,
bool enforce_overlayable, LogInfo& log_info);
// Retrieves the mapping of target resource id to overlay value.
inline const TargetResourceMap& GetTargetToOverlayMap() const {
return target_map_;
}
WARN_UNUSED const TargetResourceMap& GetTargetToOverlayMap() const;
// Retrieves the mapping of overlay resource id to target resource id. This allows a reference to
// an overlay resource to appear as a reference to its corresponding target resource at runtime.
OverlayResourceMap GetOverlayToTargetMap() const;
// Retrieves the build-time package id of the target package.
inline uint32_t GetTargetPackageId() const {
return target_package_id_;
}
// Retrieves the build-time package id of the overlay package.
inline uint32_t GetOverlayPackageId() const {
return overlay_package_id_;
}
WARN_UNUSED const OverlayResourceMap& GetOverlayToTargetMap() const;
// Retrieves the offset that was added to the index of inline string overlay values so the indices
// do not collide with the indices of the overlay resource table string pool.
inline uint32_t GetStringPoolOffset() const {
return string_pool_offset_;
}
WARN_UNUSED uint32_t GetStringPoolOffset() const;
// Retrieves the raw string pool data from the xml referenced in android:resourcesMap.
inline const StringPiece GetStringPoolData() const {
return StringPiece(reinterpret_cast<const char*>(string_pool_data_.get()),
string_pool_data_length_);
}
WARN_UNUSED StringPiece GetStringPoolData() const;
private:
ResourceMapping() = default;
// Maps a target resource id to an overlay resource id.
// If rewrite_overlay_reference is `true` then references to the overlay
// resource should appear as a reference to its corresponding target resource at runtime.
Result<Unit> AddMapping(ResourceId target_resource, ResourceId overlay_resource,
bool rewrite_overlay_reference);
// Maps a target resource id to a data type and value combination.
// The `data_type` is the runtime format of the data value (see Res_value::dataType).
Result<Unit> AddMapping(ResourceId target_resource, TargetValue::DataType data_type,
TargetValue::DataValue data_value);
// Removes the overlay value mapping for the target resource.
void RemoveMapping(ResourceId target_resource);
// Parses the mapping of target resources to overlay resources to generate a ResourceMapping.
static Result<ResourceMapping> CreateResourceMapping(const AssetManager2* target_am,
const LoadedPackage* target_package,
const LoadedPackage* overlay_package,
size_t string_pool_offset,
const XmlParser& overlay_parser,
LogInfo& log_info);
// Generates a ResourceMapping that maps target resources to overlay resources by name. To overlay
// a target resource, a resource must exist in the overlay with the same type and entry name as
// the target resource.
static Result<ResourceMapping> CreateResourceMappingLegacy(const AssetManager2* target_am,
const AssetManager2* overlay_am,
const LoadedPackage* target_package,
const LoadedPackage* overlay_package,
LogInfo& log_info);
// Removes resources that do not pass policy or overlayable checks of the target package.
void FilterOverlayableResources(const AssetManager2* target_am,
const LoadedPackage* target_package,
const LoadedPackage* overlay_package,
const OverlayManifestInfo& overlay_info,
const PolicyBitmask& fulfilled_policies, LogInfo& log_info);
// Maps a target resource id to an overlay resource id or a android::Res_value value.
//
// If `allow_rewriting_` is true, then the overlay-to-target map will be populated if the target
// resource id is mapped to an overlay resource id.
Result<Unit> AddMapping(ResourceId target_resource,
const std::variant<OverlayData::ResourceIdValue, TargetValue>& value);
TargetResourceMap target_map_;
std::multimap<ResourceId, ResourceId> overlay_map_;
uint32_t target_package_id_ = 0;
uint32_t overlay_package_id_ = 0;
OverlayResourceMap overlay_map_;
uint32_t string_pool_offset_ = 0;
uint32_t string_pool_data_length_ = 0;
std::unique_ptr<uint8_t[]> string_pool_data_ = nullptr;
};
inline const TargetResourceMap& ResourceMapping::GetTargetToOverlayMap() const {
return target_map_;
}
inline const OverlayResourceMap& ResourceMapping::GetOverlayToTargetMap() const {
return overlay_map_;
}
inline uint32_t ResourceMapping::GetStringPoolOffset() const {
return string_pool_offset_;
}
inline StringPiece ResourceMapping::GetStringPoolData() const {
return StringPiece(reinterpret_cast<const char*>(string_pool_data_.get()),
string_pool_data_length_);
}
} // namespace android::idmap2
#endif // IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_

View File

@@ -22,19 +22,26 @@
#include "androidfw/AssetManager2.h"
#include "idmap2/Result.h"
#include "idmap2/ZipFile.h"
namespace android::idmap2 {
// use typedefs to let the compiler warn us about implicit casts
typedef uint32_t ResourceId; // 0xpptteeee
typedef uint8_t PackageId; // pp in 0xpptteeee
typedef uint8_t TypeId; // tt in 0xpptteeee
typedef uint16_t EntryId; // eeee in 0xpptteeee
#define EXTRACT_TYPE(resid) ((0x00ff0000 & (resid)) >> 16)
#define EXTRACT_ENTRY(resid) (0x0000ffff & (resid))
// use typedefs to let the compiler warn us about implicit casts
using ResourceId = uint32_t; // 0xpptteeee
using PackageId = uint8_t; // pp in 0xpptteeee
using TypeId = uint8_t; // tt in 0xpptteeee
using EntryId = uint16_t; // eeee in 0xpptteeee
using DataType = uint8_t; // Res_value::dataType
using DataValue = uint32_t; // Res_value::data
struct TargetValue {
DataType data_type;
DataValue data_value;
};
namespace utils {
// Returns whether the Res_value::data_type represents a dynamic or regular resource reference.
@@ -43,20 +50,10 @@ bool IsReference(uint8_t data_type);
// Converts the Res_value::data_type to a human-readable string representation.
StringPiece DataTypeToString(uint8_t data_type);
struct OverlayManifestInfo {
std::string name; // NOLINT(misc-non-private-member-variables-in-classes)
std::string target_package; // NOLINT(misc-non-private-member-variables-in-classes)
std::string target_name; // NOLINT(misc-non-private-member-variables-in-classes)
uint32_t resource_mapping; // NOLINT(misc-non-private-member-variables-in-classes)
};
Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path,
const std::string& name);
// Retrieves the type and entry name of the resource in the AssetManager in the form type/entry.
Result<std::string> ResToTypeEntryName(const AssetManager2& am, ResourceId resid);
} // namespace utils
} // namespace android::idmap2
#endif // IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_

View File

@@ -30,8 +30,7 @@
namespace android::idmap2 {
class XmlParser {
public:
struct XmlParser {
using Event = ResXMLParser::event_code_t;
class iterator;
@@ -127,23 +126,19 @@ class XmlParser {
};
// Creates a new xml parser beginning at the first tag.
static Result<std::unique_ptr<const XmlParser>> Create(const void* data, size_t size,
bool copy_data = false);
~XmlParser();
static Result<XmlParser> Create(const void* data, size_t size, bool copy_data = false);
inline iterator tree_iterator() const {
return iterator(tree_);
return iterator(*tree_);
}
inline const ResStringPool& get_strings() const {
return tree_.getStrings();
return tree_->getStrings();
}
private:
XmlParser() = default;
mutable ResXMLTree tree_;
DISALLOW_COPY_AND_ASSIGN(XmlParser);
explicit XmlParser(std::unique_ptr<ResXMLTree> tree);
mutable std::unique_ptr<ResXMLTree> tree_;
};
} // namespace android::idmap2

View File

@@ -1,60 +0,0 @@
/*
* Copyright (C) 2018 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 IDMAP2_INCLUDE_IDMAP2_ZIPFILE_H_
#define IDMAP2_INCLUDE_IDMAP2_ZIPFILE_H_
#include <memory>
#include <string>
#include "android-base/macros.h"
#include "idmap2/Result.h"
#include "ziparchive/zip_archive.h"
namespace android::idmap2 {
struct MemoryChunk {
size_t size;
uint8_t buf[0];
static std::unique_ptr<MemoryChunk> Allocate(size_t size);
private:
MemoryChunk() {
}
};
class ZipFile {
public:
static std::unique_ptr<const ZipFile> Open(const std::string& path);
std::unique_ptr<const MemoryChunk> Uncompress(const std::string& entryPath) const;
Result<uint32_t> Crc(const std::string& entryPath) const;
~ZipFile();
private:
explicit ZipFile(const ::ZipArchiveHandle handle) : handle_(handle) {
}
const ::ZipArchiveHandle handle_;
DISALLOW_COPY_AND_ASSIGN(ZipFile);
};
} // namespace android::idmap2
#endif // IDMAP2_INCLUDE_IDMAP2_ZIPFILE_H_

View File

@@ -87,10 +87,6 @@ void BinaryStreamVisitor::visit(const IdmapData& data) {
}
void BinaryStreamVisitor::visit(const IdmapData::Header& header) {
Write8(header.GetTargetPackageId());
Write8(header.GetOverlayPackageId());
Write8(0U); // padding
Write8(0U); // padding
Write32(header.GetTargetEntryCount());
Write32(header.GetTargetInlineEntryCount());
Write32(header.GetOverlayEntryCount());

View File

@@ -0,0 +1,297 @@
/*
* Copyright (C) 2021 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 "idmap2/FabricatedOverlay.h"
#include <androidfw/ResourceUtils.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
#include <utils/ByteOrder.h>
#include <zlib.h>
#include <fstream>
namespace android::idmap2 {
namespace {
bool Read32(std::istream& stream, uint32_t* out) {
uint32_t value;
if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint32_t))) {
*out = dtohl(value);
return true;
}
return false;
}
void Write32(std::ostream& stream, uint32_t value) {
uint32_t x = htodl(value);
stream.write(reinterpret_cast<char*>(&x), sizeof(uint32_t));
}
} // namespace
FabricatedOverlay::FabricatedOverlay(pb::FabricatedOverlay&& overlay,
std::optional<uint32_t> crc_from_disk)
: overlay_pb_(std::forward<pb::FabricatedOverlay>(overlay)), crc_from_disk_(crc_from_disk) {
}
FabricatedOverlay::Builder::Builder(const std::string& package_name, const std::string& name,
const std::string& target_package_name) {
package_name_ = package_name;
name_ = name;
target_package_name_ = target_package_name;
}
FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetOverlayable(const std::string& name) {
target_overlayable_ = name;
return *this;
}
FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetResourceValue(
const std::string& resource_name, uint8_t data_type, uint32_t data_value) {
entries_.emplace_back(Entry{resource_name, data_type, data_value});
return *this;
}
Result<FabricatedOverlay> FabricatedOverlay::Builder::Build() {
std::map<std::string, std::map<std::string, std::map<std::string, TargetValue>>> entries;
for (const auto& res_entry : entries_) {
StringPiece package_substr;
StringPiece type_name;
StringPiece entry_name;
if (!android::ExtractResourceName(StringPiece(res_entry.resource_name), &package_substr,
&type_name, &entry_name)) {
return Error("failed to parse resource name '%s'", res_entry.resource_name.c_str());
}
std::string package_name =
package_substr.empty() ? target_package_name_ : package_substr.to_string();
if (type_name.empty()) {
return Error("resource name '%s' missing type name", res_entry.resource_name.c_str());
}
if (entry_name.empty()) {
return Error("resource name '%s' missing entry name", res_entry.resource_name.c_str());
}
auto package = entries.find(package_name);
if (package == entries.end()) {
package = entries
.insert(std::make_pair<>(
package_name, std::map<std::string, std::map<std::string, TargetValue>>()))
.first;
}
auto type = package->second.find(type_name.to_string());
if (type == package->second.end()) {
type =
package->second
.insert(std::make_pair<>(type_name.to_string(), std::map<std::string, TargetValue>()))
.first;
}
auto entry = type->second.find(entry_name.to_string());
if (entry == type->second.end()) {
entry = type->second.insert(std::make_pair<>(entry_name.to_string(), TargetValue())).first;
}
entry->second = TargetValue{res_entry.data_type, res_entry.data_value};
}
pb::FabricatedOverlay overlay_pb;
overlay_pb.set_package_name(package_name_);
overlay_pb.set_name(name_);
overlay_pb.set_target_package_name(target_package_name_);
overlay_pb.set_target_overlayable(target_overlayable_);
for (const auto& package : entries) {
auto package_pb = overlay_pb.add_packages();
package_pb->set_name(package.first);
for (const auto& type : package.second) {
auto type_pb = package_pb->add_types();
type_pb->set_name(type.first);
for (const auto& entry : type.second) {
auto entry_pb = type_pb->add_entries();
entry_pb->set_name(entry.first);
pb::ResourceValue* value = entry_pb->mutable_res_value();
value->set_data_type(entry.second.data_type);
value->set_data_value(entry.second.data_value);
}
}
}
return FabricatedOverlay(std::move(overlay_pb));
}
Result<FabricatedOverlay> FabricatedOverlay::FromBinaryStream(std::istream& stream) {
uint32_t magic;
if (!Read32(stream, &magic)) {
return Error("Failed to read fabricated overlay magic.");
}
if (magic != kFabricatedOverlayMagic) {
return Error("Not a fabricated overlay file.");
}
uint32_t version;
if (!Read32(stream, &version)) {
return Error("Failed to read fabricated overlay version.");
}
if (version != 1) {
return Error("Invalid fabricated overlay version '%u'.", version);
}
uint32_t crc;
if (!Read32(stream, &crc)) {
return Error("Failed to read fabricated overlay version.");
}
pb::FabricatedOverlay overlay{};
if (!overlay.ParseFromIstream(&stream)) {
return Error("Failed read fabricated overlay proto.");
}
// If the proto version is the latest version, then the contents of the proto must be the same
// when the proto is re-serialized; otherwise, the crc must be calculated because migrating the
// proto to the latest version will likely change the contents of the fabricated overlay.
return FabricatedOverlay(std::move(overlay), version == kFabricatedOverlayCurrentVersion
? std::optional<uint32_t>(crc)
: std::nullopt);
}
Result<FabricatedOverlay::SerializedData*> FabricatedOverlay::InitializeData() const {
if (!data_.has_value()) {
auto size = overlay_pb_.ByteSizeLong();
auto data = std::unique_ptr<uint8_t[]>(new uint8_t[size]);
// Ensure serialization is deterministic
google::protobuf::io::ArrayOutputStream array_stream(data.get(), size);
google::protobuf::io::CodedOutputStream output_stream(&array_stream);
output_stream.SetSerializationDeterministic(true);
overlay_pb_.SerializeWithCachedSizes(&output_stream);
if (output_stream.HadError() || size != output_stream.ByteCount()) {
return Error("Failed to serialize fabricated overlay.");
}
// Calculate the crc using the proto data and the version.
uint32_t crc = crc32(0L, Z_NULL, 0);
crc = crc32(crc, reinterpret_cast<const uint8_t*>(&kFabricatedOverlayCurrentVersion),
sizeof(uint32_t));
crc = crc32(crc, data.get(), size);
data_ = SerializedData{std::move(data), size, crc};
}
return &(*data_);
}
Result<uint32_t> FabricatedOverlay::GetCrc() const {
if (crc_from_disk_.has_value()) {
return *crc_from_disk_;
}
auto data = InitializeData();
if (!data) {
return data.GetError();
}
return (*data)->crc;
}
Result<Unit> FabricatedOverlay::ToBinaryStream(std::ostream& stream) {
auto data = InitializeData();
if (!data) {
return data.GetError();
}
Write32(stream, kFabricatedOverlayMagic);
Write32(stream, kFabricatedOverlayCurrentVersion);
Write32(stream, (*data)->crc);
stream.write(reinterpret_cast<const char*>((*data)->data.get()), (*data)->data_size);
if (stream.bad()) {
return Error("Failed to write serialized fabricated overlay.");
}
return Unit{};
}
using FabContainer = FabricatedOverlayContainer;
FabContainer::FabricatedOverlayContainer(FabricatedOverlay&& overlay, std::string&& path)
: overlay_(std::forward<FabricatedOverlay>(overlay)), path_(std::forward<std::string>(path)) {
}
FabContainer::~FabricatedOverlayContainer() = default;
Result<std::unique_ptr<FabContainer>> FabContainer::FromPath(std::string path) {
std::fstream fin(path);
auto overlay = FabricatedOverlay::FromBinaryStream(fin);
if (!overlay) {
return overlay.GetError();
}
return std::unique_ptr<FabContainer>(
new FabricatedOverlayContainer(std::move(*overlay), std::move(path)));
}
std::unique_ptr<FabricatedOverlayContainer> FabContainer::FromOverlay(FabricatedOverlay&& overlay) {
return std::unique_ptr<FabContainer>(
new FabricatedOverlayContainer(std::move(overlay), {} /* path */));
}
Result<OverlayManifestInfo> FabContainer::FindOverlayInfo(const std::string& name) const {
const pb::FabricatedOverlay& overlay_pb = overlay_.overlay_pb_;
if (name != overlay_pb.name()) {
return Error("Failed to find name '%s' in fabricated overlay", name.c_str());
}
return OverlayManifestInfo{
.name = overlay_pb.name(),
.target_package = overlay_pb.target_package_name(),
.target_name = overlay_pb.target_overlayable(),
};
}
Result<OverlayData> FabContainer::GetOverlayData(const OverlayManifestInfo& info) const {
const pb::FabricatedOverlay& overlay_pb = overlay_.overlay_pb_;
if (info.name != overlay_pb.name()) {
return Error("Failed to find name '%s' in fabricated overlay", info.name.c_str());
}
OverlayData result{};
for (const auto& package : overlay_pb.packages()) {
for (const auto& type : package.types()) {
for (const auto& entry : type.entries()) {
auto name = base::StringPrintf("%s:%s/%s", package.name().c_str(), type.name().c_str(),
entry.name().c_str());
const auto& res_value = entry.res_value();
result.pairs.emplace_back(OverlayData::Value{
name, TargetValue{.data_type = static_cast<uint8_t>(res_value.data_type()),
.data_value = res_value.data_value()}});
}
}
}
return result;
}
Result<uint32_t> FabContainer::GetCrc() const {
return overlay_.GetCrc();
}
const std::string& FabContainer::GetPath() const {
return path_;
}
Result<std::string> FabContainer::GetResourceName(ResourceId /* id */) const {
return Error("Fabricated overlay does not contain resources.");
}
} // namespace android::idmap2

View File

@@ -20,23 +20,16 @@
#include <iostream>
#include <iterator>
#include <limits>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "android-base/macros.h"
#include "android-base/stringprintf.h"
#include "androidfw/AssetManager2.h"
#include "idmap2/ResourceMapping.h"
#include "idmap2/ResourceUtils.h"
#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
#include "idmap2/ZipFile.h"
#include "utils/String16.h"
#include "utils/String8.h"
namespace android::idmap2 {
@@ -93,22 +86,13 @@ bool WARN_UNUSED ReadString(std::istream& stream, std::string* out) {
} // namespace
Result<uint32_t> GetPackageCrc(const ZipFile& zip) {
const Result<uint32_t> a = zip.Crc("resources.arsc");
const Result<uint32_t> b = zip.Crc("AndroidManifest.xml");
return a && b
? Result<uint32_t>(*a ^ *b)
: Error("failed to get CRC for \"%s\"", a ? "AndroidManifest.xml" : "resources.arsc");
}
std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& stream) {
std::unique_ptr<IdmapHeader> idmap_header(new IdmapHeader());
if (!Read32(stream, &idmap_header->magic_) || !Read32(stream, &idmap_header->version_)) {
return nullptr;
}
if (idmap_header->magic_ != kIdmapMagic ||
idmap_header->version_ != kIdmapCurrentVersion) {
if (idmap_header->magic_ != kIdmapMagic || idmap_header->version_ != kIdmapCurrentVersion) {
// Do not continue parsing if the file is not a current version idmap.
return nullptr;
}
@@ -127,32 +111,22 @@ std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& s
return std::move(idmap_header);
}
Result<Unit> IdmapHeader::IsUpToDate(const std::string& target_path,
const std::string& overlay_path,
Result<Unit> IdmapHeader::IsUpToDate(const TargetResourceContainer& target,
const OverlayResourceContainer& overlay,
const std::string& overlay_name,
PolicyBitmask fulfilled_policies,
bool enforce_overlayable) const {
const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_path);
if (!target_zip) {
return Error("failed to open target %s", target_path.c_str());
}
const Result<uint32_t> target_crc = GetPackageCrc(*target_zip);
const Result<uint32_t> target_crc = target.GetCrc();
if (!target_crc) {
return Error("failed to get target crc");
}
const std::unique_ptr<const ZipFile> overlay_zip = ZipFile::Open(overlay_path);
if (!overlay_zip) {
return Error("failed to overlay target %s", overlay_path.c_str());
}
const Result<uint32_t> overlay_crc = GetPackageCrc(*overlay_zip);
const Result<uint32_t> overlay_crc = overlay.GetCrc();
if (!overlay_crc) {
return Error("failed to get overlay crc");
}
return IsUpToDate(target_path, overlay_path, overlay_name, *target_crc, *overlay_crc,
return IsUpToDate(target.GetPath(), overlay.GetPath(), overlay_name, *target_crc, *overlay_crc,
fulfilled_policies, enforce_overlayable);
}
@@ -209,11 +183,7 @@ Result<Unit> IdmapHeader::IsUpToDate(const std::string& target_path,
std::unique_ptr<const IdmapData::Header> IdmapData::Header::FromBinaryStream(std::istream& stream) {
std::unique_ptr<IdmapData::Header> idmap_data_header(new IdmapData::Header());
uint8_t padding;
if (!Read8(stream, &idmap_data_header->target_package_id_) ||
!Read8(stream, &idmap_data_header->overlay_package_id_) || !Read8(stream, &padding) ||
!Read8(stream, &padding) || !Read32(stream, &idmap_data_header->target_entry_count) ||
if (!Read32(stream, &idmap_data_header->target_entry_count) ||
!Read32(stream, &idmap_data_header->target_entry_inline_count) ||
!Read32(stream, &idmap_data_header->overlay_entry_count) ||
!Read32(stream, &idmap_data_header->string_pool_index_offset)) {
@@ -321,8 +291,6 @@ Result<std::unique_ptr<const IdmapData>> IdmapData::FromResourceMapping(
}
std::unique_ptr<IdmapData::Header> data_header(new IdmapData::Header());
data_header->target_package_id_ = resource_mapping.GetTargetPackageId();
data_header->overlay_package_id_ = resource_mapping.GetOverlayPackageId();
data_header->target_entry_count = static_cast<uint32_t>(data->target_entries_.size());
data_header->target_entry_inline_count =
static_cast<uint32_t>(data->target_inline_entries_.size());
@@ -332,57 +300,46 @@ Result<std::unique_ptr<const IdmapData>> IdmapData::FromResourceMapping(
return {std::move(data)};
}
Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const ApkAssets& target_apk_assets,
const ApkAssets& overlay_apk_assets,
const std::string& overlay_name,
const PolicyBitmask& fulfilled_policies,
bool enforce_overlayable) {
Result<std::unique_ptr<const Idmap>> Idmap::FromContainers(const TargetResourceContainer& target,
const OverlayResourceContainer& overlay,
const std::string& overlay_name,
const PolicyBitmask& fulfilled_policies,
bool enforce_overlayable) {
SYSTRACE << "Idmap::FromApkAssets";
const std::string& target_apk_path = target_apk_assets.GetPath();
const std::string& overlay_apk_path = overlay_apk_assets.GetPath();
const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_apk_path);
if (!target_zip) {
return Error("failed to open target as zip");
}
const std::unique_ptr<const ZipFile> overlay_zip = ZipFile::Open(overlay_apk_path);
if (!overlay_zip) {
return Error("failed to open overlay as zip");
}
std::unique_ptr<IdmapHeader> header(new IdmapHeader());
header->magic_ = kIdmapMagic;
header->version_ = kIdmapCurrentVersion;
Result<uint32_t> crc = GetPackageCrc(*target_zip);
if (!crc) {
return Error(crc.GetError(), "failed to get zip CRC for target");
const auto target_crc = target.GetCrc();
if (!target_crc) {
return Error(target_crc.GetError(), "failed to get zip CRC for '%s'", target.GetPath().data());
}
header->target_crc_ = *crc;
header->target_crc_ = *target_crc;
crc = GetPackageCrc(*overlay_zip);
if (!crc) {
return Error(crc.GetError(), "failed to get zip CRC for overlay");
const auto overlay_crc = overlay.GetCrc();
if (!overlay_crc) {
return Error(overlay_crc.GetError(), "failed to get zip CRC for '%s'",
overlay.GetPath().data());
}
header->overlay_crc_ = *crc;
header->overlay_crc_ = *overlay_crc;
header->fulfilled_policies_ = fulfilled_policies;
header->enforce_overlayable_ = enforce_overlayable;
header->target_path_ = target_apk_path;
header->overlay_path_ = overlay_apk_path;
header->target_path_ = target.GetPath();
header->overlay_path_ = overlay.GetPath();
header->overlay_name_ = overlay_name;
auto info = utils::ExtractOverlayManifestInfo(overlay_apk_path, overlay_name);
auto info = overlay.FindOverlayInfo(overlay_name);
if (!info) {
return info.GetError();
return Error(info.GetError(), "failed to get overlay info for '%s'", overlay.GetPath().data());
}
LogInfo log_info;
auto resource_mapping =
ResourceMapping::FromApkAssets(target_apk_assets, overlay_apk_assets, *info,
fulfilled_policies, enforce_overlayable, log_info);
auto resource_mapping = ResourceMapping::FromContainers(
target, overlay, *info, fulfilled_policies, enforce_overlayable, log_info);
if (!resource_mapping) {
return resource_mapping.GetError();
return Error(resource_mapping.GetError(), "failed to generate resource map for '%s'",
overlay.GetPath().data());
}
auto idmap_data = IdmapData::FromResourceMapping(*resource_mapping);

View File

@@ -27,8 +27,6 @@
namespace android::idmap2 {
#define RESID(pkg, type, entry) (((pkg) << 24) | ((type) << 16) | (entry))
#define TAB " "
void PrettyPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
@@ -36,8 +34,8 @@ void PrettyPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
void PrettyPrintVisitor::visit(const IdmapHeader& header) {
stream_ << "Paths:" << std::endl
<< TAB "target apk path : " << header.GetTargetPath() << std::endl
<< TAB "overlay apk path : " << header.GetOverlayPath() << std::endl;
<< TAB "target path : " << header.GetTargetPath() << std::endl
<< TAB "overlay path : " << header.GetOverlayPath() << std::endl;
if (!header.GetOverlayName().empty()) {
stream_ << "Overlay name: " << header.GetOverlayName() << std::endl;
@@ -53,14 +51,11 @@ void PrettyPrintVisitor::visit(const IdmapHeader& header) {
}
}
if (auto target_apk_ = ApkAssets::Load(header.GetTargetPath())) {
target_am_.SetApkAssets({target_apk_.get()});
apk_assets_.push_back(std::move(target_apk_));
if (auto target = TargetResourceContainer::FromPath(header.GetTargetPath())) {
target_ = std::move(*target);
}
if (auto overlay_apk = ApkAssets::Load(header.GetOverlayPath())) {
overlay_am_.SetApkAssets({overlay_apk.get()});
apk_assets_.push_back(std::move(overlay_apk));
if (auto overlay = OverlayResourceContainer::FromPath(header.GetOverlayPath())) {
overlay_ = std::move(*overlay);
}
stream_ << "Mapping:" << std::endl;
@@ -72,23 +67,20 @@ void PrettyPrintVisitor::visit(const IdmapData::Header& header ATTRIBUTE_UNUSED)
void PrettyPrintVisitor::visit(const IdmapData& data) {
static constexpr const char* kUnknownResourceName = "???";
const bool target_package_loaded = !target_am_.GetApkAssets().empty();
const bool overlay_package_loaded = !overlay_am_.GetApkAssets().empty();
const ResStringPool string_pool(data.GetStringPoolData().data(), data.GetStringPoolData().size());
const size_t string_pool_offset = data.GetHeader()->GetStringPoolIndexOffset();
for (const auto& target_entry : data.GetTargetEntries()) {
std::string target_name = kUnknownResourceName;
if (target_package_loaded) {
if (auto name = utils::ResToTypeEntryName(target_am_, target_entry.target_id)) {
if (target_ != nullptr) {
if (auto name = target_->GetResourceName(target_entry.target_id)) {
target_name = *name;
}
}
std::string overlay_name = kUnknownResourceName;
if (overlay_package_loaded) {
if (auto name = utils::ResToTypeEntryName(overlay_am_, target_entry.overlay_id)) {
if (overlay_ != nullptr) {
if (auto name = overlay_->GetResourceName(target_entry.overlay_id)) {
overlay_name = *name;
}
}
@@ -112,8 +104,8 @@ void PrettyPrintVisitor::visit(const IdmapData& data) {
}
std::string target_name = kUnknownResourceName;
if (target_package_loaded) {
if (auto name = utils::ResToTypeEntryName(target_am_, target_entry.target_id)) {
if (target_ != nullptr) {
if (auto name = target_->GetResourceName(target_entry.target_id)) {
target_name = *name;
}
}

View File

@@ -18,16 +18,13 @@
#include <algorithm>
#include <cstdarg>
#include <string>
#include "android-base/macros.h"
#include "android-base/stringprintf.h"
#include "androidfw/ApkAssets.h"
#include "idmap2/PolicyUtils.h"
#include "idmap2/ResourceUtils.h"
#include "idmap2/Result.h"
using android::ApkAssets;
using android::idmap2::policy::PoliciesToDebugString;
namespace android::idmap2 {
@@ -48,27 +45,19 @@ void RawPrintVisitor::visit(const IdmapHeader& header) {
print(header.GetOverlayName(), true /* print_value */, "overlay name");
print(header.GetDebugInfo(), false /* print_value */, "debug info");
auto target_apk_ = ApkAssets::Load(header.GetTargetPath());
if (target_apk_) {
target_am_.SetApkAssets({target_apk_.get()});
apk_assets_.push_back(std::move(target_apk_));
if (auto target = TargetResourceContainer::FromPath(header.GetTargetPath())) {
target_ = std::move(*target);
}
auto overlay_apk_ = ApkAssets::Load(header.GetOverlayPath());
if (overlay_apk_) {
overlay_am_.SetApkAssets({overlay_apk_.get()});
apk_assets_.push_back(std::move(overlay_apk_));
if (auto overlay = OverlayResourceContainer::FromPath(header.GetOverlayPath())) {
overlay_ = std::move(*overlay);
}
}
void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
const bool target_package_loaded = !target_am_.GetApkAssets().empty();
const bool overlay_package_loaded = !overlay_am_.GetApkAssets().empty();
for (auto& target_entry : data.GetTargetEntries()) {
Result<std::string> target_name(Error(""));
if (target_package_loaded) {
target_name = utils::ResToTypeEntryName(target_am_, target_entry.target_id);
if (target_ != nullptr) {
target_name = target_->GetResourceName(target_entry.target_id);
}
if (target_name) {
print(target_entry.target_id, "target id: %s", target_name->c_str());
@@ -77,8 +66,8 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
}
Result<std::string> overlay_name(Error(""));
if (overlay_package_loaded) {
overlay_name = utils::ResToTypeEntryName(overlay_am_, target_entry.overlay_id);
if (overlay_ != nullptr) {
overlay_name = overlay_->GetResourceName(target_entry.overlay_id);
}
if (overlay_name) {
print(target_entry.overlay_id, "overlay id: %s", overlay_name->c_str());
@@ -89,8 +78,8 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
for (auto& target_entry : data.GetTargetInlineEntries()) {
Result<std::string> target_name(Error(""));
if (target_package_loaded) {
target_name = utils::ResToTypeEntryName(target_am_, target_entry.target_id);
if (target_ != nullptr) {
target_name = target_->GetResourceName(target_entry.target_id);
}
if (target_name) {
print(target_entry.target_id, "target id: %s", target_name->c_str());
@@ -104,10 +93,10 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
utils::DataTypeToString(target_entry.value.data_type).data());
Result<std::string> overlay_name(Error(""));
if (overlay_package_loaded &&
if (overlay_ != nullptr &&
(target_entry.value.data_value == Res_value::TYPE_REFERENCE ||
target_entry.value.data_value == Res_value::TYPE_DYNAMIC_REFERENCE)) {
overlay_name = utils::ResToTypeEntryName(overlay_am_, target_entry.value.data_value);
overlay_name = overlay_->GetResourceName(target_entry.value.data_value);
}
if (overlay_name) {
@@ -119,8 +108,8 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
for (auto& overlay_entry : data.GetOverlayEntries()) {
Result<std::string> overlay_name(Error(""));
if (overlay_package_loaded) {
overlay_name = utils::ResToTypeEntryName(overlay_am_, overlay_entry.overlay_id);
if (overlay_ != nullptr) {
overlay_name = overlay_->GetResourceName(overlay_entry.overlay_id);
}
if (overlay_name) {
@@ -130,8 +119,8 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
}
Result<std::string> target_name(Error(""));
if (target_package_loaded) {
target_name = utils::ResToTypeEntryName(target_am_, overlay_entry.target_id);
if (target_ != nullptr) {
target_name = target_->GetResourceName(overlay_entry.target_id);
}
if (target_name) {
@@ -145,9 +134,6 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
}
void RawPrintVisitor::visit(const IdmapData::Header& header) {
print(header.GetTargetPackageId(), "target package id");
print(header.GetOverlayPackageId(), "overlay package id");
align();
print(header.GetTargetEntryCount(), "target entry count");
print(header.GetTargetInlineEntryCount(), "target inline entry count");
print(header.GetOverlayEntryCount(), "overlay entry count");

View File

@@ -0,0 +1,440 @@
/*
* Copyright (C) 2021 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 "idmap2/ResourceContainer.h"
#include "androidfw/ApkAssets.h"
#include "androidfw/AssetManager.h"
#include "androidfw/Util.h"
#include "idmap2/FabricatedOverlay.h"
#include "idmap2/XmlParser.h"
namespace android::idmap2 {
namespace {
#define REWRITE_PACKAGE(resid, package_id) \
(((resid)&0x00ffffffU) | (((uint32_t)(package_id)) << 24U))
#define EXTRACT_PACKAGE(resid) ((0xff000000 & (resid)) >> 24)
constexpr ResourceId kAttrName = 0x01010003;
constexpr ResourceId kAttrResourcesMap = 0x01010609;
constexpr ResourceId kAttrTargetName = 0x0101044d;
constexpr ResourceId kAttrTargetPackage = 0x01010021;
// idmap version 0x01 naively assumes that the package to use is always the first ResTable_package
// in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so
// this assumption tends to work out. That said, the correct thing to do is to scan
// resources.arsc for a package with a given name as read from the package manifest instead of
// relying on a hard-coded index. This however requires storing the package name in the idmap
// header, which in turn requires incrementing the idmap version. Because the initial version of
// idmap2 is compatible with idmap, this will have to wait for now.
const LoadedPackage* GetPackageAtIndex0(const LoadedArsc* loaded_arsc) {
const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages();
if (packages.empty()) {
return nullptr;
}
return loaded_arsc->GetPackageById(packages[0]->GetPackageId());
}
Result<uint32_t> CalculateCrc(const ZipAssetsProvider* zip_assets) {
constexpr const char* kResourcesArsc = "resources.arsc";
std::optional<uint32_t> res_crc = zip_assets->GetCrc(kResourcesArsc);
if (!res_crc) {
return Error("failed to get CRC for '%s'", kResourcesArsc);
}
constexpr const char* kManifest = "AndroidManifest.xml";
std::optional<uint32_t> man_crc = zip_assets->GetCrc(kManifest);
if (!man_crc) {
return Error("failed to get CRC for '%s'", kManifest);
}
return *res_crc ^ *man_crc;
}
Result<XmlParser> OpenXmlParser(const std::string& entry_path, const ZipAssetsProvider* zip) {
auto manifest = zip->Open(entry_path);
if (manifest == nullptr) {
return Error("failed to find %s ", entry_path.c_str());
}
auto size = manifest->getLength();
auto buffer = manifest->getIncFsBuffer(true /* aligned */).convert<uint8_t>();
if (!buffer.verify(size)) {
return Error("failed to read entire %s", entry_path.c_str());
}
return XmlParser::Create(buffer.unsafe_ptr(), size, true /* copyData */);
}
Result<XmlParser> OpenXmlParser(ResourceId id, const ZipAssetsProvider* zip,
const AssetManager2* am) {
const auto ref_table = am->GetDynamicRefTableForCookie(0);
if (ref_table == nullptr) {
return Error("failed to find dynamic ref table for cookie 0");
}
ref_table->lookupResourceId(&id);
auto value = am->GetResource(id);
if (!value.has_value()) {
return Error("failed to find resource for id 0x%08x", id);
}
if (value->type != Res_value::TYPE_STRING) {
return Error("resource for is 0x%08x is not a file", id);
}
auto string_pool = am->GetStringPoolForCookie(value->cookie);
auto file = string_pool->string8ObjectAt(value->data);
if (!file.has_value()) {
return Error("failed to find string for index %d", value->data);
}
return OpenXmlParser(file->c_str(), zip);
}
Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const ZipAssetsProvider* zip,
const std::string& name) {
Result<XmlParser> xml = OpenXmlParser("AndroidManifest.xml", zip);
if (!xml) {
return xml.GetError();
}
auto manifest_it = xml->tree_iterator();
if (manifest_it->event() != XmlParser::Event::START_TAG || manifest_it->name() != "manifest") {
return Error("root element tag is not <manifest> in AndroidManifest.xml");
}
for (auto&& it : manifest_it) {
if (it.event() != XmlParser::Event::START_TAG || it.name() != "overlay") {
continue;
}
OverlayManifestInfo info{};
if (auto result_str = it.GetAttributeStringValue(kAttrName, "android:name")) {
if (*result_str != name) {
// A value for android:name was found, but either the name does not match the requested
// name, or an <overlay> tag with no name was requested.
continue;
}
info.name = *result_str;
} else if (!name.empty()) {
// This tag does not have a value for android:name, but an <overlay> tag with a specific name
// has been requested.
continue;
}
if (auto result_str = it.GetAttributeStringValue(kAttrTargetPackage, "android:targetPackage")) {
info.target_package = *result_str;
} else {
return Error("android:targetPackage missing from <overlay> in AndroidManifest.xml");
}
if (auto result_str = it.GetAttributeStringValue(kAttrTargetName, "android:targetName")) {
info.target_name = *result_str;
}
if (auto result_value = it.GetAttributeValue(kAttrResourcesMap, "android:resourcesMap")) {
if (utils::IsReference((*result_value).dataType)) {
info.resource_mapping = (*result_value).data;
} else {
return Error("android:resourcesMap is not a reference in AndroidManifest.xml");
}
}
return info;
}
return Error("<overlay> with android:name \"%s\" missing from AndroidManifest.xml", name.c_str());
}
Result<OverlayData> CreateResourceMapping(ResourceId id, const ZipAssetsProvider* zip,
const AssetManager2* overlay_am,
const LoadedArsc* overlay_arsc,
const LoadedPackage* overlay_package) {
auto parser = OpenXmlParser(id, zip, overlay_am);
if (!parser) {
return parser.GetError();
}
OverlayData overlay_data{};
const uint32_t string_pool_offset = overlay_arsc->GetStringPool()->size();
const uint8_t package_id = overlay_package->GetPackageId();
auto root_it = parser->tree_iterator();
if (root_it->event() != XmlParser::Event::START_TAG || root_it->name() != "overlay") {
return Error("root element is not <overlay> tag");
}
auto overlay_it_end = root_it.end();
for (auto overlay_it = root_it.begin(); overlay_it != overlay_it_end; ++overlay_it) {
if (overlay_it->event() == XmlParser::Event::BAD_DOCUMENT) {
return Error("failed to parse overlay xml document");
}
if (overlay_it->event() != XmlParser::Event::START_TAG) {
continue;
}
if (overlay_it->name() != "item") {
return Error("unexpected tag <%s> in <overlay>", overlay_it->name().c_str());
}
Result<std::string> target_resource = overlay_it->GetAttributeStringValue("target");
if (!target_resource) {
return Error(R"(<item> tag missing expected attribute "target")");
}
Result<android::Res_value> overlay_resource = overlay_it->GetAttributeValue("value");
if (!overlay_resource) {
return Error(R"(<item> tag missing expected attribute "value")");
}
if (overlay_resource->dataType == Res_value::TYPE_STRING) {
overlay_resource->data += string_pool_offset;
}
if (utils::IsReference(overlay_resource->dataType)) {
// Only rewrite resources defined within the overlay package to their corresponding target
// resource ids at runtime.
bool rewrite_id = package_id == EXTRACT_PACKAGE(overlay_resource->data);
overlay_data.pairs.emplace_back(OverlayData::Value{
*target_resource, OverlayData::ResourceIdValue{overlay_resource->data, rewrite_id}});
} else {
overlay_data.pairs.emplace_back(
OverlayData::Value{*target_resource, TargetValue{.data_type = overlay_resource->dataType,
.data_value = overlay_resource->data}});
}
}
const auto& string_pool = parser->get_strings();
const uint32_t string_pool_data_length = string_pool.bytes();
overlay_data.string_pool_data = OverlayData::InlineStringPoolData{
.data = std::unique_ptr<uint8_t[]>(new uint8_t[string_pool_data_length]),
.data_length = string_pool_data_length,
.string_pool_offset = string_pool_offset,
};
// Overlays should not be incrementally installed, so calling unsafe_ptr is fine here.
memcpy(overlay_data.string_pool_data->data.get(), string_pool.data().unsafe_ptr(),
string_pool_data_length);
return overlay_data;
}
OverlayData CreateResourceMappingLegacy(const AssetManager2* overlay_am,
const LoadedPackage* overlay_package) {
OverlayData overlay_data{};
for (const ResourceId overlay_resid : *overlay_package) {
if (auto name = utils::ResToTypeEntryName(*overlay_am, overlay_resid)) {
// Disable rewriting. Overlays did not support internal references before
// android:resourcesMap. Do not introduce new behavior.
overlay_data.pairs.emplace_back(OverlayData::Value{
*name, OverlayData::ResourceIdValue{overlay_resid, false /* rewrite_id */}});
}
}
return overlay_data;
}
struct ResState {
std::unique_ptr<ApkAssets> apk_assets;
const LoadedArsc* arsc;
const LoadedPackage* package;
std::unique_ptr<AssetManager2> am;
ZipAssetsProvider* zip_assets;
static Result<ResState> Initialize(std::unique_ptr<ZipAssetsProvider> zip) {
ResState state;
state.zip_assets = zip.get();
if ((state.apk_assets = ApkAssets::Load(std::move(zip))) == nullptr) {
return Error("failed to load apk asset");
}
if ((state.arsc = state.apk_assets->GetLoadedArsc()) == nullptr) {
return Error("failed to retrieve loaded arsc");
}
if ((state.package = GetPackageAtIndex0(state.arsc)) == nullptr) {
return Error("failed to retrieve loaded package at index 0");
}
state.am = std::make_unique<AssetManager2>();
if (!state.am->SetApkAssets({state.apk_assets.get()})) {
return Error("failed to create asset manager");
}
return state;
}
};
} // namespace
struct ApkResourceContainer : public TargetResourceContainer, public OverlayResourceContainer {
static Result<std::unique_ptr<ApkResourceContainer>> FromPath(const std::string& path);
// inherited from TargetResourceContainer
Result<bool> DefinesOverlayable() const override;
Result<const android::OverlayableInfo*> GetOverlayableInfo(ResourceId id) const override;
Result<ResourceId> GetResourceId(const std::string& name) const override;
// inherited from OverlayResourceContainer
Result<OverlayData> GetOverlayData(const OverlayManifestInfo& info) const override;
Result<OverlayManifestInfo> FindOverlayInfo(const std::string& name) const override;
// inherited from ResourceContainer
Result<uint32_t> GetCrc() const override;
Result<std::string> GetResourceName(ResourceId id) const override;
const std::string& GetPath() const override;
~ApkResourceContainer() override = default;
private:
ApkResourceContainer(std::unique_ptr<ZipAssetsProvider> zip_assets, std::string path);
Result<const ResState*> GetState() const;
ZipAssetsProvider* GetZipAssets() const;
mutable std::variant<std::unique_ptr<ZipAssetsProvider>, ResState> state_;
std::string path_;
};
ApkResourceContainer::ApkResourceContainer(std::unique_ptr<ZipAssetsProvider> zip_assets,
std::string path)
: state_(std::move(zip_assets)), path_(std::move(path)) {
}
Result<std::unique_ptr<ApkResourceContainer>> ApkResourceContainer::FromPath(
const std::string& path) {
auto zip_assets = ZipAssetsProvider::Create(path);
if (zip_assets == nullptr) {
return Error("failed to load zip assets");
}
return std::unique_ptr<ApkResourceContainer>(
new ApkResourceContainer(std::move(zip_assets), path));
}
Result<const ResState*> ApkResourceContainer::GetState() const {
if (auto state = std::get_if<ResState>(&state_); state != nullptr) {
return state;
}
auto state =
ResState::Initialize(std::move(std::get<std::unique_ptr<ZipAssetsProvider>>(state_)));
if (!state) {
return state.GetError();
}
state_ = std::move(*state);
return &std::get<ResState>(state_);
}
ZipAssetsProvider* ApkResourceContainer::GetZipAssets() const {
if (auto zip = std::get_if<std::unique_ptr<ZipAssetsProvider>>(&state_); zip != nullptr) {
return zip->get();
}
return std::get<ResState>(state_).zip_assets;
}
Result<bool> ApkResourceContainer::DefinesOverlayable() const {
auto state = GetState();
if (!state) {
return state.GetError();
}
return (*state)->package->DefinesOverlayable();
}
Result<const android::OverlayableInfo*> ApkResourceContainer::GetOverlayableInfo(
ResourceId id) const {
auto state = GetState();
if (!state) {
return state.GetError();
}
return (*state)->package->GetOverlayableInfo(id);
}
Result<OverlayManifestInfo> ApkResourceContainer::FindOverlayInfo(const std::string& name) const {
return ExtractOverlayManifestInfo(GetZipAssets(), name);
}
Result<OverlayData> ApkResourceContainer::GetOverlayData(const OverlayManifestInfo& info) const {
const auto state = GetState();
if (!state) {
return state.GetError();
}
if (info.resource_mapping != 0) {
return CreateResourceMapping(info.resource_mapping, GetZipAssets(), (*state)->am.get(),
(*state)->arsc, (*state)->package);
}
return CreateResourceMappingLegacy((*state)->am.get(), (*state)->package);
}
Result<uint32_t> ApkResourceContainer::GetCrc() const {
return CalculateCrc(GetZipAssets());
}
const std::string& ApkResourceContainer::GetPath() const {
return path_;
}
Result<ResourceId> ApkResourceContainer::GetResourceId(const std::string& name) const {
auto state = GetState();
if (!state) {
return state.GetError();
}
auto id = (*state)->am->GetResourceId(name, "", (*state)->package->GetPackageName());
if (!id.has_value()) {
return Error("failed to find resource '%s'", name.c_str());
}
// Retrieve the compile-time resource id of the target resource.
return REWRITE_PACKAGE(*id, (*state)->package->GetPackageId());
}
Result<std::string> ApkResourceContainer::GetResourceName(ResourceId id) const {
auto state = GetState();
if (!state) {
return state.GetError();
}
return utils::ResToTypeEntryName(*(*state)->am, id);
}
Result<std::unique_ptr<TargetResourceContainer>> TargetResourceContainer::FromPath(
std::string path) {
auto result = ApkResourceContainer::FromPath(path);
if (!result) {
return result.GetError();
}
return std::unique_ptr<TargetResourceContainer>(result->release());
}
Result<std::unique_ptr<OverlayResourceContainer>> OverlayResourceContainer::FromPath(
std::string path) {
// Load the path as a fabricated overlay if the file magic indicates this is a fabricated overlay.
if (android::IsFabricatedOverlay(path)) {
auto result = FabricatedOverlayContainer::FromPath(path);
if (!result) {
return result.GetError();
}
return std::unique_ptr<OverlayResourceContainer>(result->release());
}
// Fallback to loading the container as an APK.
auto result = ApkResourceContainer::FromPath(path);
if (!result) {
return result.GetError();
}
return std::unique_ptr<OverlayResourceContainer>(result->release());
}
} // namespace android::idmap2

View File

@@ -30,19 +30,12 @@
using android::base::StringPrintf;
using android::idmap2::utils::BitmaskToPolicies;
using android::idmap2::utils::IsReference;
using android::idmap2::utils::ResToTypeEntryName;
using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
namespace android::idmap2 {
namespace {
#define REWRITE_PACKAGE(resid, package_id) \
(((resid)&0x00ffffffU) | (((uint32_t)(package_id)) << 24U))
#define EXTRACT_PACKAGE(resid) ((0xff000000 & (resid)) >> 24)
std::string ConcatPolicies(const std::vector<std::string>& policies) {
std::string message;
for (const std::string& policy : policies) {
@@ -55,11 +48,11 @@ std::string ConcatPolicies(const std::vector<std::string>& policies) {
return message;
}
Result<Unit> CheckOverlayable(const LoadedPackage& target_package,
Result<Unit> CheckOverlayable(const TargetResourceContainer& target,
const OverlayManifestInfo& overlay_info,
const PolicyBitmask& fulfilled_policies,
const ResourceId& target_resource) {
static constexpr const PolicyBitmask sDefaultPolicies =
constexpr const PolicyBitmask kDefaultPolicies =
PolicyFlags::ODM_PARTITION | PolicyFlags::OEM_PARTITION | PolicyFlags::SYSTEM_PARTITION |
PolicyFlags::VENDOR_PARTITION | PolicyFlags::PRODUCT_PARTITION | PolicyFlags::SIGNATURE |
PolicyFlags::CONFIG_SIGNATURE;
@@ -68,8 +61,13 @@ Result<Unit> CheckOverlayable(const LoadedPackage& target_package,
// the overlay is preinstalled, signed with the same signature as the target or signed with the
// same signature as reference package defined in SystemConfig under 'overlay-config-signature'
// tag.
if (!target_package.DefinesOverlayable()) {
return (sDefaultPolicies & fulfilled_policies) != 0
const Result<bool> defines_overlayable = target.DefinesOverlayable();
if (!defines_overlayable) {
return Error(defines_overlayable.GetError(), "unable to retrieve overlayable info");
}
if (!*defines_overlayable) {
return (kDefaultPolicies & fulfilled_policies) != 0
? Result<Unit>({})
: Error(
"overlay must be preinstalled, signed with the same signature as the target,"
@@ -77,367 +75,112 @@ Result<Unit> CheckOverlayable(const LoadedPackage& target_package,
" <overlay-config-signature>.");
}
const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(target_resource);
if (overlayable_info == nullptr) {
const auto overlayable_info = target.GetOverlayableInfo(target_resource);
if (!overlayable_info) {
return overlayable_info.GetError();
}
if (*overlayable_info == nullptr) {
// Do not allow non-overlayable resources to be overlaid.
return Error("target resource has no overlayable declaration");
}
if (overlay_info.target_name != overlayable_info->name) {
if (overlay_info.target_name != (*overlayable_info)->name) {
// If the overlay supplies a target overlayable name, the resource must belong to the
// overlayable defined with the specified name to be overlaid.
return Error(R"(<overlay> android:targetName "%s" does not match overlayable name "%s")",
overlay_info.target_name.c_str(), overlayable_info->name.c_str());
overlay_info.target_name.c_str(), (*overlayable_info)->name.c_str());
}
// Enforce policy restrictions if the resource is declared as overlayable.
if ((overlayable_info->policy_flags & fulfilled_policies) == 0) {
if (((*overlayable_info)->policy_flags & fulfilled_policies) == 0) {
return Error(R"(overlay with policies "%s" does not fulfill any overlayable policies "%s")",
ConcatPolicies(BitmaskToPolicies(fulfilled_policies)).c_str(),
ConcatPolicies(BitmaskToPolicies(overlayable_info->policy_flags)).c_str());
ConcatPolicies(BitmaskToPolicies((*overlayable_info)->policy_flags)).c_str());
}
return Result<Unit>({});
}
// TODO(martenkongstad): scan for package name instead of assuming package at index 0
//
// idmap version 0x01 naively assumes that the package to use is always the first ResTable_package
// in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so
// this assumption tends to work out. That said, the correct thing to do is to scan
// resources.arsc for a package with a given name as read from the package manifest instead of
// relying on a hard-coded index. This however requires storing the package name in the idmap
// header, which in turn requires incrementing the idmap version. Because the initial version of
// idmap2 is compatible with idmap, this will have to wait for now.
const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) {
const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc.GetPackages();
if (packages.empty()) {
return nullptr;
std::string GetDebugResourceName(const ResourceContainer& container, ResourceId resid) {
auto name = container.GetResourceName(resid);
if (name) {
return *name;
}
int id = packages[0]->GetPackageId();
return loaded_arsc.GetPackageById(id);
return StringPrintf("0x%08x", resid);
}
Result<std::unique_ptr<Asset>> OpenNonAssetFromResource(const ResourceId& resource_id,
const AssetManager2& asset_manager) {
auto value = asset_manager.GetResource(resource_id);
if (!value.has_value()) {
return Error("failed to find resource for id 0x%08x", resource_id);
}
if (value->type != Res_value::TYPE_STRING) {
return Error("resource for is 0x%08x is not a file", resource_id);
}
auto string_pool = asset_manager.GetStringPoolForCookie(value->cookie);
auto file = string_pool->string8ObjectAt(value->data);
if (!file.has_value()) {
return Error("failed to find string for index %d", value->data);
}
// Load the overlay resource mappings from the file specified using android:resourcesMap.
auto asset = asset_manager.OpenNonAsset(file->c_str(), Asset::AccessMode::ACCESS_BUFFER);
if (asset == nullptr) {
return Error("file \"%s\" not found", file->c_str());
}
return asset;
}
} // namespace
Result<ResourceMapping> ResourceMapping::CreateResourceMapping(const AssetManager2* target_am,
const LoadedPackage* target_package,
const LoadedPackage* overlay_package,
size_t string_pool_offset,
const XmlParser& overlay_parser,
LogInfo& log_info) {
ResourceMapping resource_mapping;
auto root_it = overlay_parser.tree_iterator();
if (root_it->event() != XmlParser::Event::START_TAG || root_it->name() != "overlay") {
return Error("root element is not <overlay> tag");
Result<ResourceMapping> ResourceMapping::FromContainers(const TargetResourceContainer& target,
const OverlayResourceContainer& overlay,
const OverlayManifestInfo& overlay_info,
const PolicyBitmask& fulfilled_policies,
bool enforce_overlayable,
LogInfo& log_info) {
auto overlay_data = overlay.GetOverlayData(overlay_info);
if (!overlay_data) {
return overlay_data.GetError();
}
const uint8_t target_package_id = target_package->GetPackageId();
const uint8_t overlay_package_id = overlay_package->GetPackageId();
auto overlay_it_end = root_it.end();
for (auto overlay_it = root_it.begin(); overlay_it != overlay_it_end; ++overlay_it) {
if (overlay_it->event() == XmlParser::Event::BAD_DOCUMENT) {
return Error("failed to parse overlay xml document");
}
if (overlay_it->event() != XmlParser::Event::START_TAG) {
ResourceMapping mapping;
for (const auto& overlay_pair : overlay_data->pairs) {
const auto target_resid = target.GetResourceId(overlay_pair.resource_name);
if (!target_resid) {
log_info.Warning(LogMessage() << target_resid.GetErrorMessage());
continue;
}
if (overlay_it->name() != "item") {
return Error("unexpected tag <%s> in <overlay>", overlay_it->name().c_str());
if (enforce_overlayable) {
// Filter out resources the overlay is not allowed to override.
auto overlayable = CheckOverlayable(target, overlay_info, fulfilled_policies, *target_resid);
if (!overlayable) {
log_info.Warning(LogMessage() << "overlay '" << overlay.GetPath()
<< "' is not allowed to overlay resource '"
<< GetDebugResourceName(target, *target_resid)
<< "' in target: " << overlayable.GetErrorMessage());
continue;
}
}
Result<std::string> target_resource = overlay_it->GetAttributeStringValue("target");
if (!target_resource) {
return Error(R"(<item> tag missing expected attribute "target")");
}
Result<android::Res_value> overlay_resource = overlay_it->GetAttributeValue("value");
if (!overlay_resource) {
return Error(R"(<item> tag missing expected attribute "value")");
}
auto target_id_result =
target_am->GetResourceId(*target_resource, "", target_package->GetPackageName());
if (!target_id_result.has_value()) {
log_info.Warning(LogMessage() << "failed to find resource \"" << *target_resource
<< "\" in target resources");
continue;
}
// Retrieve the compile-time resource id of the target resource.
uint32_t target_id = REWRITE_PACKAGE(*target_id_result, target_package_id);
if (overlay_resource->dataType == Res_value::TYPE_STRING) {
overlay_resource->data += string_pool_offset;
}
if (IsReference(overlay_resource->dataType)) {
// Only rewrite resources defined within the overlay package to their corresponding target
// resource ids at runtime.
bool rewrite_reference = overlay_package_id == EXTRACT_PACKAGE(overlay_resource->data);
resource_mapping.AddMapping(target_id, overlay_resource->data, rewrite_reference);
} else {
resource_mapping.AddMapping(target_id, overlay_resource->dataType, overlay_resource->data);
if (auto result = mapping.AddMapping(*target_resid, overlay_pair.value); !result) {
return Error(result.GetError(), "failed to add mapping for '%s'",
GetDebugResourceName(target, *target_resid).c_str());
}
}
return resource_mapping;
auto& string_pool_data = overlay_data->string_pool_data;
if (string_pool_data.has_value()) {
mapping.string_pool_offset_ = string_pool_data->string_pool_offset;
mapping.string_pool_data_ = std::move(string_pool_data->data);
mapping.string_pool_data_length_ = string_pool_data->data_length;
}
return std::move(mapping);
}
Result<ResourceMapping> ResourceMapping::CreateResourceMappingLegacy(
const AssetManager2* target_am, const AssetManager2* overlay_am,
const LoadedPackage* target_package, const LoadedPackage* overlay_package, LogInfo& log_info) {
ResourceMapping resource_mapping;
const uint8_t target_package_id = target_package->GetPackageId();
const auto end = overlay_package->end();
for (auto iter = overlay_package->begin(); iter != end; ++iter) {
const ResourceId overlay_resid = *iter;
Result<std::string> name = utils::ResToTypeEntryName(*overlay_am, overlay_resid);
if (!name) {
continue;
Result<Unit> ResourceMapping::AddMapping(
ResourceId target_resource,
const std::variant<OverlayData::ResourceIdValue, TargetValue>& value) {
if (target_map_.find(target_resource) != target_map_.end()) {
return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
}
// TODO(141485591): Ensure that the overlay type is compatible with the target type. If the
// runtime types are not compatible, it could cause runtime crashes when the resource is resolved.
if (auto overlay_resource = std::get_if<OverlayData::ResourceIdValue>(&value)) {
target_map_.insert(std::make_pair(target_resource, overlay_resource->overlay_id));
if (overlay_resource->rewrite_id) {
// An overlay resource can override multiple target resources at once. Rewrite the overlay
// resource as the first target resource it overrides.
overlay_map_.insert(std::make_pair(overlay_resource->overlay_id, target_resource));
}
// Find the resource with the same type and entry name within the target package.
const std::string full_name =
base::StringPrintf("%s:%s", target_package->GetPackageName().c_str(), name->c_str());
auto target_resource_result = target_am->GetResourceId(full_name);
if (!target_resource_result.has_value()) {
log_info.Warning(LogMessage()
<< "failed to find resource \"" << full_name << "\" in target resources");
continue;
}
// Retrieve the compile-time resource id of the target resource.
ResourceId target_resource = REWRITE_PACKAGE(*target_resource_result, target_package_id);
resource_mapping.AddMapping(target_resource, overlay_resid,
false /* rewrite_overlay_reference */);
}
return resource_mapping;
}
void ResourceMapping::FilterOverlayableResources(const AssetManager2* target_am,
const LoadedPackage* target_package,
const LoadedPackage* overlay_package,
const OverlayManifestInfo& overlay_info,
const PolicyBitmask& fulfilled_policies,
LogInfo& log_info) {
std::set<ResourceId> remove_ids;
for (const auto& target_map : target_map_) {
const ResourceId target_resid = target_map.first;
Result<Unit> success =
CheckOverlayable(*target_package, overlay_info, fulfilled_policies, target_resid);
if (success) {
continue;
}
// Attempting to overlay a resource that is not allowed to be overlaid is treated as a
// warning.
Result<std::string> name = utils::ResToTypeEntryName(*target_am, target_resid);
if (!name) {
name = StringPrintf("0x%08x", target_resid);
}
log_info.Warning(LogMessage() << "overlay \"" << overlay_package->GetPackageName()
<< "\" is not allowed to overlay resource \"" << *name
<< "\" in target: " << success.GetErrorMessage());
remove_ids.insert(target_resid);
}
for (const ResourceId target_resid : remove_ids) {
RemoveMapping(target_resid);
}
}
Result<ResourceMapping> ResourceMapping::FromApkAssets(const ApkAssets& target_apk_assets,
const ApkAssets& overlay_apk_assets,
const OverlayManifestInfo& overlay_info,
const PolicyBitmask& fulfilled_policies,
bool enforce_overlayable,
LogInfo& log_info) {
AssetManager2 target_asset_manager;
if (!target_asset_manager.SetApkAssets({&target_apk_assets})) {
return Error("failed to create target asset manager");
}
AssetManager2 overlay_asset_manager;
if (!overlay_asset_manager.SetApkAssets({&overlay_apk_assets})) {
return Error("failed to create overlay asset manager");
}
const LoadedArsc* target_arsc = target_apk_assets.GetLoadedArsc();
if (target_arsc == nullptr) {
return Error("failed to load target resources.arsc");
}
const LoadedArsc* overlay_arsc = overlay_apk_assets.GetLoadedArsc();
if (overlay_arsc == nullptr) {
return Error("failed to load overlay resources.arsc");
}
const LoadedPackage* target_pkg = GetPackageAtIndex0(*target_arsc);
if (target_pkg == nullptr) {
return Error("failed to load target package from resources.arsc");
}
const LoadedPackage* overlay_pkg = GetPackageAtIndex0(*overlay_arsc);
if (overlay_pkg == nullptr) {
return Error("failed to load overlay package from resources.arsc");
}
size_t string_pool_data_length = 0U;
size_t string_pool_offset = 0U;
std::unique_ptr<uint8_t[]> string_pool_data;
Result<ResourceMapping> resource_mapping = {{}};
if (overlay_info.resource_mapping != 0U) {
// Use the dynamic reference table to find the assigned resource id of the map xml.
const auto& ref_table = overlay_asset_manager.GetDynamicRefTableForCookie(0);
uint32_t resource_mapping_id = overlay_info.resource_mapping;
ref_table->lookupResourceId(&resource_mapping_id);
// Load the overlay resource mappings from the file specified using android:resourcesMap.
auto asset = OpenNonAssetFromResource(resource_mapping_id, overlay_asset_manager);
if (!asset) {
return Error("failed opening xml for android:resourcesMap: %s",
asset.GetErrorMessage().c_str());
}
auto parser =
XmlParser::Create((*asset)->getBuffer(true /* wordAligned*/), (*asset)->getLength());
if (!parser) {
return Error("failed opening ResXMLTree");
}
// Copy the xml string pool data before the parse goes out of scope.
auto& string_pool = (*parser)->get_strings();
string_pool_data_length = string_pool.bytes();
string_pool_data.reset(new uint8_t[string_pool_data_length]);
// Overlays should not be incrementally installed, so calling unsafe_ptr is fine here.
memcpy(string_pool_data.get(), string_pool.data().unsafe_ptr(), string_pool_data_length);
// Offset string indices by the size of the overlay resource table string pool.
string_pool_offset = overlay_arsc->GetStringPool()->size();
resource_mapping = CreateResourceMapping(&target_asset_manager, target_pkg, overlay_pkg,
string_pool_offset, *(*parser), log_info);
} else {
// If no file is specified using android:resourcesMap, it is assumed that the overlay only
// defines resources intended to override target resources of the same type and name.
resource_mapping = CreateResourceMappingLegacy(&target_asset_manager, &overlay_asset_manager,
target_pkg, overlay_pkg, log_info);
auto overlay_value = std::get<TargetValue>(value);
target_map_.insert(std::make_pair(target_resource, overlay_value));
}
if (!resource_mapping) {
return resource_mapping.GetError();
}
if (enforce_overlayable) {
// Filter out resources the overlay is not allowed to override.
(*resource_mapping)
.FilterOverlayableResources(&target_asset_manager, target_pkg, overlay_pkg, overlay_info,
fulfilled_policies, log_info);
}
resource_mapping->target_package_id_ = target_pkg->GetPackageId();
resource_mapping->overlay_package_id_ = overlay_pkg->GetPackageId();
resource_mapping->string_pool_offset_ = string_pool_offset;
resource_mapping->string_pool_data_ = std::move(string_pool_data);
resource_mapping->string_pool_data_length_ = string_pool_data_length;
return std::move(*resource_mapping);
}
OverlayResourceMap ResourceMapping::GetOverlayToTargetMap() const {
// An overlay resource can override multiple target resources at once. Rewrite the overlay
// resource as the first target resource it overrides.
OverlayResourceMap map;
for (const auto& mappings : overlay_map_) {
map.insert(std::make_pair(mappings.first, mappings.second));
}
return map;
}
Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource, ResourceId overlay_resource,
bool rewrite_overlay_reference) {
if (target_map_.find(target_resource) != target_map_.end()) {
return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
}
// TODO(141485591): Ensure that the overlay type is compatible with the target type. If the
// runtime types are not compatible, it could cause runtime crashes when the resource is resolved.
target_map_.insert(std::make_pair(target_resource, overlay_resource));
if (rewrite_overlay_reference) {
overlay_map_.insert(std::make_pair(overlay_resource, target_resource));
}
return Unit{};
}
Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource,
TargetValue::DataType data_type,
TargetValue::DataValue data_value) {
if (target_map_.find(target_resource) != target_map_.end()) {
return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
}
// TODO(141485591): Ensure that the overlay type is compatible with the target type. If the
// runtime types are not compatible, it could cause runtime crashes when the resource is resolved.
target_map_.insert(std::make_pair(target_resource, TargetValue{data_type, data_value}));
return Unit{};
}
void ResourceMapping::RemoveMapping(ResourceId target_resource) {
auto target_iter = target_map_.find(target_resource);
if (target_iter == target_map_.end()) {
return;
}
const auto value = target_iter->second;
target_map_.erase(target_iter);
const ResourceId* overlay_resource = std::get_if<ResourceId>(&value);
if (overlay_resource == nullptr) {
return;
}
auto overlay_iter = overlay_map_.equal_range(*overlay_resource);
for (auto i = overlay_iter.first; i != overlay_iter.second; ++i) {
if (i->second == target_resource) {
overlay_map_.erase(i);
return;
}
}
}
} // namespace android::idmap2

View File

@@ -17,27 +17,16 @@
#include "idmap2/ResourceUtils.h"
#include <memory>
#include <string>
#include "androidfw/StringPiece.h"
#include "androidfw/Util.h"
#include "idmap2/Result.h"
#include "idmap2/XmlParser.h"
#include "idmap2/ZipFile.h"
using android::StringPiece16;
using android::idmap2::Result;
using android::idmap2::XmlParser;
using android::idmap2::ZipFile;
using android::util::Utf16ToUtf8;
namespace android::idmap2::utils {
namespace {
constexpr ResourceId kAttrName = 0x01010003;
constexpr ResourceId kAttrResourcesMap = 0x01010609;
constexpr ResourceId kAttrTargetName = 0x0101044d;
constexpr ResourceId kAttrTargetPackage = 0x01010021;
} // namespace
bool IsReference(uint8_t data_type) {
return data_type == Res_value::TYPE_REFERENCE || data_type == Res_value::TYPE_DYNAMIC_REFERENCE;
@@ -97,71 +86,4 @@ Result<std::string> ResToTypeEntryName(const AssetManager2& am, uint32_t resid)
return out;
}
Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path,
const std::string& name) {
std::unique_ptr<const ZipFile> zip = ZipFile::Open(path);
if (!zip) {
return Error("failed to open %s as a zip file", path.c_str());
}
std::unique_ptr<const MemoryChunk> entry = zip->Uncompress("AndroidManifest.xml");
if (!entry) {
return Error("failed to uncompress AndroidManifest.xml from %s", path.c_str());
}
Result<std::unique_ptr<const XmlParser>> xml = XmlParser::Create(entry->buf, entry->size);
if (!xml) {
return Error("failed to parse AndroidManifest.xml from %s", path.c_str());
}
auto manifest_it = (*xml)->tree_iterator();
if (manifest_it->event() != XmlParser::Event::START_TAG || manifest_it->name() != "manifest") {
return Error("root element tag is not <manifest> in AndroidManifest.xml of %s", path.c_str());
}
for (auto&& it : manifest_it) {
if (it.event() != XmlParser::Event::START_TAG || it.name() != "overlay") {
continue;
}
OverlayManifestInfo info{};
if (auto result_str = it.GetAttributeStringValue(kAttrName, "android:name")) {
if (*result_str != name) {
// A value for android:name was found, but either a the name does not match the requested
// name, or an <overlay> tag with no name was requested.
continue;
}
info.name = *result_str;
} else if (!name.empty()) {
// This tag does not have a value for android:name, but an <overlay> tag with a specific name
// has been requested.
continue;
}
if (auto result_str = it.GetAttributeStringValue(kAttrTargetPackage, "android:targetPackage")) {
info.target_package = *result_str;
} else {
return Error("android:targetPackage missing from <overlay> of %s: %s", path.c_str(),
result_str.GetErrorMessage().c_str());
}
if (auto result_str = it.GetAttributeStringValue(kAttrTargetName, "android:targetName")) {
info.target_name = *result_str;
}
if (auto result_value = it.GetAttributeValue(kAttrResourcesMap, "android:resourcesMap")) {
if (IsReference((*result_value).dataType)) {
info.resource_mapping = (*result_value).data;
} else {
return Error("android:resourcesMap is not a reference in AndroidManifest.xml of %s",
path.c_str());
}
}
return info;
}
return Error("<overlay> with android:name \"%s\" missing from AndroidManifest.xml of %s",
name.c_str(), path.c_str());
}
} // namespace android::idmap2::utils

View File

@@ -151,16 +151,18 @@ Result<std::string> XmlParser::Node::GetAttributeStringValue(const std::string&
return value ? GetStringValue(parser_, *value, name) : value.GetError();
}
Result<std::unique_ptr<const XmlParser>> XmlParser::Create(const void* data, size_t size,
bool copy_data) {
auto parser = std::unique_ptr<const XmlParser>(new XmlParser());
if (parser->tree_.setTo(data, size, copy_data) != NO_ERROR) {
XmlParser::XmlParser(std::unique_ptr<ResXMLTree> tree) : tree_(std::move(tree)) {
}
Result<XmlParser> XmlParser::Create(const void* data, size_t size, bool copy_data) {
auto tree = std::make_unique<ResXMLTree>();
if (tree->setTo(data, size, copy_data) != NO_ERROR) {
return Error("Malformed xml block");
}
// Find the beginning of the first tag.
XmlParser::Event event;
while ((event = parser->tree_.next()) != XmlParser::Event::BAD_DOCUMENT &&
while ((event = tree->next()) != XmlParser::Event::BAD_DOCUMENT &&
event != XmlParser::Event::END_DOCUMENT && event != XmlParser::Event::START_TAG) {
}
@@ -172,11 +174,7 @@ Result<std::unique_ptr<const XmlParser>> XmlParser::Create(const void* data, siz
return Error("Bad xml document");
}
return parser;
}
XmlParser::~XmlParser() {
tree_.uninit();
return XmlParser{std::move(tree)};
}
} // namespace android::idmap2

View File

@@ -1,70 +0,0 @@
/*
* Copyright (C) 2018 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 "idmap2/ZipFile.h"
#include <memory>
#include <string>
#include "idmap2/Result.h"
namespace android::idmap2 {
std::unique_ptr<MemoryChunk> MemoryChunk::Allocate(size_t size) {
void* ptr = ::operator new(sizeof(MemoryChunk) + size);
std::unique_ptr<MemoryChunk> chunk(reinterpret_cast<MemoryChunk*>(ptr));
chunk->size = size;
return chunk;
}
std::unique_ptr<const ZipFile> ZipFile::Open(const std::string& path) {
::ZipArchiveHandle handle;
int32_t status = ::OpenArchive(path.c_str(), &handle);
if (status != 0) {
::CloseArchive(handle);
return nullptr;
}
return std::unique_ptr<ZipFile>(new ZipFile(handle));
}
ZipFile::~ZipFile() {
::CloseArchive(handle_);
}
std::unique_ptr<const MemoryChunk> ZipFile::Uncompress(const std::string& entryPath) const {
::ZipEntry entry;
int32_t status = ::FindEntry(handle_, entryPath, &entry);
if (status != 0) {
return nullptr;
}
std::unique_ptr<MemoryChunk> chunk = MemoryChunk::Allocate(entry.uncompressed_length);
status = ::ExtractToMemory(handle_, &entry, chunk->buf, chunk->size);
if (status != 0) {
return nullptr;
}
return chunk;
}
Result<uint32_t> ZipFile::Crc(const std::string& entryPath) const {
::ZipEntry entry;
int32_t status = ::FindEntry(handle_, entryPath, &entry);
if (status != 0) {
return Error("failed to find zip entry %s", entryPath.c_str());
}
return entry.crc32;
}
} // namespace android::idmap2

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2021 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.
*/
syntax = "proto3";
package android.idmap2.pb;
option optimize_for = LITE_RUNTIME;
// All changes to the proto messages in this file MUST be backwards compatible. Backwards
// incompatible changes will cause previously fabricated overlays to be considered corrupt by the
// new proto message specification.
message FabricatedOverlay {
repeated ResourcePackage packages = 1;
string name = 2;
string package_name = 3;
string target_package_name = 4;
string target_overlayable = 5;
}
message ResourcePackage {
string name = 1;
repeated ResourceType types = 2;
}
message ResourceType {
string name = 1;
repeated ResourceEntry entries = 2;
}
message ResourceEntry {
string name = 1;
oneof value {
ResourceValue res_value = 2;
}
}
message ResourceValue {
// Corresponds with android::Res_value::dataType
uint32 data_type = 1;
// Corresponds with android::Res_value::data
uint32 data_value = 2;
}

View File

@@ -33,7 +33,7 @@ using ::testing::NotNull;
namespace android::idmap2 {
TEST(BinaryStreamVisitorTests, CreateBinaryStreamViaBinaryStreamVisitor) {
std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen);
std::istringstream raw_stream(raw);
auto result1 = Idmap::FromBinaryStream(raw_stream);

View File

@@ -0,0 +1,138 @@
/*
* Copyright (C) 2021 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 <android-base/file.h>
#include <gtest/gtest.h>
#include <idmap2/FabricatedOverlay.h>
#include <fstream>
namespace android::idmap2 {
TEST(FabricatedOverlayTests, OverlayInfo) {
auto overlay =
FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target")
.SetOverlayable("TestResources")
.Build();
ASSERT_TRUE(overlay);
auto container = FabricatedOverlayContainer::FromOverlay(std::move(*overlay));
auto info = container->FindOverlayInfo("SandTheme");
ASSERT_TRUE(info);
EXPECT_EQ("SandTheme", (*info).name);
EXPECT_EQ("TestResources", (*info).target_name);
info = container->FindOverlayInfo("OceanTheme");
ASSERT_FALSE(info);
}
TEST(FabricatedOverlayTests, SetResourceValue) {
auto overlay =
FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target")
.SetResourceValue("com.example.target:integer/int1", Res_value::TYPE_INT_DEC, 1U)
.SetResourceValue("com.example.target.split:integer/int2", Res_value::TYPE_INT_DEC, 2U)
.SetResourceValue("string/int3", Res_value::TYPE_REFERENCE, 0x7f010000)
.Build();
ASSERT_TRUE(overlay);
auto container = FabricatedOverlayContainer::FromOverlay(std::move(*overlay));
auto info = container->FindOverlayInfo("SandTheme");
ASSERT_TRUE(info);
ASSERT_TRUE((*info).target_name.empty());
auto crc = (*container).GetCrc();
ASSERT_TRUE(crc) << crc.GetErrorMessage();
EXPECT_NE(0U, *crc);
auto pairs = container->GetOverlayData(*info);
ASSERT_TRUE(pairs);
EXPECT_FALSE(pairs->string_pool_data.has_value());
ASSERT_EQ(3U, pairs->pairs.size());
auto& it = pairs->pairs[0];
ASSERT_EQ("com.example.target:integer/int1", it.resource_name);
auto entry = std::get_if<TargetValue>(&it.value);
ASSERT_NE(nullptr, entry);
ASSERT_EQ(1U, entry->data_value);
ASSERT_EQ(Res_value::TYPE_INT_DEC, entry->data_type);
it = pairs->pairs[1];
ASSERT_EQ("com.example.target:string/int3", it.resource_name);
entry = std::get_if<TargetValue>(&it.value);
ASSERT_NE(nullptr, entry);
ASSERT_EQ(0x7f010000, entry->data_value);
ASSERT_EQ(Res_value::TYPE_REFERENCE, entry->data_type);
it = pairs->pairs[2];
ASSERT_EQ("com.example.target.split:integer/int2", it.resource_name);
entry = std::get_if<TargetValue>(&it.value);
ASSERT_NE(nullptr, entry);
ASSERT_EQ(2U, entry->data_value);
ASSERT_EQ(Res_value::TYPE_INT_DEC, entry->data_type);
}
TEST(FabricatedOverlayTests, SetResourceValueBadArgs) {
{
auto builder =
FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target")
.SetResourceValue("int1", Res_value::TYPE_INT_DEC, 1U);
ASSERT_FALSE(builder.Build());
}
{
auto builder =
FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target")
.SetResourceValue("com.example.target:int2", Res_value::TYPE_INT_DEC, 1U);
ASSERT_FALSE(builder.Build());
}
}
TEST(FabricatedOverlayTests, SerializeAndDeserialize) {
auto overlay =
FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target")
.SetOverlayable("TestResources")
.SetResourceValue("com.example.target:integer/int1", Res_value::TYPE_INT_DEC, 1U)
.Build();
ASSERT_TRUE(overlay);
TemporaryFile tf;
std::ofstream out(tf.path);
ASSERT_TRUE((*overlay).ToBinaryStream(out));
out.close();
auto container = OverlayResourceContainer::FromPath(tf.path);
ASSERT_TRUE(container) << container.GetErrorMessage();
EXPECT_EQ(tf.path, (*container)->GetPath());
auto crc = (*container)->GetCrc();
ASSERT_TRUE(crc) << crc.GetErrorMessage();
EXPECT_NE(0U, *crc);
auto info = (*container)->FindOverlayInfo("SandTheme");
ASSERT_TRUE(info) << info.GetErrorMessage();
EXPECT_EQ("SandTheme", (*info).name);
EXPECT_EQ("TestResources", (*info).target_name);
auto pairs = (*container)->GetOverlayData(*info);
ASSERT_TRUE(pairs) << pairs.GetErrorMessage();
EXPECT_EQ(1U, pairs->pairs.size());
auto& it = pairs->pairs[0];
ASSERT_EQ("com.example.target:integer/int1", it.resource_name);
auto entry = std::get_if<TargetValue>(&it.value);
ASSERT_NE(nullptr, entry);
EXPECT_EQ(1U, entry->data_value);
EXPECT_EQ(Res_value::TYPE_INT_DEC, entry->data_type);
}
} // namespace android::idmap2

View File

@@ -14,6 +14,8 @@
* limitations under the License.
*/
#include <android-base/file.h>
#include <cstdio> // fclose
#include <fstream>
#include <memory>
@@ -61,12 +63,12 @@ TEST(IdmapTests, TestCanonicalIdmapPathFor) {
}
TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) {
std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen);
std::istringstream stream(raw);
std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
ASSERT_THAT(header, NotNull());
ASSERT_EQ(header->GetMagic(), 0x504d4449U);
ASSERT_EQ(header->GetVersion(), 0x07U);
ASSERT_EQ(header->GetVersion(), 0x08U);
ASSERT_EQ(header->GetTargetCrc(), 0x1234U);
ASSERT_EQ(header->GetOverlayCrc(), 0x5678U);
ASSERT_EQ(header->GetFulfilledPolicies(), 0x11);
@@ -81,7 +83,7 @@ TEST(IdmapTests, IdmapFailParsingDifferentVersion) {
std::stringstream stream;
stream << android::kIdmapMagic;
stream << 0xffffffffU;
stream << std::string(kJunkSize, (char) 0xffU);
stream << std::string(kJunkSize, (char)0xffU);
ASSERT_FALSE(Idmap::FromBinaryStream(stream));
}
@@ -90,14 +92,13 @@ TEST(IdmapTests, IdmapFailParsingDifferentMagic) {
std::stringstream stream;
stream << 0xffffffffU;
stream << android::kIdmapCurrentVersion;
stream << std::string(kJunkSize, (char) 0xffU);
stream << std::string(kJunkSize, (char)0xffU);
ASSERT_FALSE(Idmap::FromBinaryStream(stream));
}
TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) {
const size_t offset = kIdmapRawDataOffset;
std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
kIdmapRawDataLen - offset);
std::string raw(reinterpret_cast<const char*>(kIdmapRawData + offset), kIdmapRawDataLen - offset);
std::istringstream stream(raw);
std::unique_ptr<const IdmapData::Header> header = IdmapData::Header::FromBinaryStream(stream);
@@ -108,8 +109,7 @@ TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) {
TEST(IdmapTests, CreateIdmapDataFromBinaryStream) {
const size_t offset = kIdmapRawDataOffset;
std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
kIdmapRawDataLen - offset);
std::string raw(reinterpret_cast<const char*>(kIdmapRawData + offset), kIdmapRawDataLen - offset);
std::istringstream stream(raw);
std::unique_ptr<const IdmapData> data = IdmapData::FromBinaryStream(stream);
@@ -134,7 +134,7 @@ TEST(IdmapTests, CreateIdmapDataFromBinaryStream) {
}
TEST(IdmapTests, CreateIdmapFromBinaryStream) {
std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen);
std::istringstream stream(raw);
auto result = Idmap::FromBinaryStream(stream);
@@ -143,7 +143,7 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) {
ASSERT_THAT(idmap->GetHeader(), NotNull());
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x07U);
ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x08U);
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234U);
ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678U);
ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), kIdmapRawDataPolicies);
@@ -177,7 +177,7 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) {
}
TEST(IdmapTests, GracefullyFailToCreateIdmapFromCorruptBinaryStream) {
std::string raw(reinterpret_cast<const char*>(idmap_raw_data),
std::string raw(reinterpret_cast<const char*>(kIdmapRawData),
10); // data too small
std::istringstream stream(raw);
@@ -189,14 +189,14 @@ TEST(IdmapTests, CreateIdmapHeaderFromApkAssets) {
std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk";
std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
ASSERT_THAT(target_apk, NotNull());
auto target = TargetResourceContainer::FromPath(target_apk_path);
ASSERT_TRUE(target);
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
ASSERT_TRUE(overlay);
auto idmap_result = Idmap::FromApkAssets(
*target_apk, *overlay_apk, TestConstants::OVERLAY_NAME_ALL_POLICIES, PolicyFlags::PUBLIC,
auto idmap_result = Idmap::FromContainers(
**target, **overlay, TestConstants::OVERLAY_NAME_ALL_POLICIES, PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
auto& idmap = *idmap_result;
@@ -204,7 +204,7 @@ TEST(IdmapTests, CreateIdmapHeaderFromApkAssets) {
ASSERT_THAT(idmap->GetHeader(), NotNull());
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x07U);
ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x08U);
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), android::idmap2::TestConstants::TARGET_CRC);
ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), android::idmap2::TestConstants::OVERLAY_CRC);
ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), PolicyFlags::PUBLIC);
@@ -218,15 +218,15 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssets) {
std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk";
std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
ASSERT_THAT(target_apk, NotNull());
auto target = TargetResourceContainer::FromPath(target_apk_path);
ASSERT_TRUE(target);
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
ASSERT_TRUE(overlay);
auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk,
TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
auto idmap_result = Idmap::FromContainers(
**target, **overlay, TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
auto& idmap = *idmap_result;
ASSERT_THAT(idmap, NotNull());
@@ -255,25 +255,66 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssets) {
ASSERT_OVERLAY_ENTRY(overlay_entries[3], R::overlay::string::str4, R::target::string::str4);
}
TEST(IdmapTests, FabricatedOverlay) {
std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
auto target = TargetResourceContainer::FromPath(target_apk_path);
ASSERT_TRUE(target);
auto frro = FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "test.target")
.SetOverlayable("TestResources")
.SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U)
.SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000)
.Build();
ASSERT_TRUE(frro);
TemporaryFile tf;
std::ofstream out(tf.path);
ASSERT_TRUE((*frro).ToBinaryStream(out));
out.close();
auto overlay = OverlayResourceContainer::FromPath(tf.path);
ASSERT_TRUE(overlay);
auto idmap_result = Idmap::FromContainers(**target, **overlay, "SandTheme", PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
auto& idmap = *idmap_result;
ASSERT_THAT(idmap, NotNull());
const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
ASSERT_EQ(dataBlocks.size(), 1U);
const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
ASSERT_THAT(data, NotNull());
ASSERT_EQ(data->GetTargetEntries().size(), 0U);
ASSERT_EQ(data->GetOverlayEntries().size(), 0U);
const auto& target_inline_entries = data->GetTargetInlineEntries();
ASSERT_EQ(target_inline_entries.size(), 2U);
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::integer::int1,
Res_value::TYPE_INT_DEC, 2U);
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[1], R::target::string::str1,
Res_value::TYPE_REFERENCE, 0x7f010000);
}
TEST(IdmapTests, FailCreateIdmapInvalidName) {
std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk";
std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
ASSERT_THAT(target_apk, NotNull());
auto target = TargetResourceContainer::FromPath(target_apk_path);
ASSERT_TRUE(target);
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
ASSERT_TRUE(overlay);
{
auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, "", PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
auto idmap_result = Idmap::FromContainers(**target, **overlay, "", PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_FALSE(idmap_result);
}
{
auto idmap_result =
Idmap::FromApkAssets(*target_apk, *overlay_apk, "unknown", PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
auto idmap_result = Idmap::FromContainers(**target, **overlay, "unknown", PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_FALSE(idmap_result);
}
}
@@ -282,15 +323,15 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssetsSharedLibOverlay) {
std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay-shared.apk";
std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
ASSERT_THAT(target_apk, NotNull());
auto target = TargetResourceContainer::FromPath(target_apk_path);
ASSERT_TRUE(target);
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
ASSERT_TRUE(overlay);
auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk,
TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
auto idmap_result = Idmap::FromContainers(
**target, **overlay, TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
auto& idmap = *idmap_result;
ASSERT_THAT(idmap, NotNull());
@@ -328,30 +369,29 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssetsSharedLibOverlay) {
}
Result<std::unique_ptr<const IdmapData>> TestIdmapDataFromApkAssets(
const std::string& local_target_apk_path, const std::string& local_overlay_apk_path,
const std::string& local_target_path, const std::string& local_overlay_path,
const std::string& overlay_name, const PolicyBitmask& fulfilled_policies,
bool enforce_overlayable) {
auto overlay_info =
utils::ExtractOverlayManifestInfo(GetTestDataPath() + local_overlay_apk_path, overlay_name);
const std::string target_path(GetTestDataPath() + local_target_path);
auto target = TargetResourceContainer::FromPath(target_path);
if (!target) {
return Error(R"(Failed to load target "%s")", target_path.c_str());
}
const std::string overlay_path(GetTestDataPath() + local_overlay_path);
auto overlay = OverlayResourceContainer::FromPath(overlay_path);
if (!overlay) {
return Error(R"(Failed to load overlay "%s")", overlay_path.c_str());
}
auto overlay_info = (*overlay)->FindOverlayInfo(overlay_name);
if (!overlay_info) {
return overlay_info.GetError();
}
const std::string target_apk_path(GetTestDataPath() + local_target_apk_path);
std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
if (!target_apk) {
return Error(R"(Failed to load target apk "%s")", target_apk_path.data());
}
const std::string overlay_apk_path(GetTestDataPath() + local_overlay_apk_path);
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
if (!overlay_apk) {
return Error(R"(Failed to load overlay apk "%s")", overlay_apk_path.data());
return Error(R"(Failed to find overlay name "%s")", overlay_name.c_str());
}
LogInfo log_info;
auto mapping = ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, *overlay_info,
fulfilled_policies, enforce_overlayable, log_info);
auto mapping = ResourceMapping::FromContainers(**target, **overlay, *overlay_info,
fulfilled_policies, enforce_overlayable, log_info);
if (!mapping) {
return mapping.GetError();
}
@@ -360,11 +400,9 @@ Result<std::unique_ptr<const IdmapData>> TestIdmapDataFromApkAssets(
}
TEST(IdmapTests, CreateIdmapDataDoNotRewriteNonOverlayResourceId) {
auto idmap_data =
TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk", "DifferentPackages",
PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
auto idmap_data = TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk",
"DifferentPackages", PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage();
auto& data = *idmap_data;
@@ -417,7 +455,7 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) {
const uint32_t target_crc = kIdmapRawDataTargetCrc;
const uint32_t overlay_crc = kIdmapRawOverlayCrc;
std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen);
std::istringstream raw_stream(raw);
auto result = Idmap::FromBinaryStream(raw_stream);
@@ -468,8 +506,8 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) {
ASSERT_THAT(bad_target_crc_header, NotNull());
ASSERT_NE(header->GetTargetCrc(), bad_target_crc_header->GetTargetCrc());
ASSERT_FALSE(bad_target_crc_header->IsUpToDate(target_apk_path, overlay_apk_path, overlay_name,
target_crc, overlay_crc, policies,
/* enforce_overlayable */ true));
target_crc, overlay_crc, policies,
/* enforce_overlayable */ true));
// overlay crc: bytes (0xc, 0xf)
std::string bad_overlay_crc_string(stream.str());
@@ -483,8 +521,8 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) {
ASSERT_THAT(bad_overlay_crc_header, NotNull());
ASSERT_NE(header->GetOverlayCrc(), bad_overlay_crc_header->GetOverlayCrc());
ASSERT_FALSE(bad_overlay_crc_header->IsUpToDate(target_apk_path, overlay_apk_path, overlay_name,
target_crc, overlay_crc, policies,
/* enforce_overlayable */ true));
target_crc, overlay_crc, policies,
/* enforce_overlayable */ true));
// fulfilled policy: bytes (0x10, 0x13)
std::string bad_policy_string(stream.str());
@@ -522,8 +560,8 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) {
ASSERT_THAT(bad_target_path_header, NotNull());
ASSERT_NE(header->GetTargetPath(), bad_target_path_header->GetTargetPath());
ASSERT_FALSE(bad_target_path_header->IsUpToDate(target_apk_path, overlay_apk_path, overlay_name,
target_crc, overlay_crc, policies,
/* enforce_overlayable */ true));
target_crc, overlay_crc, policies,
/* enforce_overlayable */ true));
// overlay path: bytes (0x2c, 0x37)
std::string bad_overlay_path_string(stream.str());
@@ -576,7 +614,7 @@ class TestVisitor : public Visitor {
};
TEST(IdmapTests, TestVisitor) {
std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen);
std::istringstream stream(raw);
const auto idmap = Idmap::FromBinaryStream(stream);

View File

@@ -27,35 +27,31 @@
#include "idmap2/Idmap.h"
#include "idmap2/PrettyPrintVisitor.h"
using android::ApkAssets;
using android::base::StringPrintf;
using ::testing::NotNull;
using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
namespace android::idmap2 {
TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) {
const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
ASSERT_THAT(target_apk, NotNull());
auto target = TargetResourceContainer::FromPath(target_apk_path);
ASSERT_TRUE(target);
const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
ASSERT_TRUE(overlay);
const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk,
TestConstants::OVERLAY_NAME_DEFAULT, PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
const auto idmap = Idmap::FromContainers(**target, **overlay, TestConstants::OVERLAY_NAME_DEFAULT,
PolicyFlags::PUBLIC, /* enforce_overlayable */ true);
ASSERT_TRUE(idmap);
std::stringstream stream;
PrettyPrintVisitor visitor(stream);
(*idmap)->accept(&visitor);
ASSERT_NE(stream.str().find("target apk path : "), std::string::npos);
ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos);
ASSERT_NE(stream.str().find("target path : "), std::string::npos);
ASSERT_NE(stream.str().find("overlay path : "), std::string::npos);
ASSERT_NE(stream.str().find(StringPrintf("0x%08x -> 0x%08x (integer/int1 -> integer/int1)\n",
R::target::integer::int1, R::overlay::integer::int1)),
std::string::npos);
@@ -64,7 +60,7 @@ TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) {
TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitorWithoutAccessToApks) {
fclose(stderr); // silence expected warnings from libandroidfw
std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen);
std::istringstream raw_stream(raw);
const auto idmap = Idmap::FromBinaryStream(raw_stream);
@@ -74,8 +70,8 @@ TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitorWithoutAccessToApks) {
PrettyPrintVisitor visitor(stream);
(*idmap)->accept(&visitor);
ASSERT_NE(stream.str().find("target apk path : "), std::string::npos);
ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos);
ASSERT_NE(stream.str().find("target path : "), std::string::npos);
ASSERT_NE(stream.str().find("overlay path : "), std::string::npos);
ASSERT_NE(stream.str().find("0x7f020000 -> 0x7f020000 (\?\?\? -> \?\?\?)\n"), std::string::npos);
}

View File

@@ -30,17 +30,16 @@
#include "idmap2/RawPrintVisitor.h"
using android::base::StringPrintf;
using ::testing::NotNull;
using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
namespace android::idmap2 {
#define ASSERT_CONTAINS_REGEX(pattern, str) \
do { \
ASSERT_TRUE(std::regex_search(str, std::regex(pattern))) \
<< "pattern '" << pattern << "' not found in\n--------\n" \
<< str << "--------"; \
#define ASSERT_CONTAINS_REGEX(pattern, str) \
do { \
ASSERT_TRUE(std::regex_search(str, std::regex(pattern))) \
<< "pattern '" << (pattern) << "' not found in\n--------\n" \
<< (str) << "--------"; \
} while (0)
#define ADDRESS "[0-9a-f]{8}: "
@@ -49,16 +48,15 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
fclose(stderr); // silence expected warnings
const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
ASSERT_THAT(target_apk, NotNull());
auto target = TargetResourceContainer::FromPath(target_apk_path);
ASSERT_TRUE(target);
const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
ASSERT_TRUE(overlay);
const auto idmap =
Idmap::FromApkAssets(*target_apk, *overlay_apk, TestConstants::OVERLAY_NAME_DEFAULT,
PolicyFlags::PUBLIC, /* enforce_overlayable */ true);
const auto idmap = Idmap::FromContainers(**target, **overlay, TestConstants::OVERLAY_NAME_DEFAULT,
PolicyFlags::PUBLIC, /* enforce_overlayable */ true);
ASSERT_TRUE(idmap);
std::stringstream stream;
@@ -66,7 +64,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
(*idmap)->accept(&visitor);
ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000007 version\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000008 version\n", stream.str());
ASSERT_CONTAINS_REGEX(
StringPrintf(ADDRESS "%s target crc\n", android::idmap2::TestConstants::TARGET_CRC_STRING),
stream.str());
@@ -75,8 +73,6 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000001 fulfilled policies: public\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000001 enforce overlayable\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 target entry count", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000000 target inline entry count", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 overlay entry count", stream.str());
@@ -104,7 +100,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
fclose(stderr); // silence expected warnings from libandroidfw
std::string raw(reinterpret_cast<const char*>(idmap_raw_data), kIdmapRawDataLen);
std::string raw(reinterpret_cast<const char*>(kIdmapRawData), kIdmapRawDataLen);
std::istringstream raw_stream(raw);
const auto idmap = Idmap::FromBinaryStream(raw_stream);
@@ -115,7 +111,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
(*idmap)->accept(&visitor);
ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000007 version\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000008 version\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00001234 target crc\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00005678 overlay crc\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000011 fulfilled policies: public|signature\n", stream.str());
@@ -126,8 +122,6 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
ASSERT_CONTAINS_REGEX(ADDRESS "........ overlay path: overlayX.apk\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "0000000b overlay name size\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "........ overlay name: OverlayName\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000003 target entry count\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000001 target inline entry count\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000003 overlay entry count\n", stream.str());
@@ -140,7 +134,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 overlay id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f030002 target id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 string pool size\n", stream.str());
ASSERT_CONTAINS_REGEX("000000a8: ........ string pool\n", stream.str());
ASSERT_CONTAINS_REGEX("000000a4: ........ string pool\n", stream.str());
}
} // namespace android::idmap2

View File

@@ -14,6 +14,10 @@
* limitations under the License.
*/
#include <android-base/file.h>
#include <androidfw/ResourceTypes.h>
#include <gtest/gtest.h>
#include <cstdio> // fclose
#include <fstream>
#include <memory>
@@ -22,14 +26,10 @@
#include "R.h"
#include "TestConstants.h"
#include "TestHelpers.h"
#include "androidfw/ResourceTypes.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "idmap2/LogInfo.h"
#include "idmap2/ResourceMapping.h"
using android::Res_value;
using android::idmap2::utils::ExtractOverlayManifestInfo;
using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
@@ -41,32 +41,36 @@ namespace android::idmap2 {
ASSERT_TRUE(result) << result.GetErrorMessage(); \
} while (0)
Result<ResourceMapping> TestGetResourceMapping(const std::string& local_target_apk_path,
const std::string& local_overlay_apk_path,
Result<ResourceMapping> TestGetResourceMapping(const std::string& local_target_path,
const std::string& local_overlay_path,
const std::string& overlay_name,
const PolicyBitmask& fulfilled_policies,
bool enforce_overlayable) {
auto overlay_info =
ExtractOverlayManifestInfo(GetTestDataPath() + local_overlay_apk_path, overlay_name);
const std::string target_path = (local_target_path[0] == '/')
? local_target_path
: (GetTestDataPath() + "/" + local_target_path);
auto target = TargetResourceContainer::FromPath(target_path);
if (!target) {
return Error(target.GetError(), R"(Failed to load target "%s")", target_path.c_str());
}
const std::string overlay_path = (local_overlay_path[0] == '/')
? local_overlay_path
: (GetTestDataPath() + "/" + local_overlay_path);
auto overlay = OverlayResourceContainer::FromPath(overlay_path);
if (!overlay) {
return Error(overlay.GetError(), R"(Failed to load overlay "%s")", overlay_path.c_str());
}
auto overlay_info = (*overlay)->FindOverlayInfo(overlay_name);
if (!overlay_info) {
return overlay_info.GetError();
}
const std::string target_apk_path(GetTestDataPath() + local_target_apk_path);
std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
if (!target_apk) {
return Error(R"(Failed to load target apk "%s")", target_apk_path.data());
}
const std::string overlay_apk_path(GetTestDataPath() + local_overlay_apk_path);
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
if (!overlay_apk) {
return Error(R"(Failed to load overlay apk "%s")", overlay_apk_path.data());
return Error(overlay_info.GetError(), R"(Failed to find overlay name "%s")",
overlay_name.c_str());
}
LogInfo log_info;
return ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, *overlay_info,
fulfilled_policies, enforce_overlayable, log_info);
return ResourceMapping::FromContainers(**target, **overlay, *overlay_info, fulfilled_policies,
enforce_overlayable, log_info);
}
Result<Unit> MappingExists(const ResourceMapping& mapping, ResourceId target_resource,
@@ -128,7 +132,7 @@ Result<Unit> MappingExists(const ResourceMapping& mapping, const ResourceId& tar
}
TEST(ResourceMappingTests, ResourcesFromApkAssetsLegacy) {
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay-legacy.apk", "",
auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay-legacy.apk", "",
PolicyFlags::PUBLIC, /* enforce_overlayable */ false);
ASSERT_TRUE(resources) << resources.GetErrorMessage();
@@ -145,7 +149,7 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsLegacy) {
}
TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) {
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", "SwapNames",
auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk", "SwapNames",
PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
@@ -161,7 +165,7 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) {
}
TEST(ResourceMappingTests, DoNotRewriteNonOverlayResourceId) {
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk",
auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk",
"DifferentPackages", PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
@@ -176,7 +180,7 @@ TEST(ResourceMappingTests, DoNotRewriteNonOverlayResourceId) {
}
TEST(ResourceMappingTests, InlineResources) {
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", "Inline",
auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk", "Inline",
PolicyFlags::PUBLIC, /* enforce_overlayable */ false);
constexpr size_t overlay_string_pool_size = 10U;
@@ -189,8 +193,32 @@ TEST(ResourceMappingTests, InlineResources) {
ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 73U));
}
TEST(ResourceMappingTests, FabricatedOverlay) {
auto frro = FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "test.target")
.SetOverlayable("TestResources")
.SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U)
.SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000)
.Build();
ASSERT_TRUE(frro);
TemporaryFile tf;
std::ofstream out(tf.path);
ASSERT_TRUE((*frro).ToBinaryStream(out));
out.close();
auto resources = TestGetResourceMapping("target/target.apk", tf.path, "SandTheme",
PolicyFlags::PUBLIC, /* enforce_overlayable */ false);
ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources;
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
ASSERT_EQ(res.GetOverlayToTargetMap().size(), 0U);
ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE, 0x7f010000));
ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 2U));
}
TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk",
auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk",
TestConstants::OVERLAY_NAME_ALL_POLICIES,
PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
@@ -209,7 +237,7 @@ TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
// Resources that are not declared as overlayable and resources that a protected by policies the
// overlay does not fulfill must not map to overlay resources.
TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) {
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk",
auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk",
TestConstants::OVERLAY_NAME_ALL_POLICIES,
PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
@@ -229,7 +257,7 @@ TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) {
// overlay does not fulfilled can map to overlay resources when overlayable enforcement is turned
// off.
TEST(ResourceMappingTests, ResourcesFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) {
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk",
auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay.apk",
TestConstants::OVERLAY_NAME_ALL_POLICIES,
PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
@@ -264,7 +292,7 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsPolicySystemPublicInvalidIgnore
// Overlays that do not target an <overlayable> tag can overlay any resource if overlayable
// enforcement is disabled.
TEST(ResourceMappingTests, ResourcesFromApkAssetsNoDefinedOverlayableAndNoTargetName) {
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay-legacy.apk", "",
auto resources = TestGetResourceMapping("target/target.apk", "overlay/overlay-legacy.apk", "",
PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
@@ -284,10 +312,9 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsNoDefinedOverlayableAndNoTarget
// Overlays that are neither pre-installed nor signed with the same signature as the target cannot
// overlay packages that have not defined overlayable resources.
TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPoliciesPublicFail) {
auto resources =
TestGetResourceMapping("/target/target-no-overlayable.apk", "/overlay/overlay.apk",
"NoTargetName", PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
auto resources = TestGetResourceMapping("target/target-no-overlayable.apk", "overlay/overlay.apk",
"NoTargetName", PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(resources) << resources.GetErrorMessage();
ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 0U);
@@ -297,9 +324,9 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPoliciesPublicFail) {
// signed with the same signature as the reference package can overlay packages that have not
// defined overlayable resources.
TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPolicies) {
auto CheckEntries = [&](const PolicyBitmask& fulfilled_policies) -> void {
auto CheckEntries = [&](const PolicyBitmask& fulfilled_policies) {
auto resources =
TestGetResourceMapping("/target/target-no-overlayable.apk", "/overlay/overlay.apk",
TestGetResourceMapping("target/target-no-overlayable.apk", "overlay/overlay.apk",
TestConstants::OVERLAY_NAME_ALL_POLICIES, fulfilled_policies,
/* enforce_overlayable */ true);

View File

@@ -17,10 +17,12 @@
#include <memory>
#include <string>
#include "R.h"
#include "TestHelpers.h"
#include "androidfw/ApkAssets.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "idmap2/ResourceContainer.h"
#include "idmap2/ResourceUtils.h"
#include "idmap2/Result.h"
@@ -49,8 +51,8 @@ class ResourceUtilsTests : public Idmap2Tests {
};
TEST_F(ResourceUtilsTests, ResToTypeEntryName) {
Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), 0x7f010000U);
ASSERT_TRUE(name);
Result<std::string> name = utils::ResToTypeEntryName(GetAssetManager(), R::target::integer::int1);
ASSERT_TRUE(name) << name.GetErrorMessage();
ASSERT_EQ(*name, "integer/int1");
}
@@ -60,25 +62,34 @@ TEST_F(ResourceUtilsTests, ResToTypeEntryNameNoSuchResourceId) {
}
TEST_F(ResourceUtilsTests, InvalidValidOverlayNameInvalidAttributes) {
auto info = utils::ExtractOverlayManifestInfo(GetTestDataPath() + "/overlay/overlay-invalid.apk",
"InvalidName");
auto overlay =
OverlayResourceContainer::FromPath(GetTestDataPath() + "/overlay/overlay-invalid.apk");
ASSERT_TRUE(overlay);
auto info = (*overlay)->FindOverlayInfo("InvalidName");
ASSERT_FALSE(info);
}
TEST_F(ResourceUtilsTests, ValidOverlayNameInvalidAttributes) {
auto info = utils::ExtractOverlayManifestInfo(GetTestDataPath() + "/overlay/overlay-invalid.apk",
"ValidName");
auto overlay =
OverlayResourceContainer::FromPath(GetTestDataPath() + "/overlay/overlay-invalid.apk");
ASSERT_TRUE(overlay);
auto info = (*overlay)->FindOverlayInfo("ValidName");
ASSERT_FALSE(info);
}
TEST_F(ResourceUtilsTests, ValidOverlayNameAndTargetPackageInvalidAttributes) {
auto info = utils::ExtractOverlayManifestInfo(GetTestDataPath() + "/overlay/overlay-invalid.apk",
"ValidNameAndTargetPackage");
auto overlay =
OverlayResourceContainer::FromPath(GetTestDataPath() + "/overlay/overlay-invalid.apk");
ASSERT_TRUE(overlay);
auto info = (*overlay)->FindOverlayInfo("ValidNameAndTargetPackage");
ASSERT_TRUE(info);
ASSERT_EQ("ValidNameAndTargetPackage", info->name);
ASSERT_EQ("Valid", info->target_package);
ASSERT_EQ("", info->target_name); // Attribute resource id could not be found
ASSERT_EQ(0, info->resource_mapping); // Attribute resource id could not be found
ASSERT_EQ("", info->target_name); // Attribute resource id could not be found
ASSERT_EQ(0, info->resource_mapping); // Attribute resource id could not be found
}
}// namespace android::idmap2
} // namespace android::idmap2

View File

@@ -24,13 +24,13 @@
namespace android::idmap2 {
const unsigned char idmap_raw_data[] = {
const unsigned char kIdmapRawData[] = {
// IDMAP HEADER
// 0x0: magic
0x49, 0x44, 0x4d, 0x50,
// 0x4: version
0x07, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00,
// 0x8: target crc
0x34, 0x12, 0x00, 0x00,
@@ -70,81 +70,72 @@ const unsigned char idmap_raw_data[] = {
0x64, 0x65, 0x62, 0x75, 0x67, 0x00, 0x00, 0x00,
// DATA HEADER
// 0x54: target_package_id
0x7f,
// 0x55: overlay_package_id
0x7f,
// 0x56: padding
0x00, 0x00,
// 0x58: target_entry_count
// 0x54: target_entry_count
0x03, 0x00, 0x00, 0x00,
// 0x5c: target_inline_entry_count
// 0x58: target_inline_entry_count
0x01, 0x00, 0x00, 0x00,
// 0x60: overlay_entry_count
// 0x5c: overlay_entry_count
0x03, 0x00, 0x00, 0x00,
// 0x64: string_pool_offset
// 0x60: string_pool_offset
0x00, 0x00, 0x00, 0x00,
// TARGET ENTRIES
// 0x68: target id (0x7f020000)
// 0x64: target id (0x7f020000)
0x00, 0x00, 0x02, 0x7f,
// 0x6c: overlay_id (0x7f020000)
// 0x68: overlay_id (0x7f020000)
0x00, 0x00, 0x02, 0x7f,
// 0x70: target id (0x7f030000)
// 0x6c: target id (0x7f030000)
0x00, 0x00, 0x03, 0x7f,
// 0x74: overlay_id (0x7f030000)
// 0x70: overlay_id (0x7f030000)
0x00, 0x00, 0x03, 0x7f,
// 0x78: target id (0x7f030002)
// 0x74: target id (0x7f030002)
0x02, 0x00, 0x03, 0x7f,
// 0x7c: overlay_id (0x7f030001)
// 0x78: overlay_id (0x7f030001)
0x01, 0x00, 0x03, 0x7f,
// INLINE TARGET ENTRIES
// 0x80: target_id
// 0x7c: target_id
0x00, 0x00, 0x04, 0x7f,
// 0x84: Res_value::size (value ignored by idmap)
// 0x80: Res_value::size (value ignored by idmap)
0x08, 0x00,
// 0x87: Res_value::res0 (value ignored by idmap)
// 0x82: Res_value::res0 (value ignored by idmap)
0x00,
// 0x88: Res_value::dataType (TYPE_INT_HEX)
// 0x83: Res_value::dataType (TYPE_INT_HEX)
0x11,
// 0x8c: Res_value::data
// 0x84: Res_value::data
0x78, 0x56, 0x34, 0x12,
// OVERLAY ENTRIES
// 0x90: 0x7f020000 -> 0x7f020000
// 0x88: 0x7f020000 -> 0x7f020000
0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x02, 0x7f,
// 0x98: 0x7f030000 -> 0x7f030000
// 0x90: 0x7f030000 -> 0x7f030000
0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f,
// 0xa0: 0x7f030001 -> 0x7f030002
// 0x98: 0x7f030001 -> 0x7f030002
0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f,
// 0xa4: string pool
// 0xa0: string pool
// string length,
0x04, 0x00, 0x00, 0x00,
// 0xa8 string contents "test"
// 0xa4 string contents "test"
0x74, 0x65, 0x73, 0x74};
const unsigned int kIdmapRawDataLen = 0xac;
const unsigned int kIdmapRawDataLen = 0xa8;
const unsigned int kIdmapRawDataOffset = 0x54;
const unsigned int kIdmapRawDataTargetCrc = 0x1234;
const unsigned int kIdmapRawOverlayCrc = 0x5678;

View File

@@ -19,25 +19,25 @@
#include <string>
#include "TestHelpers.h"
#include "gmock/gmock.h"
#include "androidfw/AssetsProvider.h"
#include "gtest/gtest.h"
#include "idmap2/XmlParser.h"
#include "idmap2/ZipFile.h"
namespace android::idmap2 {
Result<std::unique_ptr<const XmlParser>> CreateTestParser(const std::string& test_file) {
auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
Result<XmlParser> CreateTestParser(const std::string& test_file) {
auto zip = ZipAssetsProvider::Create(GetTestDataPath() + "/target/target.apk");
if (zip == nullptr) {
return Error("Failed to open zip file");
}
auto data = zip->Uncompress(test_file);
auto data = zip->Open(test_file);
if (data == nullptr) {
return Error("Failed to open xml file");
}
return XmlParser::Create(data->buf, data->size, /* copy_data */ true);
return XmlParser::Create(data->getBuffer(true /* aligned*/), data->getLength(),
/* copy_data */ true);
}
TEST(XmlParserTests, Create) {
@@ -54,7 +54,7 @@ TEST(XmlParserTests, NextChild) {
auto xml = CreateTestParser("res/xml/test.xml");
ASSERT_TRUE(xml) << xml.GetErrorMessage();
auto root_iter = (*xml)->tree_iterator();
auto root_iter = xml->tree_iterator();
ASSERT_EQ(root_iter->event(), XmlParser::Event::START_TAG);
ASSERT_EQ(root_iter->name(), "a");
@@ -85,7 +85,7 @@ TEST(XmlParserTests, AttributeValues) {
ASSERT_TRUE(xml) << xml.GetErrorMessage();
// Start at the <a> tag.
auto root_iter = (*xml)->tree_iterator();
auto root_iter = xml->tree_iterator();
// Start at the <b> tag.
auto a_iter = root_iter.begin();
@@ -111,8 +111,8 @@ TEST(XmlParserTests, IteratorEquality) {
ASSERT_TRUE(xml) << xml.GetErrorMessage();
// Start at the <a> tag.
auto root_iter_1 = (*xml)->tree_iterator();
auto root_iter_2 = (*xml)->tree_iterator();
auto root_iter_1 = xml->tree_iterator();
auto root_iter_2 = xml->tree_iterator();
ASSERT_EQ(root_iter_1, root_iter_2);
ASSERT_EQ(*root_iter_1, *root_iter_2);
@@ -146,7 +146,7 @@ TEST(XmlParserTests, Backtracking) {
ASSERT_TRUE(xml) << xml.GetErrorMessage();
// Start at the <a> tag.
auto root_iter_1 = (*xml)->tree_iterator();
auto root_iter_1 = xml->tree_iterator();
// Start at the <b> tag.
auto a_iter_1 = root_iter_1.begin();

View File

@@ -1,66 +0,0 @@
/*
* Copyright (C) 2018 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 <cstdio> // fclose
#include <string>
#include "TestHelpers.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "idmap2/Result.h"
#include "idmap2/ZipFile.h"
using ::testing::IsNull;
using ::testing::NotNull;
namespace android::idmap2 {
TEST(ZipFileTests, BasicOpen) {
auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
ASSERT_THAT(zip, NotNull());
fclose(stderr); // silence expected warnings from libziparchive
auto fail = ZipFile::Open(GetTestDataPath() + "/does-not-exist");
ASSERT_THAT(fail, IsNull());
}
TEST(ZipFileTests, Crc) {
auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
ASSERT_THAT(zip, NotNull());
Result<uint32_t> crc = zip->Crc("AndroidManifest.xml");
ASSERT_TRUE(crc);
ASSERT_EQ(*crc, 0x762f3d24);
Result<uint32_t> crc2 = zip->Crc("does-not-exist");
ASSERT_FALSE(crc2);
}
TEST(ZipFileTests, Uncompress) {
auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
ASSERT_THAT(zip, NotNull());
auto data = zip->Uncompress("assets/lorem-ipsum.txt");
ASSERT_THAT(data, NotNull());
const std::string lorem_ipsum("Lorem ipsum dolor sit amet.\n");
ASSERT_THAT(data->size, lorem_ipsum.size());
ASSERT_THAT(std::string(reinterpret_cast<const char*>(data->buf), data->size), lorem_ipsum);
auto fail = zip->Uncompress("does-not-exist");
ASSERT_THAT(fail, IsNull());
}
} // namespace android::idmap2