implement drawable frros
Test: unit tests Bug: 251283316 Change-Id: Iead53711c54596c3787eeba6dcf9ced129f94426
This commit is contained in:
@@ -222,6 +222,7 @@ cc_test {
|
||||
},
|
||||
data: [
|
||||
"tests/data/**/*.apk",
|
||||
"tests/data/**/*.png",
|
||||
],
|
||||
compile_multilib: "first",
|
||||
test_options: {
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -24,5 +24,6 @@ parcelable FabricatedOverlayInternalEntry {
|
||||
int dataType;
|
||||
int data;
|
||||
@nullable @utf8InCpp String stringData;
|
||||
@nullable ParcelFileDescriptor binaryData;
|
||||
@nullable @utf8InCpp String configuration;
|
||||
}
|
||||
@@ -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_;
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
BIN
cmds/idmap2/tests/data/overlay/res/drawable/android.png
Normal file
BIN
cmds/idmap2/tests/data/overlay/res/drawable/android.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.1 KiB |
@@ -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
|
||||
|
||||
BIN
cmds/idmap2/tests/data/target/res/drawable/dr1.png
Normal file
BIN
cmds/idmap2/tests/data/target/res/drawable/dr1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 546 B |
@@ -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>
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -54,7 +54,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);
|
||||
|
||||
@@ -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;
|
||||
@@ -63,7 +64,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;
|
||||
@@ -257,7 +259,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":
|
||||
@@ -276,7 +278,7 @@ final class OverlayManagerShellCommand extends ShellCommand {
|
||||
filename = getNextArgRequired();
|
||||
break;
|
||||
case "--config":
|
||||
configuration = getNextArgRequired();
|
||||
config = getNextArgRequired();
|
||||
break;
|
||||
default:
|
||||
err.println("Error: Unknown option: " + opt);
|
||||
@@ -311,7 +313,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()
|
||||
@@ -368,8 +372,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -383,7 +389,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());
|
||||
@@ -398,6 +404,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")) {
|
||||
@@ -407,6 +416,7 @@ final class OverlayManagerShellCommand extends ShellCommand {
|
||||
}
|
||||
overlayBuilder.setResourceValue(resourceName, type, intData, configuration);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int runEnableExclusive() throws RemoteException {
|
||||
|
||||
Reference in New Issue
Block a user