diff options
author | 2022-10-21 17:42:14 +0000 | |
---|---|---|
committer | 2022-11-14 18:38:12 +0000 | |
commit | a761f3325ecada28ba5c7a6697d9183fc4b30f92 (patch) | |
tree | 9c144a6053b2320adad7f2b2aaec282d828398a7 | |
parent | 090ed0c155c610006d701ccb4297ea0d35dd6817 (diff) |
implement drawable frros
Test: unit tests
Bug: 251283316
Change-Id: Iead53711c54596c3787eeba6dcf9ced129f94426
22 files changed, 248 insertions, 65 deletions
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp index 4f8faca59e4c..7a08cbdcddd4 100644 --- a/cmds/idmap2/Android.bp +++ b/cmds/idmap2/Android.bp @@ -222,6 +222,7 @@ cc_test { }, data: [ "tests/data/**/*.apk", + "tests/data/**/*.png", ], compile_multilib: "first", test_options: { diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp index 44311648da80..10947dc90a76 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.cpp +++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp @@ -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)) { diff --git a/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl b/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl index c773e112997d..3ad6d58e8253 100644 --- a/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl +++ b/cmds/idmap2/idmap2d/aidl/core/android/os/FabricatedOverlayInternalEntry.aidl @@ -24,5 +24,6 @@ parcelable FabricatedOverlayInternalEntry { int dataType; int data; @nullable @utf8InCpp String stringData; + @nullable ParcelFileDescriptor binaryData; @nullable @utf8InCpp String configuration; }
\ No newline at end of file diff --git a/cmds/idmap2/include/idmap2/FabricatedOverlay.h b/cmds/idmap2/include/idmap2/FabricatedOverlay.h index 05b0618131c9..9f57710edb0b 100644 --- a/cmds/idmap2/include/idmap2/FabricatedOverlay.h +++ b/cmds/idmap2/include/idmap2/FabricatedOverlay.h @@ -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_; diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h index af4dd8960cc3..2214a83bd2da 100644 --- a/cmds/idmap2/include/idmap2/ResourceUtils.h +++ b/cmds/idmap2/include/idmap2/ResourceUtils.h @@ -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 { diff --git a/cmds/idmap2/libidmap2/FabricatedOverlay.cpp b/cmds/idmap2/libidmap2/FabricatedOverlay.cpp index bde9b0be4361..d517e29f3369 100644 --- a/cmds/idmap2/libidmap2/FabricatedOverlay.cpp +++ b/cmds/idmap2/libidmap2/FabricatedOverlay.cpp @@ -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()) { diff --git a/cmds/idmap2/tests/FabricatedOverlayTests.cpp b/cmds/idmap2/tests/FabricatedOverlayTests.cpp index e804c879ee82..e13a0eb5d488 100644 --- a/cmds/idmap2/tests/FabricatedOverlayTests.cpp +++ b/cmds/idmap2/tests/FabricatedOverlayTests.cpp @@ -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); diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp index 7b7dc17deea4..b473f26b2230 100644 --- a/cmds/idmap2/tests/IdmapTests.cpp +++ b/cmds/idmap2/tests/IdmapTests.cpp @@ -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)); } diff --git a/cmds/idmap2/tests/R.h b/cmds/idmap2/tests/R.h index ad998b9a7a4c..80c062d92640 100644 --- a/cmds/idmap2/tests/R.h +++ b/cmds/idmap2/tests/R.h @@ -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 diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp index 7112eeb9ea0c..68164e26f352 100644 --- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp +++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp @@ -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()); } diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp index 016d427e7452..380e462a3aba 100644 --- a/cmds/idmap2/tests/ResourceMappingTests.cpp +++ b/cmds/idmap2/tests/ResourceMappingTests.cpp @@ -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)); } diff --git a/cmds/idmap2/tests/TestConstants.h b/cmds/idmap2/tests/TestConstants.h index d5799adf0ec3..794d6221c2c0 100644 --- a/cmds/idmap2/tests/TestConstants.h +++ b/cmds/idmap2/tests/TestConstants.h @@ -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"; diff --git a/cmds/idmap2/tests/data/overlay/res/drawable/android.png b/cmds/idmap2/tests/data/overlay/res/drawable/android.png Binary files differnew file mode 100644 index 000000000000..b7317b0933fb --- /dev/null +++ b/cmds/idmap2/tests/data/overlay/res/drawable/android.png diff --git a/cmds/idmap2/tests/data/target/build b/cmds/idmap2/tests/data/target/build index e6df742cc9da..cd13a7ec1bd8 100755 --- a/cmds/idmap2/tests/data/target/build +++ b/cmds/idmap2/tests/data/target/build @@ -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
\ No newline at end of file +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 diff --git a/cmds/idmap2/tests/data/target/res/drawable/dr1.png b/cmds/idmap2/tests/data/target/res/drawable/dr1.png Binary files differnew file mode 100644 index 000000000000..1a56e68fd6d2 --- /dev/null +++ b/cmds/idmap2/tests/data/target/res/drawable/dr1.png diff --git a/cmds/idmap2/tests/data/target/res/values/overlayable.xml b/cmds/idmap2/tests/data/target/res/values/overlayable.xml index 57e6c439c23c..aac9081b9513 100644 --- a/cmds/idmap2/tests/data/target/res/values/overlayable.xml +++ b/cmds/idmap2/tests/data/target/res/values/overlayable.xml @@ -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> diff --git a/cmds/idmap2/tests/data/target/target-no-overlayable.apk b/cmds/idmap2/tests/data/target/target-no-overlayable.apk Binary files differindex cc3491de894d..680eeb609f8e 100644 --- a/cmds/idmap2/tests/data/target/target-no-overlayable.apk +++ b/cmds/idmap2/tests/data/target/target-no-overlayable.apk diff --git a/cmds/idmap2/tests/data/target/target.apk b/cmds/idmap2/tests/data/target/target.apk Binary files differindex 4a58c5e28f49..145e737ca138 100644 --- a/cmds/idmap2/tests/data/target/target.apk +++ b/cmds/idmap2/tests/data/target/target.apk diff --git a/core/java/android/content/om/FabricatedOverlay.java b/core/java/android/content/om/FabricatedOverlay.java index 3ca056097c1f..dbefa65f5c68 100644 --- a/core/java/android/content/om/FabricatedOverlay.java +++ b/core/java/android/content/om/FabricatedOverlay.java @@ -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(); diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index ff072916292b..09d24d47cc7a 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -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); diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 24628cd36ba5..c5e381da9354 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -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); diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java index bb918d594167..7ec167bf5633 100644 --- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java +++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java @@ -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 { |