Merge "implement drawable frros"

This commit is contained in:
Jeremy Meyer
2022-11-14 22:35:08 +00:00
committed by Android (Google) Code Review
22 changed files with 248 additions and 65 deletions

View File

@@ -222,6 +222,7 @@ cc_test {
},
data: [
"tests/data/**/*.apk",
"tests/data/**/*.png",
],
compile_multilib: "first",
test_options: {

View File

@@ -39,6 +39,7 @@
#include "idmap2/PrettyPrintVisitor.h"
#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
#include <fcntl.h>
using android::base::StringPrintf;
using android::binder::Status;
@@ -238,6 +239,9 @@ Status Idmap2Service::createFabricatedOverlay(
if (res.dataType == Res_value::TYPE_STRING) {
builder.SetResourceValue(res.resourceName, res.dataType, res.stringData.value(),
res.configuration.value_or(std::string()));
} else if (res.binaryData.has_value()) {
builder.SetResourceValue(res.resourceName, res.binaryData->get(),
res.configuration.value_or(std::string()));
} else {
builder.SetResourceValue(res.resourceName, res.dataType, res.data,
res.configuration.value_or(std::string()));
@@ -264,6 +268,7 @@ Status Idmap2Service::createFabricatedOverlay(
file_name.c_str(), kMaxFileNameLength));
}
} while (std::filesystem::exists(path));
builder.setFrroPath(path);
const uid_t uid = IPCThreadState::self()->getCallingUid();
if (!UidHasWriteAccessToPath(uid, path)) {

View File

@@ -24,5 +24,6 @@ parcelable FabricatedOverlayInternalEntry {
int dataType;
int data;
@nullable @utf8InCpp String stringData;
@nullable ParcelFileDescriptor binaryData;
@nullable @utf8InCpp String configuration;
}

View File

@@ -28,6 +28,7 @@
#include "idmap2/ResourceContainer.h"
#include "idmap2/Result.h"
#include <binder/ParcelFileDescriptor.h>
namespace android::idmap2 {
@@ -45,6 +46,15 @@ struct FabricatedOverlay {
const std::string& data_string_value,
const std::string& configuration);
Builder& SetResourceValue(const std::string& resource_name,
std::optional<android::base::borrowed_fd>&& binary_value,
const std::string& configuration);
inline Builder& setFrroPath(std::string frro_path) {
frro_path_ = std::move(frro_path);
return *this;
}
WARN_UNUSED Result<FabricatedOverlay> Build();
private:
@@ -53,6 +63,7 @@ struct FabricatedOverlay {
DataType data_type;
DataValue data_value;
std::string data_string_value;
std::optional<android::base::borrowed_fd> data_binary_value;
std::string configuration;
};
@@ -60,6 +71,7 @@ struct FabricatedOverlay {
std::string name_;
std::string target_package_name_;
std::string target_overlayable_;
std::string frro_path_;
std::vector<Entry> entries_;
};
@@ -79,10 +91,14 @@ struct FabricatedOverlay {
explicit FabricatedOverlay(pb::FabricatedOverlay&& overlay,
std::string&& string_pool_data_,
std::vector<android::base::borrowed_fd> binary_files_,
off_t total_binary_bytes_,
std::optional<uint32_t> crc_from_disk = {});
pb::FabricatedOverlay overlay_pb_;
std::string string_pool_data_;
std::vector<android::base::borrowed_fd> binary_files_;
uint32_t total_binary_bytes_;
std::optional<uint32_t> crc_from_disk_;
mutable std::optional<SerializedData> data_;

View File

@@ -19,6 +19,7 @@
#include <optional>
#include <string>
#include <android-base/unique_fd.h>
#include "androidfw/AssetManager2.h"
#include "idmap2/Result.h"
@@ -41,6 +42,7 @@ struct TargetValue {
DataType data_type;
DataValue data_value;
std::string data_string_value;
std::optional<android::base::borrowed_fd> data_binary_value;
};
struct TargetValueWithConfig {

View File

@@ -16,6 +16,10 @@
#include "idmap2/FabricatedOverlay.h"
#include <sys/stat.h> // umask
#include <sys/types.h> // umask
#include <android-base/file.h>
#include <androidfw/ResourceUtils.h>
#include <androidfw/StringPool.h>
#include <google/protobuf/io/coded_stream.h>
@@ -51,9 +55,13 @@ void Write32(std::ostream& stream, uint32_t value) {
FabricatedOverlay::FabricatedOverlay(pb::FabricatedOverlay&& overlay,
std::string&& string_pool_data,
std::vector<android::base::borrowed_fd> binary_files,
off_t total_binary_bytes,
std::optional<uint32_t> crc_from_disk)
: overlay_pb_(std::forward<pb::FabricatedOverlay>(overlay)),
string_pool_data_(std::move(string_pool_data)),
binary_files_(std::move(binary_files)),
total_binary_bytes_(total_binary_bytes),
crc_from_disk_(crc_from_disk) {
}
@@ -72,14 +80,23 @@ FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetOverlayable(const std
FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetResourceValue(
const std::string& resource_name, uint8_t data_type, uint32_t data_value,
const std::string& configuration) {
entries_.emplace_back(Entry{resource_name, data_type, data_value, "", configuration});
entries_.emplace_back(
Entry{resource_name, data_type, data_value, "", std::nullopt, configuration});
return *this;
}
FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetResourceValue(
const std::string& resource_name, uint8_t data_type, const std::string& data_string_value,
const std::string& configuration) {
entries_.emplace_back(Entry{resource_name, data_type, 0, data_string_value, configuration});
entries_.emplace_back(
Entry{resource_name, data_type, 0, data_string_value, std::nullopt, configuration});
return *this;
}
FabricatedOverlay::Builder& FabricatedOverlay::Builder::SetResourceValue(
const std::string& resource_name, std::optional<android::base::borrowed_fd>&& binary_value,
const std::string& configuration) {
entries_.emplace_back(Entry{resource_name, 0, 0, "", binary_value, configuration});
return *this;
}
@@ -135,7 +152,7 @@ Result<FabricatedOverlay> FabricatedOverlay::Builder::Build() {
}
value->second = TargetValue{res_entry.data_type, res_entry.data_value,
res_entry.data_string_value};
res_entry.data_string_value, res_entry.data_binary_value};
}
pb::FabricatedOverlay overlay_pb;
@@ -144,6 +161,11 @@ Result<FabricatedOverlay> FabricatedOverlay::Builder::Build() {
overlay_pb.set_target_package_name(target_package_name_);
overlay_pb.set_target_overlayable(target_overlayable_);
std::vector<android::base::borrowed_fd> binary_files;
size_t total_binary_bytes = 0;
// 16 for the number of bytes in the frro file before the binary data
const size_t FRRO_HEADER_SIZE = 16;
for (auto& package : package_map) {
auto package_pb = overlay_pb.add_packages();
package_pb->set_name(package.first);
@@ -162,6 +184,20 @@ Result<FabricatedOverlay> FabricatedOverlay::Builder::Build() {
if (value.second.data_type == Res_value::TYPE_STRING) {
auto ref = string_pool.MakeRef(value.second.data_string_value);
pb_value->set_data_value(ref.index());
} else if (value.second.data_binary_value.has_value()) {
pb_value->set_data_type(Res_value::TYPE_STRING);
struct stat s;
if (fstat(value.second.data_binary_value->get(), &s) == -1) {
return Error("unable to get size of binary file: %d", errno);
}
std::string uri
= StringPrintf("frro:/%s?offset=%d&size=%d", frro_path_.c_str(),
static_cast<int> (FRRO_HEADER_SIZE + total_binary_bytes),
static_cast<int> (s.st_size));
total_binary_bytes += s.st_size;
binary_files.emplace_back(value.second.data_binary_value->get());
auto ref = string_pool.MakeRef(std::move(uri));
pb_value->set_data_value(ref.index());
} else {
pb_value->set_data_value(value.second.data_value);
}
@@ -169,10 +205,10 @@ Result<FabricatedOverlay> FabricatedOverlay::Builder::Build() {
}
}
}
android::BigBuffer string_buffer(kBufferSize);
android::StringPool::FlattenUtf8(&string_buffer, string_pool, nullptr);
return FabricatedOverlay(std::move(overlay_pb), string_buffer.to_string());
return FabricatedOverlay(std::move(overlay_pb), string_buffer.to_string(),
std::move(binary_files), total_binary_bytes);
}
Result<FabricatedOverlay> FabricatedOverlay::FromBinaryStream(std::istream& stream) {
@@ -190,7 +226,7 @@ Result<FabricatedOverlay> FabricatedOverlay::FromBinaryStream(std::istream& stre
return Error("Failed to read fabricated overlay version.");
}
if (version != 1 && version != 2) {
if (version < 1 || version > 3) {
return Error("Invalid fabricated overlay version '%u'.", version);
}
@@ -201,7 +237,14 @@ Result<FabricatedOverlay> FabricatedOverlay::FromBinaryStream(std::istream& stre
pb::FabricatedOverlay overlay{};
std::string sp_data;
if (version == 2) {
uint32_t total_binary_bytes;
if (version == 3) {
if (!Read32(stream, &total_binary_bytes)) {
return Error("Failed read total binary bytes.");
}
stream.seekg(total_binary_bytes, std::istream::cur);
}
if (version >= 2) {
uint32_t sp_size;
if (!Read32(stream, &sp_size)) {
return Error("Failed read string pool size.");
@@ -211,20 +254,15 @@ Result<FabricatedOverlay> FabricatedOverlay::FromBinaryStream(std::istream& stre
return Error("Failed to read string pool.");
}
sp_data = buf;
if (!overlay.ParseFromIstream(&stream)) {
return Error("Failed read fabricated overlay proto.");
}
} else {
if (!overlay.ParseFromIstream(&stream)) {
return Error("Failed read fabricated overlay proto.");
}
}
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), std::move(sp_data),
return FabricatedOverlay(std::move(overlay), std::move(sp_data), {}, total_binary_bytes,
version == kFabricatedOverlayCurrentVersion
? std::optional<uint32_t>(crc)
: std::nullopt);
@@ -274,6 +312,14 @@ Result<Unit> FabricatedOverlay::ToBinaryStream(std::ostream& stream) const {
Write32(stream, kFabricatedOverlayMagic);
Write32(stream, kFabricatedOverlayCurrentVersion);
Write32(stream, (*data)->pb_crc);
Write32(stream, total_binary_bytes_);
std::string file_contents;
for (const android::base::borrowed_fd fd : binary_files_) {
if (!ReadFdToString(fd, &file_contents)) {
return Error("Failed to read binary file data.");
}
stream.write(file_contents.data(), file_contents.length());
}
Write32(stream, (*data)->sp_data.length());
stream.write((*data)->sp_data.data(), (*data)->sp_data.length());
if (stream.bad()) {

View File

@@ -17,6 +17,7 @@
#include <android-base/file.h>
#include <gtest/gtest.h>
#include <idmap2/FabricatedOverlay.h>
#include "TestHelpers.h"
#include <fstream>
#include <utility>
@@ -41,6 +42,10 @@ TEST(FabricatedOverlayTests, OverlayInfo) {
}
TEST(FabricatedOverlayTests, SetResourceValue) {
auto path = GetTestDataPath() + "/overlay/res/drawable/android.png";
auto fd = android::base::unique_fd(::open(path.c_str(), O_RDONLY | O_CLOEXEC));
ASSERT_TRUE(fd > 0) << "errno " << errno << " for path " << path;
auto overlay =
FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "com.example.target")
.SetResourceValue(
@@ -54,6 +59,8 @@ TEST(FabricatedOverlayTests, SetResourceValue) {
Res_value::TYPE_STRING,
"foobar",
"en-rUS-normal-xxhdpi-v21")
.SetResourceValue("com.example.target:drawable/dr1", fd, "port-xxhdpi-v7")
.setFrroPath("/foo/bar/biz.frro")
.Build();
ASSERT_TRUE(overlay);
auto container = FabricatedOverlayContainer::FromOverlay(std::move(*overlay));
@@ -67,19 +74,28 @@ TEST(FabricatedOverlayTests, SetResourceValue) {
auto pairs = container->GetOverlayData(*info);
ASSERT_TRUE(pairs);
ASSERT_EQ(4U, pairs->pairs.size());
ASSERT_EQ(5U, pairs->pairs.size());
auto string_pool = ResStringPool(pairs->string_pool_data->data.get(),
pairs->string_pool_data->data_length, false);
auto& it = pairs->pairs[0];
ASSERT_EQ("com.example.target:integer/int1", it.resource_name);
ASSERT_EQ("com.example.target:drawable/dr1", it.resource_name);
auto entry = std::get_if<TargetValueWithConfig>(&it.value);
ASSERT_NE(nullptr, entry);
ASSERT_EQ(std::string("frro://foo/bar/biz.frro?offset=16&size=8341"),
string_pool.string8At(entry->value.data_value).value_or(""));
ASSERT_EQ(Res_value::TYPE_STRING, entry->value.data_type);
ASSERT_EQ("port-xxhdpi-v7", entry->config);
it = pairs->pairs[1];
ASSERT_EQ("com.example.target:integer/int1", it.resource_name);
entry = std::get_if<TargetValueWithConfig>(&it.value);
ASSERT_NE(nullptr, entry);
ASSERT_EQ(1U, entry->value.data_value);
ASSERT_EQ(Res_value::TYPE_INT_DEC, entry->value.data_type);
ASSERT_EQ("port", entry->config);
it = pairs->pairs[1];
it = pairs->pairs[2];
ASSERT_EQ("com.example.target:string/int3", it.resource_name);
entry = std::get_if<TargetValueWithConfig>(&it.value);
ASSERT_NE(nullptr, entry);
@@ -87,7 +103,7 @@ TEST(FabricatedOverlayTests, SetResourceValue) {
ASSERT_EQ(Res_value::TYPE_REFERENCE, entry->value.data_type);
ASSERT_EQ("xxhdpi-v7", entry->config);
it = pairs->pairs[2];
it = pairs->pairs[3];
ASSERT_EQ("com.example.target:string/string1", it.resource_name);
entry = std::get_if<TargetValueWithConfig>(&it.value);
ASSERT_NE(nullptr, entry);
@@ -95,7 +111,7 @@ TEST(FabricatedOverlayTests, SetResourceValue) {
ASSERT_EQ(std::string("foobar"), string_pool.string8At(entry->value.data_value).value_or(""));
ASSERT_EQ("en-rUS-normal-xxhdpi-v21", entry->config);
it = pairs->pairs[3];
it = pairs->pairs[4];
ASSERT_EQ("com.example.target.split:integer/int2", it.resource_name);
entry = std::get_if<TargetValueWithConfig>(&it.value);
ASSERT_NE(nullptr, entry);

View File

@@ -260,11 +260,17 @@ TEST(IdmapTests, FabricatedOverlay) {
auto target = TargetResourceContainer::FromPath(target_apk_path);
ASSERT_TRUE(target);
auto path = GetTestDataPath() + "/overlay/res/drawable/android.png";
auto fd = android::base::unique_fd(::open(path.c_str(), O_RDONLY | O_CLOEXEC));
ASSERT_TRUE(fd > 0) << "errno " << errno << " for path " << path;
auto frro = FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "test.target")
.SetOverlayable("TestResources")
.SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U, "land-xxhdpi-v7")
.SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000, "land")
.SetResourceValue("string/str2", Res_value::TYPE_STRING, "foobar", "xxhdpi-v7")
.SetResourceValue("drawable/dr1", fd, "port-xxhdpi-v7")
.setFrroPath("/foo/bar/biz.frro")
.Build();
ASSERT_TRUE(frro);
@@ -293,14 +299,19 @@ TEST(IdmapTests, FabricatedOverlay) {
auto string_pool_data = data->GetStringPoolData();
auto string_pool = ResStringPool(string_pool_data.data(), string_pool_data.size(), false);
std::u16string expected_uri = u"frro://foo/bar/biz.frro?offset=16&size=8341";
uint32_t uri_index
= string_pool.indexOfString(expected_uri.data(), expected_uri.length()).value_or(-1);
const auto& target_inline_entries = data->GetTargetInlineEntries();
ASSERT_EQ(target_inline_entries.size(), 3U);
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::integer::int1, "land-xxhdpi-v7",
ASSERT_EQ(target_inline_entries.size(), 4U);
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::drawable::dr1, "port-xxhdpi-v7",
Res_value::TYPE_STRING, uri_index);
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[1], R::target::integer::int1, "land-xxhdpi-v7",
Res_value::TYPE_INT_DEC, 2U);
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[1], R::target::string::str1, "land",
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[2], R::target::string::str1, "land",
Res_value::TYPE_REFERENCE, 0x7f010000);
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[2], R::target::string::str2, "xxhdpi-v7",
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[3], R::target::string::str2, "xxhdpi-v7",
Res_value::TYPE_STRING,
(uint32_t) (string_pool.indexOfString(u"foobar", 6)).value_or(-1));
}

View File

@@ -26,24 +26,27 @@ namespace android::idmap2 {
// clang-format off
namespace R::target {
namespace integer { // NOLINT(runtime/indentation_namespace)
constexpr ResourceId int1 = 0x7f010000;
constexpr ResourceId int1 = 0x7f020000;
}
namespace drawable {
constexpr ResourceId dr1 = 0x7f010000;
}
namespace string { // NOLINT(runtime/indentation_namespace)
constexpr ResourceId not_overlayable = 0x7f020003;
constexpr ResourceId other = 0x7f020004;
constexpr ResourceId policy_actor = 0x7f020005;
constexpr ResourceId policy_config_signature = 0x7f020006;
constexpr ResourceId policy_odm = 0x7f020007;
constexpr ResourceId policy_oem = 0x7f020008;
constexpr ResourceId policy_product = 0x7f020009;
constexpr ResourceId policy_public = 0x7f02000a;
constexpr ResourceId policy_signature = 0x7f02000b;
constexpr ResourceId policy_system = 0x7f02000c;
constexpr ResourceId policy_system_vendor = 0x7f02000d;
constexpr ResourceId str1 = 0x7f02000e;
constexpr ResourceId str2 = 0x7f02000f;
constexpr ResourceId str3 = 0x7f020010;
constexpr ResourceId str4 = 0x7f020011;
constexpr ResourceId not_overlayable = 0x7f030003;
constexpr ResourceId other = 0x7f030004;
constexpr ResourceId policy_actor = 0x7f030005;
constexpr ResourceId policy_config_signature = 0x7f030006;
constexpr ResourceId policy_odm = 0x7f030007;
constexpr ResourceId policy_oem = 0x7f030008;
constexpr ResourceId policy_product = 0x7f030009;
constexpr ResourceId policy_public = 0x7f03000a;
constexpr ResourceId policy_signature = 0x7f03000b;
constexpr ResourceId policy_system = 0x7f03000c;
constexpr ResourceId policy_system_vendor = 0x7f03000d;
constexpr ResourceId str1 = 0x7f03000e;
constexpr ResourceId str2 = 0x7f03000f;
constexpr ResourceId str3 = 0x7f030010;
constexpr ResourceId str4 = 0x7f030011;
} // namespace string
} // namespace R::target

View File

@@ -79,22 +79,22 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
ASSERT_CONTAINS_REGEX(ADDRESS "00000000 config count", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 overlay entry count", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "0000000a string pool index offset", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 target id: integer/int1", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 target id: integer/int1", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 overlay id: integer/int1", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f02000e target id: string/str1", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f03000e target id: string/str1", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f02000b overlay id: string/str1", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f020010 target id: string/str3", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f030010 target id: string/str3", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f02000c overlay id: string/str3", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f020011 target id: string/str4", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f030011 target id: string/str4", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f02000d overlay id: string/str4", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 overlay id: integer/int1", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 target id: integer/int1", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 target id: integer/int1", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f02000b overlay id: string/str1", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f02000e target id: string/str1", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f03000e target id: string/str1", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f02000c overlay id: string/str3", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f020010 target id: string/str3", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f030010 target id: string/str3", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f02000d overlay id: string/str4", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f020011 target id: string/str4", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f030011 target id: string/str4", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "000000b4 string pool size", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "........ string pool", stream.str());
}

View File

@@ -23,6 +23,7 @@
#include <memory>
#include <string>
#include <fcntl.h>
#include "R.h"
#include "TestConstants.h"
#include "TestHelpers.h"
@@ -76,7 +77,12 @@ Result<Unit> MappingExists(const ResourceMapping& mapping, ResourceId target_res
auto target_map = mapping.GetTargetToOverlayMap();
auto entry_map = target_map.find(target_resource);
if (entry_map == target_map.end()) {
return Error("Failed to find mapping for target resource");
std::string keys;
for (const auto &pair : target_map) {
keys.append(fmt::format("0x{:x}", pair.first)).append(" ");
}
return Error(R"(Failed to find mapping for target resource "0x%02x": "%s")",
target_resource, keys.c_str());
}
auto actual_overlay_resource = std::get_if<ResourceId>(&entry_map->second);
@@ -108,7 +114,12 @@ Result<Unit> MappingExists(const ResourceMapping& mapping, const ResourceId& tar
auto target_map = mapping.GetTargetToOverlayMap();
auto entry_map = target_map.find(target_resource);
if (entry_map == target_map.end()) {
return Error("Failed to find mapping for target resource");
std::string keys;
for (const auto &pair : target_map) {
keys.append(fmt::format("{:x}", pair.first)).append(" ");
}
return Error(R"(Failed to find mapping for target resource "0x%02x": "%s")",
target_resource, keys.c_str());
}
auto config_map = std::get_if<ConfigMap>(&entry_map->second);
@@ -193,11 +204,16 @@ TEST(ResourceMappingTests, InlineResources) {
}
TEST(ResourceMappingTests, FabricatedOverlay) {
auto path = GetTestDataPath() + "/overlay/res/drawable/android.png";
auto fd = android::base::unique_fd(::open(path.c_str(), O_RDONLY | O_CLOEXEC));
ASSERT_TRUE(fd > 0) << "errno " << errno << " for path " << path;
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, "")
.SetResourceValue("string/str2", Res_value::TYPE_STRING, "foobar", "")
.SetResourceValue("drawable/dr1", fd, "")
.setFrroPath("/foo/bar/biz.frro")
.Build();
ASSERT_TRUE(frro);
@@ -214,11 +230,16 @@ TEST(ResourceMappingTests, FabricatedOverlay) {
auto string_pool_data = res.GetStringPoolData();
auto string_pool = ResStringPool(string_pool_data.data(), string_pool_data.size(), false);
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
std::u16string expected_uri = u"frro://foo/bar/biz.frro?offset=16&size=8341";
uint32_t uri_index
= string_pool.indexOfString(expected_uri.data(), expected_uri.length()).value_or(-1);
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
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::string::str2, Res_value::TYPE_STRING,
(uint32_t) (string_pool.indexOfString(u"foobar", 6)).value_or(-1)));
ASSERT_RESULT(MappingExists(res, R::target::drawable::dr1, Res_value::TYPE_STRING, uri_index));
ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 2U));
}

View File

@@ -19,8 +19,8 @@
namespace android::idmap2::TestConstants {
constexpr const auto TARGET_CRC = 0x7c2d4719;
constexpr const auto TARGET_CRC_STRING = "7c2d4719";
constexpr const auto TARGET_CRC = 0xa960a69;
constexpr const auto TARGET_CRC_STRING = "0a960a69";
constexpr const auto OVERLAY_CRC = 0xb71095cf;
constexpr const auto OVERLAY_CRC_STRING = "b71095cf";

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@@ -17,5 +17,7 @@ aapt2 link --manifest AndroidManifest.xml -A assets -o target.apk compiled.flata
rm compiled.flata
aapt2 compile res/values/values.xml -o .
aapt2 link --manifest AndroidManifest.xml -A assets -o target-no-overlayable.apk values_values.arsc.flat
rm values_values.arsc.flat
aapt2 compile res/drawable/dr1.png -o .
aapt2 link --manifest AndroidManifest.xml -A assets -o target-no-overlayable.apk values_values.arsc.flat drawable_dr1.png.flat
rm values_values.arsc.flat
rm drawable_dr1.png.flat

Binary file not shown.

After

Width:  |  Height:  |  Size: 546 B

View File

@@ -63,6 +63,7 @@
<item type="string" name="y" />
<item type="string" name="z" />
<item type="integer" name="int1" />
<item type="drawable" name="dr1" />
</policy>
</overlayable>

View File

@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.FabricatedOverlayInternal;
import android.os.FabricatedOverlayInternalEntry;
import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
import com.android.internal.util.Preconditions;
@@ -169,6 +170,24 @@ public class FabricatedOverlay {
return this;
}
/**
* Sets the value of the fabricated overlay
*
* @param resourceName name of the target resource to overlay (in the form
* [package]:type/entry)
* @param value the file descriptor whose contents are the value of the frro
* @param configuration The string representation of the config this overlay is enabled for
*/
public Builder setResourceValue(@NonNull String resourceName, ParcelFileDescriptor value,
String configuration) {
final FabricatedOverlayInternalEntry entry = new FabricatedOverlayInternalEntry();
entry.resourceName = resourceName;
entry.binaryData = value;
entry.configuration = configuration;
mEntries.add(entry);
return this;
}
/** Builds an immutable fabricated overlay. */
public FabricatedOverlay build() {
final FabricatedOverlayInternal overlay = new FabricatedOverlayInternal();

View File

@@ -40,8 +40,10 @@ import android.graphics.drawable.ColorStateListDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.DrawableContainer;
import android.icu.text.PluralRules;
import android.net.Uri;
import android.os.Build;
import android.os.LocaleList;
import android.os.ParcelFileDescriptor;
import android.os.Trace;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
@@ -59,6 +61,8 @@ import libcore.util.NativeAllocationRegistry;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
@@ -799,7 +803,21 @@ public class ResourcesImpl {
private Drawable decodeImageDrawable(@NonNull AssetInputStream ais,
@NonNull Resources wrapper, @NonNull TypedValue value) {
ImageDecoder.Source src = new ImageDecoder.AssetInputStreamSource(ais,
wrapper, value);
wrapper, value);
try {
return ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
});
} catch (IOException ioe) {
// This is okay. This may be something that ImageDecoder does not
// support, like SVG.
return null;
}
}
@Nullable
private Drawable decodeImageDrawable(@NonNull FileInputStream fis, @NonNull Resources wrapper) {
ImageDecoder.Source src = ImageDecoder.createSource(wrapper, fis);
try {
return ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
@@ -860,6 +878,17 @@ public class ResourcesImpl {
} else {
dr = loadXmlDrawable(wrapper, value, id, density, file);
}
} else if (file.startsWith("frro://")) {
Uri uri = Uri.parse(file);
File f = new File('/' + uri.getHost() + uri.getPath());
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(f,
ParcelFileDescriptor.MODE_READ_ONLY);
AssetFileDescriptor afd = new AssetFileDescriptor(
pfd,
Long.parseLong(uri.getQueryParameter("offset")),
Long.parseLong(uri.getQueryParameter("size")));
FileInputStream is = afd.createInputStream();
dr = decodeImageDrawable(is, wrapper);
} else {
final InputStream is = mAssets.openNonAsset(
value.assetCookie, file, AssetManager.ACCESS_STREAMING);

View File

@@ -53,7 +53,7 @@ constexpr const uint32_t kFabricatedOverlayMagic = 0x4f525246; // FRRO (big endi
// The version should only be changed when a backwards-incompatible change must be made to the
// fabricated overlay file format. Old fabricated overlays must be migrated to the new file format
// to prevent losing fabricated overlay data.
constexpr const uint32_t kFabricatedOverlayCurrentVersion = 2;
constexpr const uint32_t kFabricatedOverlayCurrentVersion = 3;
// Returns whether or not the path represents a fabricated overlay.
bool IsFabricatedOverlay(const std::string& path);

View File

@@ -29,6 +29,7 @@ import android.content.res.AssetManager;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.ShellCommand;
@@ -64,7 +65,8 @@ final class OverlayManagerShellCommand extends ShellCommand {
private final IOverlayManager mInterface;
private static final Map<String, Integer> TYPE_MAP = Map.of(
"color", TypedValue.TYPE_FIRST_COLOR_INT,
"string", TypedValue.TYPE_STRING);
"string", TypedValue.TYPE_STRING,
"drawable", -1);
OverlayManagerShellCommand(@NonNull final Context ctx, @NonNull final IOverlayManager iom) {
mContext = ctx;
@@ -258,7 +260,7 @@ final class OverlayManagerShellCommand extends ShellCommand {
String name = "";
String filename = null;
String opt;
String configuration = null;
String config = null;
while ((opt = getNextOption()) != null) {
switch (opt) {
case "--user":
@@ -277,7 +279,7 @@ final class OverlayManagerShellCommand extends ShellCommand {
filename = getNextArgRequired();
break;
case "--config":
configuration = getNextArgRequired();
config = getNextArgRequired();
break;
default:
err.println("Error: Unknown option: " + opt);
@@ -312,7 +314,9 @@ final class OverlayManagerShellCommand extends ShellCommand {
final String resourceName = getNextArgRequired();
final String typeStr = getNextArgRequired();
final String strData = String.join(" ", peekRemainingArgs());
addOverlayValue(overlayBuilder, resourceName, typeStr, strData, configuration);
if (addOverlayValue(overlayBuilder, resourceName, typeStr, strData, config) != 0) {
return 1;
}
}
mInterface.commit(new OverlayManagerTransaction.Builder()
@@ -369,8 +373,10 @@ final class OverlayManagerShellCommand extends ShellCommand {
return 1;
}
String config = parser.getAttributeValue(null, "config");
addOverlayValue(overlayBuilder, targetPackage + ':' + target,
overlayType, value, config);
if (addOverlayValue(overlayBuilder, targetPackage + ':' + target,
overlayType, value, config) != 0) {
return 1;
}
}
}
}
@@ -384,7 +390,7 @@ final class OverlayManagerShellCommand extends ShellCommand {
return 0;
}
private void addOverlayValue(FabricatedOverlay.Builder overlayBuilder,
private int addOverlayValue(FabricatedOverlay.Builder overlayBuilder,
String resourceName, String typeString, String valueString, String configuration) {
final int type;
typeString = typeString.toLowerCase(Locale.getDefault());
@@ -399,6 +405,9 @@ final class OverlayManagerShellCommand extends ShellCommand {
}
if (type == TypedValue.TYPE_STRING) {
overlayBuilder.setResourceValue(resourceName, type, valueString, configuration);
} else if (type < 0) {
ParcelFileDescriptor pfd = openFileForSystem(valueString, "r");
overlayBuilder.setResourceValue(resourceName, pfd, configuration);
} else {
final int intData;
if (valueString.startsWith("0x")) {
@@ -408,6 +417,7 @@ final class OverlayManagerShellCommand extends ShellCommand {
}
overlayBuilder.setResourceValue(resourceName, type, intData, configuration);
}
return 0;
}
private int runEnableExclusive() throws RemoteException {